Adds a new package: @microsoft/jest-sarif (#9)

* feat: Initial import of Jest matchers for SARIF.

Co-authored-by: Jeff King <jeffking@gmail.com>
This commit is contained in:
Steve Calvert 2021-04-14 17:13:08 -07:00 коммит произвёл GitHub
Родитель 960be206ed
Коммит fafeec4dc1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 1052 добавлений и 63 удалений

Просмотреть файл

@ -42,6 +42,24 @@
"unicorn/no-fn-reference-in-iterator": "off",
"unicorn/no-process-exit": "off",
"node/no-unsupported-features/es-syntax": ["error", { "ignores": ["modules"] }],
"node/no-extraneous-import": ["error"]
}
"node/no-extraneous-import": ["error"],
"node/no-missing-import": [
"error",
{
"allowModules": ["sarif"]
}
],
"prefer-const": "error"
},
"overrides": [
{
"files": ["packages/**/__tests__/**/*.ts", "packages/jest-sarif/**/*.ts"],
"env": {
"jest": true
},
"rules": {
"node/no-extraneous-import": "off"
}
}
]
}

42
.github/workflows/ci.yml поставляемый
Просмотреть файл

@ -2,17 +2,17 @@ name: CI
on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]
jobs:
lint:
# run linting with our development node/npm versions (per package.json
# volta config)
name: "Lint - ${{ matrix.os }}"
runs-on: "${{ matrix.os }}-latest"
name: 'Lint - ${{ matrix.os }}'
runs-on: '${{ matrix.os }}-latest'
strategy:
matrix:
@ -21,35 +21,40 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: volta-cli/action@v1
- run: npm ci
- run: npm run lint
- name: install dependencies
run: npm install
- name: lint
run: npm run lint
test:
# run linting with our development node/npm versions (per package.json
# volta config)
name: "Tests - Node (${{ matrix.node-version }}) - ${{ matrix.os }}"
runs-on: "${{ matrix.os }}-latest"
name: 'Tests - Node (${{ matrix.node-version }}) - ${{ matrix.os }}'
runs-on: '${{ matrix.os }}-latest'
strategy:
matrix:
os: [windows, ubuntu]
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [12.x, 14.x, 15.x]
steps:
- uses: actions/checkout@v2
- uses: volta-cli/action@v1
- run: npm ci
- run: npm run build
- run: npm test
- name: install dependencies
run: npm install
- name: build
run: npm run build
- name: test
run: npm test
floating-dependencies:
# test using latest allowed dependencies within our semver ranges this is
# an early warning detection for failures introduced by transitive
# dependencies
name: "Floating Dependencies - ${{ matrix.os }}"
runs-on: "${{ matrix.os }}-latest"
name: 'Floating Dependencies - ${{ matrix.os }}'
runs-on: '${{ matrix.os }}-latest'
strategy:
matrix:
@ -58,6 +63,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: volta-cli/action@v1
- run: npm install --no-package-lock
- run: npm run build
- run: npm test
- name: install dependencies
run: npm install --no-package-lock
- name: build
run: npm run build
- name: test
run: npm test

330
package-lock.json сгенерированный
Просмотреть файл

@ -11,6 +11,8 @@
"packages/*"
],
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/node": "^14.14.37",
"@types/sarif": "^2.1.3",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
@ -23,6 +25,7 @@
"eslint-plugin-unicorn": "^29.0.0",
"jest": "^26.6.3",
"prettier": "^2.2.1",
"ts-jest": "^26.5.4",
"typescript": "^4.2.3"
}
},
@ -859,6 +862,10 @@
"node": ">= 10.14.2"
}
},
"node_modules/@microsoft/jest-sarif": {
"resolved": "packages/jest-sarif",
"link": true
},
"node_modules/@microsoft/sarif-builder": {
"resolved": "packages/sarif-builder",
"link": true
@ -990,6 +997,16 @@
"@types/istanbul-lib-report": "*"
}
},
"node_modules/@types/jest": {
"version": "26.0.22",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz",
"integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==",
"dev": true,
"dependencies": {
"jest-diff": "^26.0.0",
"pretty-format": "^26.0.0"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -997,9 +1014,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "14.14.35",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz",
"integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==",
"version": "14.14.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz",
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==",
"dev": true
},
"node_modules/@types/normalize-package-data": {
@ -1192,7 +1209,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -1544,6 +1560,25 @@
"node": ">=0.10.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -1600,6 +1635,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bs-logger": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
"dev": true,
"dependencies": {
"fast-json-stable-stringify": "2.x"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@ -1609,6 +1656,29 @@
"node-int64": "^0.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@ -1681,7 +1751,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -1694,7 +1763,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -1706,7 +1774,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@ -1717,14 +1784,12 @@
"node_modules/chalk/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/chalk/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -1733,7 +1798,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@ -3011,8 +3075,7 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-diff": {
"version": "1.2.0",
@ -3040,8 +3103,7 @@
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
@ -3499,6 +3561,25 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
@ -4641,8 +4722,7 @@
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@ -4801,6 +4881,12 @@
"semver": "bin/semver.js"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/makeerror": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
@ -4932,6 +5018,18 @@
"node": ">=0.10.0"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -4978,6 +5076,14 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@ -5492,7 +5598,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -6641,6 +6746,18 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true
},
"node_modules/sync-fetch": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.0.tgz",
"integrity": "sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g==",
"dependencies": {
"buffer": "^5.7.0",
"node-fetch": "^2.6.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/table": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
@ -6814,6 +6931,34 @@
"node": ">=8"
}
},
"node_modules/ts-jest": {
"version": "26.5.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz",
"integrity": "sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg==",
"dev": true,
"dependencies": {
"bs-logger": "0.x",
"buffer-from": "1.x",
"fast-json-stable-stringify": "2.x",
"jest-util": "^26.1.0",
"json5": "2.x",
"lodash": "4.x",
"make-error": "1.x",
"mkdirp": "1.x",
"semver": "7.x",
"yargs-parser": "20.x"
},
"bin": {
"ts-jest": "cli.js"
},
"engines": {
"node": ">= 10"
},
"peerDependencies": {
"jest": ">=26 <27",
"typescript": ">=3.8 <5.0"
}
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -6978,7 +7123,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@ -7232,6 +7376,15 @@
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "20.2.7",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/yargs/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
@ -7245,6 +7398,19 @@
"node": ">=6"
}
},
"packages/jest-sarif": {
"name": "@microsoft/jest-sarif",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.6",
"chalk": "^4.1.0",
"sync-fetch": "^0.3.0"
},
"engines": {
"node": ">= 12.11.*"
}
},
"packages/sarif-builder": {
"name": "@microsoft/sarif-builder",
"version": "0.0.1",
@ -8005,6 +8171,14 @@
"chalk": "^4.0.0"
}
},
"@microsoft/jest-sarif": {
"version": "file:packages/jest-sarif",
"requires": {
"ajv": "^6.12.6",
"chalk": "^4.1.0",
"sync-fetch": "^0.3.0"
}
},
"@microsoft/sarif-builder": {
"version": "file:packages/sarif-builder"
},
@ -8126,6 +8300,16 @@
"@types/istanbul-lib-report": "*"
}
},
"@types/jest": {
"version": "26.0.22",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz",
"integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==",
"dev": true,
"requires": {
"jest-diff": "^26.0.0",
"pretty-format": "^26.0.0"
}
},
"@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -8133,9 +8317,9 @@
"dev": true
},
"@types/node": {
"version": "14.14.35",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz",
"integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==",
"version": "14.14.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz",
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw==",
"dev": true
},
"@types/normalize-package-data": {
@ -8298,7 +8482,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -8576,6 +8759,11 @@
}
}
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -8623,6 +8811,15 @@
"node-releases": "^1.1.70"
}
},
"bs-logger": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
"dev": true,
"requires": {
"fast-json-stable-stringify": "2.x"
}
},
"bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@ -8632,6 +8829,15 @@
"node-int64": "^0.4.0"
}
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
@ -8692,7 +8898,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -8702,7 +8907,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@ -8711,7 +8915,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
@ -8719,20 +8922,17 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@ -9771,8 +9971,7 @@
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-diff": {
"version": "1.2.0",
@ -9797,8 +9996,7 @@
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fast-levenshtein": {
"version": "2.0.6",
@ -10164,6 +10362,11 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"ignore": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
@ -11091,8 +11294,7 @@
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@ -11223,6 +11425,12 @@
}
}
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"makeerror": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
@ -11326,6 +11534,12 @@
}
}
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -11369,6 +11583,11 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@ -11773,8 +11992,7 @@
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"queue-microtask": {
"version": "1.2.3",
@ -12721,6 +12939,15 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true
},
"sync-fetch": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.0.tgz",
"integrity": "sha512-dJp4qg+x4JwSEW1HibAuMi0IIrBI3wuQr2GimmqB7OXR50wmwzfdusG+p39R9w3R6aFtZ2mzvxvWKQ3Bd/vx3g==",
"requires": {
"buffer": "^5.7.0",
"node-fetch": "^2.6.1"
}
},
"table": {
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
@ -12870,6 +13097,24 @@
"punycode": "^2.1.1"
}
},
"ts-jest": {
"version": "26.5.4",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.4.tgz",
"integrity": "sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg==",
"dev": true,
"requires": {
"bs-logger": "0.x",
"buffer-from": "1.x",
"fast-json-stable-stringify": "2.x",
"jest-util": "^26.1.0",
"json5": "2.x",
"lodash": "4.x",
"make-error": "1.x",
"mkdirp": "1.x",
"semver": "7.x",
"yargs-parser": "20.x"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -12998,7 +13243,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
@ -13226,6 +13470,12 @@
}
}
}
},
"yargs-parser": {
"version": "20.2.7",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==",
"dev": true
}
}
}

Просмотреть файл

@ -19,6 +19,8 @@
"test": "npm run test --workspaces"
},
"devDependencies": {
"@types/jest": "^26.0.20",
"@types/node": "^14.14.37",
"@types/sarif": "^2.1.3",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
@ -31,6 +33,7 @@
"eslint-plugin-unicorn": "^29.0.0",
"jest": "^26.6.3",
"prettier": "^2.2.1",
"ts-jest": "^26.5.4",
"typescript": "^4.2.3"
},
"volta": {

Просмотреть файл

@ -0,0 +1,138 @@
# @microsoft/jest-sarif
> Custom matchers for SARIF logs for Jest
## Overview
The [Static Analysis Result Interchange Format (SARIF)](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html) is comprehensive spec that provides a standardized schema for tools running static analysis. For tools producing SARIF output, it's useful to be able to test that output to validate it conforms to the SARIF JSON schema.
This library helps achieve that through custom matchers for the Jest testing library. It uses the SARIF JSON Schema to validate the log structure against the actual schema, which helps ensure flexibility when matching whole or partial portions of that schema.
## Installation
```bash
npm install @microsoft/jest-sarif --save-dev
# or
yarn add @microsoft/jest-sarif -D
```
## Usage
You can import and use the matchers in one of two ways:
1. (Recommended) Including in a [jest setup file](https://jestjs.io/docs/configuration#setupfilesafterenv-array) as a one-time setup
```ts
// ./jest-setup.js
import '@microsoft/jest-sarif';
// or
require('@microsoft/jest-sarif');
```
2. Including one of the following at the top of your test file
```ts
// my-test-file.js
import '@microsoft/jest-sarif';
// or
require('@microsoft/jest-sarif');
```
## Matchers
### `toMatchSarifLog`
Asserts that a value is an valid SARIF log.
```ts
it('validates my SARIF log', () => {
const sarifLog = buildSarifLog();
expect(sarifLog).toMatchSarifLog();
});
```
## Building Custom Matchers for SARIF Schema Fragments
You can also build your own matcher that will match a fragment of the SARIF schema. This is useful when you want to match a part of the schema, such as a `Result` object.
This is useful for tools and/or libraries that build SARIF logs incrementally, and want to validate each portion of the log generation as they compose the full log.
:warning: Note - this will only work for properties defined in the `definitions` section of the SARIF schema, as these are definitions that can be referenced through other schemas.
Example:
The following builds a matcher to match SARIF [Result objects](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html#_Toc10541076).
```ts
// jest-setup.js
const { buildMatcher } = require('@microsoft/jest-sarif');
const toMatchSarifResult = buildMatcher({
matcherName: 'toMatchSarifResult',
definitionName: 'result',
});
expect.extend({
toMatchSarifResult,
});
```
This creates a new schema validator that uses JSON pointers to reference specific SARIF Schema definitions. This is what allows us to generate portions of the schema dynamically, while ensuring the generated schemas adhere to the original.
To use in tests, call it as you would other Jest matchers:
```ts
it('validates my SARIF result', () => {
const result = buildSarifResult();
expect(result).toMatchSarifResult();
});
```
### Typescript
When using typescript in addition to the `buildMatcher` function, the dynamic matchers you build will not immediately be recognized by Jest's matcher types. To resolve this, you can add a one-time definition in a local types file that declares these additional matchers. Using the above `toMatchSarifResult` example, you'd add the following type extension to Jest's Matchers:
```ts
// types/jest-matchers.d.ts
declare global {
namespace jest {
interface Matchers<R> {
toMatchSarifResult(): R;
// other dynamic matchers
}
interface Expect {
toMatchSarifResult<T>(): jest.JestMatchers<T>;
// other dynamic matchers
}
}
}
```
## Attribution
This package was based on the [jest-json-schema](https://www.npmjs.com/package/jest-json-schema) package.
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com).
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
Any use of third-party trademarks or logos are subject to those third-party's policies.

Просмотреть файл

@ -0,0 +1,36 @@
{
"version": "2.1.0",
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.4.json",
"runs": [
{
"tool": {
"driver": {
"name": "ESLint"
}
},
"results": [
{
"ruleId": "no-unused-vars",
"level": "error",
"message": {
"text": "'x' is assigned a value but never used."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js",
"index": 0
},
"region": {
"startLine": 1,
"startColumn": 5
}
}
}
]
}
]
}
]
}

Просмотреть файл

@ -0,0 +1,21 @@
{
"ruleId": "no-unused-vars",
"level": "error",
"message": {
"text": "'x' is assigned a value but never used."
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {
"uri": "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js",
"index": 0
},
"region": {
"startLine": 1,
"startColumn": 5
}
}
}
]
}

Просмотреть файл

@ -0,0 +1,60 @@
// mimics importing from '@microsoft/jest-sarif' and ensures that we're correctly
// extending Jest's expect.
import '../../src';
describe('toMatchSarifLog', () => {
it('does not throw', () => {
// eslint-disable-next-line unicorn/no-null
expect(null).not.toMatchSarifLog();
// eslint-disable-next-line unicorn/no-useless-undefined
expect(undefined).not.toMatchSarifLog();
expect(1).not.toMatchSarifLog();
expect({}).not.toMatchSarifLog();
expect({ hello: 'world' }).not.toMatchSarifLog();
expect({ hello: 'a', world: 'b' }).not.toMatchSarifLog();
});
it('fails for wrong type', () => {
const testObj = { hello: 1 };
expect(() => expect(testObj).toMatchSarifLog()).toThrow(
"should NOT have additional properties, but found 'hello'"
);
});
it('fails for missing required keys', () => {
expect(() => expect({}).toMatchSarifLog()).toThrow("should have required property 'version'");
});
it('fails when additional properties are found but forbidden', () => {
const testObj = {
hello: 'world',
another: 'property',
};
expect(() => expect(testObj).toMatchSarifLog()).toThrow(
"should NOT have additional properties, but found 'hello'"
);
});
it('fails for matching schema when using .not', () => {
const sarifLog = require('../__fixtures__/sarif-log.json');
expect(() => expect(sarifLog).not.toMatchSarifLog()).toThrow(
'Expected value not to match schema'
);
});
it('assertion error matcherResult property contains matcher name and actual value', () => {
const testObj = { another: 'property' };
try {
expect(testObj).toMatchSarifLog();
} catch (error) {
// eslint-disable-next-line jest/no-try-expect, jest/no-conditional-expect
expect(error.matcherResult).toEqual({
actual: testObj,
message: expect.any(Function),
name: 'toMatchSarifLog',
pass: false,
});
}
});
});

Просмотреть файл

@ -0,0 +1,99 @@
import { Result } from 'sarif';
import { buildMatcher } from '../../src/build-matcher';
expect.extend({
toMatchSarifResult: buildMatcher<Result | unknown>({
matcherName: 'toMatchSarifResult',
definitionName: 'result',
}),
});
declare global {
namespace jest {
interface Matchers<R> {
toMatchSarifResult(): R;
}
interface Expect {
toMatchSarifResult<T>(): jest.JestMatchers<T>;
}
}
}
describe('toMatchSarifResult', () => {
it('does not throw', () => {
// eslint-disable-next-line unicorn/no-null
expect(null).not.toMatchSarifResult();
// eslint-disable-next-line unicorn/no-useless-undefined
expect(undefined).not.toMatchSarifResult();
expect(1).not.toMatchSarifResult();
expect({}).not.toMatchSarifResult();
expect({ hello: 'world' }).not.toMatchSarifResult();
expect({ hello: 'a', world: 'b' }).not.toMatchSarifResult();
});
it('fails for wrong type', () => {
const testObj = { hello: 1 };
expect(() => expect(testObj).toMatchSarifResult()).toThrow(
"should NOT have additional properties, but found 'hello'"
);
});
it('fails for missing required keys', () => {
expect(() => expect({}).toMatchSarifResult()).toThrow(
"should have required property 'message'"
);
});
it('fails when additional properties are found but forbidden', () => {
const testObj = {
hello: 'world',
another: 'property',
};
expect(() => expect(testObj).toMatchSarifResult()).toThrow(
"should NOT have additional properties, but found 'hello'"
);
});
it('fails for matching schema when using .not', () => {
const sarifResult = require('../__fixtures__/sarif-result.json');
expect(() => expect(sarifResult).not.toMatchSarifResult()).toThrow(
'Expected value not to match schema'
);
});
it('assertion error matcherResult property contains matcher name and actual value', () => {
const testObj = { another: 'property' };
try {
expect(testObj).toMatchSarifResult();
} catch (error) {
// eslint-disable-next-line jest/no-try-expect, jest/no-conditional-expect
expect(error.matcherResult).toEqual({
actual: testObj,
message: expect.any(Function),
name: 'toMatchSarifResult',
pass: false,
});
}
});
[
{
message: {
text: 'Foo',
},
},
{
ruleId: 'no-unused-vars',
level: 'error',
message: {
text: "'x' is assigned a value but never used.",
},
},
// eslint-disable-next-line unicorn/no-array-for-each
].forEach((value) => {
it('matches Result objects', () => {
expect(value).toMatchSarifResult();
});
});
});

Просмотреть файл

@ -0,0 +1 @@
require('./lib/index');

Просмотреть файл

@ -0,0 +1,18 @@
module.exports = {
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'js', 'json'],
transform: { '\\.ts$': 'ts-jest' },
coverageReporters: ['lcov', 'text-summary'],
// collectCoverage: !!process.env.CI,
collectCoverageFrom: ['src/**/*.ts'],
coveragePathIgnorePatterns: ['/templates/'],
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
testPathIgnorePatterns: ['/__fixtures__/'],
};

Просмотреть файл

@ -0,0 +1,38 @@
{
"name": "@microsoft/jest-sarif",
"version": "0.0.1",
"description": "A collection of jest matchers for working with SARIF",
"main": "index.js",
"scripts": {
"build": "tsc --build",
"prepare": "yarn build",
"test": "jest"
},
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/sarif-js-sdk.git"
},
"keywords": [
"jest",
"jest-matcher",
"sarif",
"jest-sarif"
],
"engines": {
"node": ">= 12.11.*"
},
"files": [
"/lib"
],
"author": "Microsoft Corporation",
"license": "MIT",
"bugs": {
"url": "https://github.com/microsoft/sarif-js-sdk/issues"
},
"homepage": "https://github.com/microsoft/sarif-js-sdk#readme",
"dependencies": {
"ajv": "^6.12.6",
"chalk": "^4.1.0",
"sync-fetch": "^0.3.0"
}
}

Просмотреть файл

@ -0,0 +1,171 @@
import { EOL } from 'os';
import Ajv, { AdditionalPropertiesParams, IfParams } from 'ajv';
import { matcherHint } from 'jest-matcher-utils';
import chalk from 'chalk';
import { getSchema } from './sarif-schema';
import { BuildMatcherOptions } from './types';
// Keywords where the `Expected: ...` output is hidden
const ERROR_KEYWORDS_HIDE_EXPECTED = new Set([
'type',
// String
'pattern',
'format',
'minLength',
'maxLength',
// Number
'minimum',
'maximum',
'exclusiveMinimum',
'exclusiveMaximum',
'multipleOf',
// Object
'minProperties',
'maxProperties',
'required',
// Array
'minItems',
'maxItems',
]);
const ERROR_KEYWORDS_SHOW_RECEIVED = new Set(['if', 'not']);
const isObject = (value: unknown) => value !== null && typeof value === 'object';
const formatForPrint = (input: unknown, displayType: boolean = true) => {
if (input === undefined || input === null) {
return chalk.yellow(`<${input}>`);
}
if (input === '') {
return chalk.yellow('<empty string>');
}
if (Array.isArray(input) || isObject(input)) {
return (
(displayType ? chalk.yellow(Array.isArray(input) ? '<array> ' : '<object> ') : '') +
JSON.stringify(input)
);
}
return `${chalk.yellow(`<${typeof input}>`)} ${input}`;
};
function getValidatorAndSchema(options: BuildMatcherOptions): [Ajv.Ajv, object | boolean] {
const ajv = new Ajv({
schemaId: 'auto',
validateSchema: false,
});
const schema = getSchema(options);
const draft4MetaSchema = require('ajv/lib/refs/json-schema-draft-04.json');
ajv.addMetaSchema(draft4MetaSchema);
if (options.definitionName) {
// When a definitionName is provided, we need to load the reference schema, which is the SARIF schema itself.
// This allows us to reference definitions in the SARIF schema via JSON pointers.
// eg. "$ref": "#/definition/result"
ajv.addSchema(
getSchema({
schemaName: 'sarif',
})
);
}
return [ajv, schema];
}
/**
* Builds a Jest matcher based on the supplied @type {BuildMatcherOptions}.
*
* @param options
* @param options.matcherName The name of the matcher.
* @param options.schemaName [Optional] The name of the schema to load.
* @param options.definitionName [Optional] The name of the SARIF schema definition fragment to dynamically build a schema for.
* @returns {jest.CustomMatcher}
*/
export function buildMatcher<T>(options: BuildMatcherOptions): jest.CustomMatcher {
const [ajv, schema] = getValidatorAndSchema(options);
// eslint-disable-next-line no-underscore-dangle
const { verbose } = ajv._opts;
return function (received: T) {
const validate = ajv.compile(schema);
const pass = validate(received) as boolean;
const message = pass
? () => {
let messageToPrint = `${matcherHint(
`.not.${options.matcherName}`,
undefined,
'schema'
)}${EOL}${EOL}Expected value not to match schema${EOL}${EOL}`;
if (verbose) {
messageToPrint += chalk.red(`received${EOL}${formatForPrint(received)}${EOL}`);
}
return messageToPrint;
}
: () => {
let messageToPrint = `${'received'}${EOL}`;
for (const error of validate.errors!) {
let line = error.message;
if (error.keyword === 'additionalProperties') {
line = `${error.message}, but found '${
(error.params as AdditionalPropertiesParams).additionalProperty
}'`;
} else if ((error as any).dataPath) {
line = `${(error as any).dataPath} ${error.message}`;
}
if (verbose && error.schemaPath) {
if (!ERROR_KEYWORDS_HIDE_EXPECTED.has(error.keyword)) {
switch (error.keyword) {
case 'if':
line += `${EOL} Expected: ${formatForPrint(
(error.parentSchema as Record<string, any>)[
(error.params as IfParams).failingKeyword
],
false
)}`;
break;
default:
line += `${EOL} Expected: ${formatForPrint(error.schema, false)}`;
break;
}
}
if ((error as any).dataPath) {
line += `${EOL} Received: ${formatForPrint(error.data)}`;
} else if (ERROR_KEYWORDS_SHOW_RECEIVED.has(error.keyword)) {
line += `${EOL} Received: ${formatForPrint(error.data, false)}`;
}
line += `${EOL} Path: ${(validate.schema as any).$id || ''}${
error.schemaPath
}`;
}
messageToPrint += chalk.red(` ${line}${EOL}`);
}
return `${matcherHint(
`.${options.matcherName}`,
undefined,
'schema'
)}${EOL}${EOL}${messageToPrint}`;
};
return {
actual: received,
message,
name: options.matcherName,
pass,
};
};
}

Просмотреть файл

@ -0,0 +1,4 @@
export { toMatchSarifLog } from './matchers/to-match-sarif-log';
export { buildMatcher } from './build-matcher';
export type { BuildMatcherOptions } from './types';

Просмотреть файл

@ -0,0 +1,34 @@
import { Log } from 'sarif';
import { buildMatcher } from '../build-matcher';
type MaybeSarifLog = Log | unknown;
declare global {
namespace jest {
interface Matchers<R, T> {
/**
* Asserts that ${value} is an valid SARIF log.
* @example
* expect(value).toMatchSarifLog();
*/
toMatchSarifLog(): R;
}
interface Expect {
/**
* Asserts that ${value} is an `Array` containing only `Boolean` values.
* @example
* expect(value).toEqual(
* expect.toMatchSarifLog()
* );
*/
toMatchSarifLog<T>(): jest.JestMatchers<T>;
}
}
}
export const toMatchSarifLog = buildMatcher<MaybeSarifLog>({
matcherName: 'toMatchSarifLog',
schemaName: 'sarif',
});
expect.extend({ toMatchSarifLog });

Просмотреть файл

@ -0,0 +1,45 @@
import fetch from 'sync-fetch';
import { SchemaOptions } from './types';
// While this seems overkill for a single item, ultimately this is how I see us supporting
// multiple versions of the SARIF schema. We'd ideally provide a version to load, which would
// be included in this map.
const schemaCache = new Map<string, object>();
const schemaUri = new Map<string, string>([
['sarif', 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json'],
]);
export function getSchema(options: SchemaOptions) {
if (options.schemaName) {
const schemaName = options.schemaName;
const uri = schemaUri.get(schemaName);
if (!schemaCache.has(schemaName) && uri) {
const schema = fetch(uri).json();
schemaCache.set(schemaName, schema);
}
return schemaCache.get(schemaName)!;
}
if (options.definitionName) {
return getFragmentSchema(options.definitionName);
}
throw new Error('You must provide either a schemaName or definitionName in the schema options');
}
function getFragmentSchema(definitionName: string) {
return JSON.parse(`{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Static Analysis Results Format (SARIF) Version 2.1.0-rtm.5 ${definitionName} fragment",
"id": "https://sarif/${definitionName}-fragment.json",
"description": "A dynamic fragment of a SARIF schema.",
"allOf": [
{
"$ref": "https://raw.githubusercontent.com/schemastore/schemastore/master/src/schemas/json/sarif-2.1.0-rtm.5.json#/definitions/${definitionName}"
}
]
}`);
}

Просмотреть файл

@ -0,0 +1,11 @@
export interface SchemaOptions {
schemaName?: string;
definitionName?: string;
}
/**
* Matcher options provided to the buildMatcher function in order to build a dynamic Jest matcher.
*/
export interface BuildMatcherOptions extends SchemaOptions {
matcherName: string;
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["./src"]
}

Просмотреть файл

@ -0,0 +1,17 @@
module.exports = {
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'js', 'json'],
transform: { '\\.ts$': 'ts-jest' },
coverageReporters: ['lcov', 'text-summary'],
// collectCoverage: !!process.env.CI,
collectCoverageFrom: ['src/**/*.ts'],
coveragePathIgnorePatterns: ['/templates/'],
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
};

Просмотреть файл

@ -9,6 +9,10 @@
"license": "MIT",
"scripts": {
"build": "tsc --build",
"prepare": "yarn build",
"test": "jest --passWithNoTests"
},
"engines": {
"node": ">= 12.11.*"
}
}

Просмотреть файл

@ -5,5 +5,5 @@
"rootDir": "src",
"resolveJsonModule": true
},
"include": ["./src"]
"include": ["./src", "./src/schemas/*.json"]
}

Просмотреть файл

@ -7,13 +7,15 @@
"importHelpers": true,
"module": "commonjs",
"strict": true,
"target": "es2020",
"target": "es2019",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {
"@microsoft/jest-sarif": ["./packages/jest-sarif/lib/index.d.ts"],
"@microsoft/sarif-builder": ["./packages/sarif-builder/lib/index.d.ts"],
"*": ["./types/*"]
}
},
"types": ["jest", "node"]
},
"exclude": ["**/node_modules/**"]
}

Просмотреть файл

@ -1,4 +1,4 @@
{
"files": [],
"references": [{ "path": "./packages/sarif-builder" }]
"references": [{ "path": "./packages/jest-sarif" }, { "path": "./packages/sarif-builder" }]
}

1
types/sync-fetch.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
declare module 'sync-fetch';