diff --git a/README.md b/README.md index f4be6e0..e98cc2d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ jobs: # java-junit # jest-junit # mocha-json + # rspec-json reporter: '' # Allows you to generate only the summary. diff --git a/__tests__/__outputs__/rspec-json.md b/__tests__/__outputs__/rspec-json.md new file mode 100644 index 0000000..04bae8a --- /dev/null +++ b/__tests__/__outputs__/rspec-json.md @@ -0,0 +1,16 @@ +![Tests failed](https://img.shields.io/badge/tests-1%20passed%2C%201%20failed%2C%201%20skipped-critical) +## ❌ fixtures/rspec-json.json +**3** tests were completed in **0ms** with **1** passed, **1** failed and **1** skipped. +|Test suite|Passed|Failed|Skipped|Time| +|:---|---:|---:|---:|---:| +|[./spec/config/check_env_vars_spec.rb](#r0s0)|1✅|1❌|1⚪|0ms| +### ❌ ./spec/config/check_env_vars_spec.rb +``` +CheckEnvVars#call when all env vars are defined behaves like success load + ❌ CheckEnvVars#call when all env vars are defined behaves like success load fails in assertion + (#ActiveSupport::BroadcastLogger:0x00007f1007fedf58).debug("All config env vars exist") + expected: 0 times with arguments: ("All config env vars exist") + received: 1 time with arguments: ("All config env vars exist") + ✅ CheckEnvVars#call when all env vars are defined behaves like success load logs success message + ⚪ CheckEnvVars#call when all env vars are defined behaves like success load skips the test +``` \ No newline at end of file diff --git a/__tests__/__snapshots__/rspec-json.test.ts.snap b/__tests__/__snapshots__/rspec-json.test.ts.snap new file mode 100644 index 0000000..cc14bfb --- /dev/null +++ b/__tests__/__snapshots__/rspec-json.test.ts.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`rspec-json tests report from ./reports/rspec-json test results matches snapshot 1`] = ` +TestRunResult { + "path": "fixtures/rspec-json.json", + "suites": [ + TestSuiteResult { + "groups": [ + TestGroupResult { + "name": "CheckEnvVars#call when all env vars are defined behaves like success load", + "tests": [ + TestCaseResult { + "error": { + "details": "/usr/local/bundle/ruby/3.3.0/gems/net-http-0.4.1/lib/net/http.rb:1603:in \`initialize' +./config/check_env_vars.rb:11:in \`call' +./spec/config/check_env_vars_spec.rb:7:in \`block (3 levels) in ' +./spec/config/check_env_vars_spec.rb:19:in \`block (4 levels) in '", + "line": 11, + "message": "(#ActiveSupport::BroadcastLogger:0x00007f1007fedf58).debug("All config env vars exist") + expected: 0 times with arguments: ("All config env vars exist") + received: 1 time with arguments: ("All config env vars exist")", + "path": "./config/check_env_vars.rb", + }, + "name": "CheckEnvVars#call when all env vars are defined behaves like success load fails in assertion", + "result": "failed", + "time": 0.004411051, + }, + TestCaseResult { + "error": undefined, + "name": "CheckEnvVars#call when all env vars are defined behaves like success load logs success message", + "result": "success", + "time": 0.079159625, + }, + TestCaseResult { + "error": undefined, + "name": "CheckEnvVars#call when all env vars are defined behaves like success load skips the test", + "result": "skipped", + "time": 0.000023007, + }, + ], + }, + ], + "name": "./spec/config/check_env_vars_spec.rb", + "totalTime": undefined, + }, + ], + "totalTime": 0.19118387, +} +`; diff --git a/__tests__/fixtures/empty/rspec-json.json b/__tests__/fixtures/empty/rspec-json.json new file mode 100644 index 0000000..3629097 --- /dev/null +++ b/__tests__/fixtures/empty/rspec-json.json @@ -0,0 +1,17 @@ +{ + "version": "3.13.0", + "messages": [ + "No examples found." + ], + "examples": [ + + ], + "summary": { + "duration": 0.002514266, + "example_count": 0, + "failure_count": 0, + "pending_count": 0, + "errors_outside_of_examples_count": 0 + }, + "summary_line": "0 examples, 0 failures" +} diff --git a/__tests__/fixtures/rspec-json.json b/__tests__/fixtures/rspec-json.json new file mode 100644 index 0000000..34d4e49 --- /dev/null +++ b/__tests__/fixtures/rspec-json.json @@ -0,0 +1,53 @@ +{ + "version": "3.13.0", + "examples": [ + { + "id": "./spec/config/check_env_vars_spec.rb[1:1:1:1:1]", + "description": "logs success message", + "full_description": "CheckEnvVars#call when all env vars are defined behaves like success load logs success message", + "status": "passed", + "file_path": "./spec/config/check_env_vars_spec.rb", + "line_number": 12, + "run_time": 0.079159625, + "pending_message": null + }, + { + "id": "./spec/config/check_env_vars_spec.rb[1:1:1:1:2]", + "description": "fails in assertion", + "full_description": "CheckEnvVars#call when all env vars are defined behaves like success load fails in assertion", + "status": "failed", + "file_path": "./spec/config/check_env_vars_spec.rb", + "line_number": 17, + "run_time": 0.004411051, + "pending_message": null, + "exception": { + "class": "RSpec::Mocks::MockExpectationError", + "message": "(#ActiveSupport::BroadcastLogger:0x00007f1007fedf58).debug(\"All config env vars exist\")\n expected: 0 times with arguments: (\"All config env vars exist\")\n received: 1 time with arguments: (\"All config env vars exist\")", + "backtrace": [ + "/usr/local/bundle/ruby/3.3.0/gems/net-http-0.4.1/lib/net/http.rb:1603:in `initialize'", + "./config/check_env_vars.rb:11:in `call'", + "./spec/config/check_env_vars_spec.rb:7:in `block (3 levels) in \u003ctop (required)\u003e'", + "./spec/config/check_env_vars_spec.rb:19:in `block (4 levels) in \u003ctop (required)\u003e'" + ] + } + }, + { + "id": "./spec/config/check_env_vars_spec.rb[1:1:1:1:4]", + "description": "skips the test", + "full_description": "CheckEnvVars#call when all env vars are defined behaves like success load skips the test", + "status": "pending", + "file_path": "./spec/config/check_env_vars_spec.rb", + "line_number": 27, + "run_time": 2.3007e-05, + "pending_message": "Temporarily skipped with xit" + } + ], + "summary": { + "duration": 0.19118387, + "example_count": 3, + "failure_count": 1, + "pending_count": 1, + "errors_outside_of_examples_count": 0 + }, + "summary_line": "3 examples, 1 failures, 1 pending" +} diff --git a/__tests__/rspec-json.test.ts b/__tests__/rspec-json.test.ts new file mode 100644 index 0000000..f77475a --- /dev/null +++ b/__tests__/rspec-json.test.ts @@ -0,0 +1,45 @@ +import * as fs from 'fs' +import * as path from 'path' + +import {RspecJsonParser} from '../src/parsers/rspec-json/rspec-json-parser' +import {ParseOptions} from '../src/test-parser' +import {getReport} from '../src/report/get-report' +import {normalizeFilePath} from '../src/utils/path-utils' + +describe('rspec-json tests', () => { + it('produces empty test run result when there are no test cases', async () => { + const fixturePath = path.join(__dirname, 'fixtures', 'empty', 'rspec-json.json') + const filePath = normalizeFilePath(path.relative(__dirname, fixturePath)) + const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'}) + + const opts: ParseOptions = { + parseErrors: true, + trackedFiles: [] + } + + const parser = new RspecJsonParser(opts) + const result = await parser.parse(filePath, fileContent) + expect(result.tests).toBe(0) + expect(result.result).toBe('success') + }) + + it('report from ./reports/rspec-json test results matches snapshot', async () => { + const fixturePath = path.join(__dirname, 'fixtures', 'rspec-json.json') + const outputPath = path.join(__dirname, '__outputs__', 'rspec-json.md') + const filePath = normalizeFilePath(path.relative(__dirname, fixturePath)) + const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'}) + + const opts: ParseOptions = { + parseErrors: true, + trackedFiles: ['test/main.test.js', 'test/second.test.js', 'lib/main.js'] + } + + const parser = new RspecJsonParser(opts) + const result = await parser.parse(filePath, fileContent) + expect(result).toMatchSnapshot() + + const report = getReport([result]) + fs.mkdirSync(path.dirname(outputPath), {recursive: true}) + fs.writeFileSync(outputPath, report) + }) +}) diff --git a/action.yml b/action.yml index 6f35ebf..8d9d728 100644 --- a/action.yml +++ b/action.yml @@ -31,6 +31,7 @@ inputs: - java-junit - jest-junit - mocha-json + - rspec-json - swift-xunit required: true list-suites: diff --git a/dist/index.js b/dist/index.js index 47a1b8d..dee4468 100644 --- a/dist/index.js +++ b/dist/index.js @@ -265,6 +265,7 @@ const dotnet_trx_parser_1 = __nccwpck_require__(2664); const java_junit_parser_1 = __nccwpck_require__(676); const jest_junit_parser_1 = __nccwpck_require__(1113); const mocha_json_parser_1 = __nccwpck_require__(6043); +const rspec_json_parser_1 = __nccwpck_require__(406); const swift_xunit_parser_1 = __nccwpck_require__(5366); const path_utils_1 = __nccwpck_require__(4070); const github_utils_1 = __nccwpck_require__(3522); @@ -434,6 +435,8 @@ class TestReporter { return new jest_junit_parser_1.JestJunitParser(options); case 'mocha-json': return new mocha_json_parser_1.MochaJsonParser(options); + case 'rspec-json': + return new rspec_json_parser_1.RspecJsonParser(options); case 'swift-xunit': return new swift_xunit_parser_1.SwiftXunitParser(options); default: @@ -1403,6 +1406,121 @@ class MochaJsonParser { exports.MochaJsonParser = MochaJsonParser; +/***/ }), + +/***/ 406: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.RspecJsonParser = void 0; +const test_results_1 = __nccwpck_require__(2768); +class RspecJsonParser { + constructor(options) { + this.options = options; + } + parse(path, content) { + return __awaiter(this, void 0, void 0, function* () { + const mocha = this.getRspecJson(path, content); + const result = this.getTestRunResult(path, mocha); + result.sort(true); + return Promise.resolve(result); + }); + } + getRspecJson(path, content) { + try { + return JSON.parse(content); + } + catch (e) { + throw new Error(`Invalid JSON at ${path}\n\n${e}`); + } + } + getTestRunResult(resultsPath, rspec) { + const suitesMap = {}; + const getSuite = (test) => { + var _a; + const path = test.file_path; + return (_a = suitesMap[path]) !== null && _a !== void 0 ? _a : (suitesMap[path] = new test_results_1.TestSuiteResult(path, [])); + }; + for (const test of rspec.examples) { + const suite = getSuite(test); + if (test.status === 'failed') { + this.processTest(suite, test, 'failed'); + } + else if (test.status === 'passed') { + this.processTest(suite, test, 'success'); + } + else if (test.status === 'pending') { + this.processTest(suite, test, 'skipped'); + } + } + const suites = Object.values(suitesMap); + return new test_results_1.TestRunResult(resultsPath, suites, rspec.summary.duration); + } + processTest(suite, test, result) { + var _a; + const groupName = test.full_description !== test.description + ? test.full_description.substr(0, test.full_description.length - test.description.length).trimEnd() + : null; + let group = suite.groups.find(grp => grp.name === groupName); + if (group === undefined) { + group = new test_results_1.TestGroupResult(groupName, []); + suite.groups.push(group); + } + const error = this.getTestCaseError(test); + const testCase = new test_results_1.TestCaseResult(test.full_description, result, (_a = test.run_time) !== null && _a !== void 0 ? _a : 0, error); + group.tests.push(testCase); + } + getTestCaseError(test) { + var _a, _b; + const backtrace = (_a = test.exception) === null || _a === void 0 ? void 0 : _a.backtrace; + const message = (_b = test.exception) === null || _b === void 0 ? void 0 : _b.message; + if (backtrace === undefined) { + return undefined; + } + let path; + let line; + const details = backtrace.join('\n'); + const src = this.getExceptionSource(backtrace); + if (src) { + path = src.path; + line = src.line; + } + return { + path, + line, + message, + details + }; + } + getExceptionSource(backtrace) { + const re = /^(.*?):(\d+):/; + for (const str of backtrace) { + const match = str.match(re); + if (match !== null) { + const [_, path, lineStr] = match; + if (path.startsWith('./')) { + const line = parseInt(lineStr); + return { path, line }; + } + } + } + return undefined; + } +} +exports.RspecJsonParser = RspecJsonParser; + + /***/ }), /***/ 5366: @@ -11219,7 +11337,8 @@ module.exports = function (/**String*/ input, /** object */ options) { * * @return Array */ - getEntries: function () { + getEntries: function (/**String*/ password) { + _zip.password=password; return _zip ? _zip.entries : []; }, @@ -11296,7 +11415,7 @@ module.exports = function (/**String*/ input, /** object */ options) { return true; } - var content = item.getData(); + var content = item.getData(_zip.password); if (!content) throw new Error(Utils.Errors.CANT_EXTRACT_FILE); if (filetools.fs.existsSync(target) && !overwrite) { @@ -11458,9 +11577,9 @@ module.exports = function (/**String*/ input, /** object */ options) { callback(getError("Unable to set times", filePath)); return; } - fileEntries.delete(entry); // call the callback if it was last entry done(); + fileEntries.delete(entry); }); }); } @@ -11624,7 +11743,9 @@ module.exports = function () { set time(val) { setTime(val); }, - + get timeHighByte() { + return (_time >>> 8) & 0xff; + }, get crc() { return _crc; }, @@ -12238,8 +12359,12 @@ function decrypt(/*Buffer*/ data, /*Object*/ header, /*String, Buffer*/ pwd) { // 2. decrypt salt what is always 12 bytes and is a part of file content const salt = decrypter(data.slice(0, 12)); - // 3. does password meet expectations - if (salt[11] !== header.crc >>> 24) { + // if bit 3 (0x08) of the general-purpose flags field is set, check salt[11] with the high byte of the header time + // 2 byte data block (as per Info-Zip spec), otherwise check with the high byte of the header entry + const verifyByte = ((header.flags & 0x8) === 0x8) ? header.timeHighByte : header.crc >>> 24; + + //3. does password meet expectations + if (salt[11] !== verifyByte) { throw "ADM-ZIP: Wrong Password"; } @@ -13205,6 +13330,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) { _comment = Buffer.alloc(0), mainHeader = new Headers.MainHeader(), loadedEntries = false; + var password = null; // assign options const opts = Object.assign(Object.create(null), options); @@ -13456,7 +13582,7 @@ module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) { // 1.2. postheader - data after data header const postHeader = Buffer.alloc(entryNameLen + entry.extra.length); entry.rawEntryName.copy(postHeader, 0); - postHeader.copy(entry.extra, entryNameLen); + entry.extra.copy(postHeader, entryNameLen); // 2. offsets const dataLength = dataHeader.length + postHeader.length + compressedData.length; @@ -26637,31 +26763,21 @@ module.exports.CancelError = CancelError; "use strict"; -const os = __nccwpck_require__(2037); const pico = __nccwpck_require__(3322); - -const isWindows = os.platform() === 'win32'; +const utils = __nccwpck_require__(479); function picomatch(glob, options, returnState = false) { // default to os.platform() if (options && (options.windows === null || options.windows === undefined)) { // don't mutate the original options object - options = { ...options, windows: isWindows }; + options = { ...options, windows: utils.isWindows() }; } + return pico(glob, options, returnState); } +Object.assign(picomatch, pico); module.exports = picomatch; -// public api -module.exports.test = pico.test; -module.exports.matchBase = pico.matchBase; -module.exports.isMatch = pico.isMatch; -module.exports.parse = pico.parse; -module.exports.scan = pico.scan; -module.exports.compileRe = pico.compileRe; -module.exports.toRegex = pico.toRegex; -// for tests -module.exports.makeRe = pico.makeRe; /***/ }), @@ -27068,8 +27184,8 @@ const parse = (input, options) => { if (tok.value || tok.output) append(tok); if (prev && prev.type === 'text' && tok.type === 'text') { + prev.output = (prev.output || prev.value) + tok.value; prev.value += tok.value; - prev.output = (prev.output || '') + tok.value; return; } @@ -27557,10 +27673,6 @@ const parse = (input, options) => { const next = peek(); let output = value; - if (next === '<' && !utils.supportsLookbehinds()) { - throw new Error('Node.js v10 or higher is required for regex lookbehinds'); - } - if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { output = `\\${value}`; } @@ -28702,6 +28814,7 @@ module.exports = scan; /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; +/*global navigator*/ const { @@ -28717,22 +28830,25 @@ exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); +exports.isWindows = () => { + if (typeof navigator !== 'undefined' && navigator.platform) { + const platform = navigator.platform.toLowerCase(); + return platform === 'win32' || platform === 'windows'; + } + + if (typeof process !== 'undefined' && process.platform) { + return process.platform === 'win32'; + } + + return false; +}; + exports.removeBackslashes = str => { return str.replace(REGEX_REMOVE_BACKSLASH, match => { return match === '\\' ? '' : match; }); }; -exports.supportsLookbehinds = () => { - if (typeof process !== 'undefined') { - const segs = process.version.slice(1).split('.').map(Number); - if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { - return true; - } - } - return false; -}; - exports.escapeLast = (input, char, lastIdx) => { const idx = input.lastIndexOf(char, lastIdx); if (idx === -1) return input; diff --git a/package-lock.json b/package-lock.json index b728053..d630191 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,21 @@ { - "name": "test-check", - "version": "1.8.0", + "name": "test-reporter", + "version": "1.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "test-check", - "version": "1.8.0", + "name": "test-reporter", + "version": "1.9.0", "license": "MIT", "dependencies": { "@actions/core": "^1.10.1", "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", - "adm-zip": "^0.5.10", + "adm-zip": "^0.5.12", "fast-glob": "^3.3.2", - "got": "^11.8.2", - "picomatch": "^3.0.1", + "got": "^11.8.6", + "picomatch": "^4.0.2", "xml2js": "^0.6.2" }, "devDependencies": { @@ -24,27 +24,27 @@ "@octokit/webhooks-types": "^7.3.1", "@types/adm-zip": "^0.5.5", "@types/github-slugger": "^1.3.0", - "@types/jest": "^29.5.11", - "@types/node": "^20.11.9", + "@types/jest": "^29.5.12", + "@types/node": "^18.19.28", "@types/picomatch": "^2.3.3", "@types/xml2js": "^0.4.14", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vercel/ncc": "^0.38.1", "eol-converter-cli": "^1.0.8", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-github": "^4.10.1", + "eslint-plugin-github": "^4.10.2", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jest": "^27.6.3", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "jest-circus": "^29.7.0", "jest-junit": "^16.0.0", "js-yaml": "^4.1.0", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "ts-jest": "^29.1.2", - "typescript": "^5.3.3" + "typescript": "^5.4.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -820,9 +820,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -843,13 +843,13 @@ "dev": true }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -870,9 +870,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1659,9 +1659,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1689,9 +1689,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.9.tgz", - "integrity": "sha512-CQXNuMoS/VcoAMISe5pm4JnEd1Br5jildbQEToEMQvutmv+EaQr90ry9raiudgpyDuqFiV9e4rnjSfLNq12M5w==", + "version": "18.19.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.28.tgz", + "integrity": "sha512-J5cOGD9n4x3YGgVuaND6khm5x07MMdAKkRyXnjVR6KFhLMNh2yONGiP7Z+4+tBOt5mK+GvDTiacTOVGGpqiecw==", "dependencies": { "undici-types": "~5.26.4" } @@ -1747,16 +1747,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.1.tgz", - "integrity": "sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.19.1", - "@typescript-eslint/type-utils": "6.19.1", - "@typescript-eslint/utils": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1782,13 +1782,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz", - "integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1799,9 +1799,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz", - "integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1812,13 +1812,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz", - "integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1840,17 +1840,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.1.tgz", - "integrity": "sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.19.1", - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/typescript-estree": "6.19.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -1865,12 +1865,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz", - "integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1906,15 +1906,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.1.tgz", - "integrity": "sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.19.1", - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/typescript-estree": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -1934,13 +1934,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz", - "integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1951,9 +1951,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz", - "integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1964,13 +1964,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz", - "integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1992,12 +1992,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz", - "integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2050,13 +2050,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.1.tgz", - "integrity": "sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.19.1", - "@typescript-eslint/utils": "6.19.1", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2077,13 +2077,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz", - "integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2094,9 +2094,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz", - "integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2107,13 +2107,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz", - "integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/visitor-keys": "6.19.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2135,17 +2135,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.1.tgz", - "integrity": "sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.19.1", - "@typescript-eslint/types": "6.19.1", - "@typescript-eslint/typescript-estree": "6.19.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -2160,12 +2160,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz", - "integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2320,9 +2320,9 @@ } }, "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.12.tgz", + "integrity": "sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==", "engines": { "node": ">=6.0" } @@ -3418,16 +3418,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -3611,14 +3611,14 @@ } }, "node_modules/eslint-plugin-github": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz", - "integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.2.tgz", + "integrity": "sha512-F1F5aAFgi1Y5hYoTFzGQACBkw5W1hu2Fu5FSTrMlXqrojJnKl1S2pWO/rprlowRQpt+hzHhqSpsfnodJEVd5QA==", "dev": true, "dependencies": { "@github/browserslist-config": "^1.0.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", "aria-query": "^5.3.0", "eslint-config-prettier": ">=8.0.0", "eslint-plugin-escompat": "^3.3.3", @@ -3641,6 +3641,220 @@ "eslint": "^8.0.1" } }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", + "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/type-utils": "7.5.0", + "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/parser": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/scope-manager": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", + "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/type-utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", + "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.5.0", + "@typescript-eslint/utils": "7.5.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/types": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", + "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", + "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/visitor-keys": "7.5.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/utils": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", + "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.5.0", + "@typescript-eslint/types": "7.5.0", + "@typescript-eslint/typescript-estree": "7.5.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", + "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.5.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-github/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-github/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint-plugin-i18n-text": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", @@ -3712,9 +3926,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.6.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz", - "integrity": "sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==", + "version": "27.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", + "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -3723,7 +3937,7 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", "eslint": "^7.0.0 || ^8.0.0", "jest": "*" }, @@ -6199,11 +6413,11 @@ "dev": true }, "node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -6292,9 +6506,9 @@ } }, "node_modules/prettier": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", - "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -7219,9 +7433,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 9742e5d..87251d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "test-check", - "version": "1.8.0", + "name": "test-reporter", + "version": "1.9.0", "private": true, "description": "Presents test results from popular testing frameworks as Github check run", "main": "lib/main.js", @@ -21,7 +21,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/dorny/test-check.git" + "url": "git+https://github.com/dorny/test-reporter.git" }, "keywords": [ "actions", @@ -35,10 +35,10 @@ "@actions/core": "^1.10.1", "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", - "adm-zip": "^0.5.10", + "adm-zip": "^0.5.12", "fast-glob": "^3.3.2", - "got": "^11.8.2", - "picomatch": "^3.0.1", + "got": "^11.8.6", + "picomatch": "^4.0.2", "xml2js": "^0.6.2" }, "devDependencies": { @@ -47,27 +47,27 @@ "@octokit/webhooks-types": "^7.3.1", "@types/adm-zip": "^0.5.5", "@types/github-slugger": "^1.3.0", - "@types/jest": "^29.5.11", - "@types/node": "^20.11.9", + "@types/jest": "^29.5.12", + "@types/node": "^18.19.28", "@types/picomatch": "^2.3.3", "@types/xml2js": "^0.4.14", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vercel/ncc": "^0.38.1", "eol-converter-cli": "^1.0.8", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-github": "^4.10.1", + "eslint-plugin-github": "^4.10.2", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jest": "^27.6.3", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "jest-circus": "^29.7.0", "jest-junit": "^16.0.0", "js-yaml": "^4.1.0", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "ts-jest": "^29.1.2", - "typescript": "^5.3.3" + "typescript": "^5.4.3" }, "jest-junit": { "suiteName": "jest tests", diff --git a/src/main.ts b/src/main.ts index c3c89ef..8f481af 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,6 +15,7 @@ import {DotnetTrxParser} from './parsers/dotnet-trx/dotnet-trx-parser' import {JavaJunitParser} from './parsers/java-junit/java-junit-parser' import {JestJunitParser} from './parsers/jest-junit/jest-junit-parser' import {MochaJsonParser} from './parsers/mocha-json/mocha-json-parser' +import {RspecJsonParser} from './parsers/rspec-json/rspec-json-parser' import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser' import {normalizeDirPath, normalizeFilePath} from './utils/path-utils' @@ -223,6 +224,8 @@ class TestReporter { return new JestJunitParser(options) case 'mocha-json': return new MochaJsonParser(options) + case 'rspec-json': + return new RspecJsonParser(options) case 'swift-xunit': return new SwiftXunitParser(options) default: diff --git a/src/parsers/rspec-json/rspec-json-parser.ts b/src/parsers/rspec-json/rspec-json-parser.ts new file mode 100644 index 0000000..bf404a1 --- /dev/null +++ b/src/parsers/rspec-json/rspec-json-parser.ts @@ -0,0 +1,113 @@ +import { Console } from 'console' +import {ParseOptions, TestParser} from '../../test-parser' +import { + TestCaseError, + TestCaseResult, + TestExecutionResult, + TestGroupResult, + TestRunResult, + TestSuiteResult +} from '../../test-results' +import {RspecJson, RspecExample} from './rspec-json-types' + +export class RspecJsonParser implements TestParser { + assumedWorkDir: string | undefined + + constructor(readonly options: ParseOptions) {} + + async parse(path: string, content: string): Promise { + const mocha = this.getRspecJson(path, content) + const result = this.getTestRunResult(path, mocha) + result.sort(true) + return Promise.resolve(result) + } + + private getRspecJson(path: string, content: string): RspecJson { + try { + return JSON.parse(content) + } catch (e) { + throw new Error(`Invalid JSON at ${path}\n\n${e}`) + } + } + + private getTestRunResult(resultsPath: string, rspec: RspecJson): TestRunResult { + const suitesMap: {[path: string]: TestSuiteResult} = {} + + const getSuite = (test: RspecExample): TestSuiteResult => { + const path = test.file_path + return suitesMap[path] ?? (suitesMap[path] = new TestSuiteResult(path, [])) + } + + for (const test of rspec.examples) { + const suite = getSuite(test) + if (test.status === 'failed') { + this.processTest(suite, test, 'failed') + } else if (test.status === 'passed') { + this.processTest(suite, test, 'success') + } else if (test.status === 'pending') { + this.processTest(suite, test, 'skipped') + } + } + + const suites = Object.values(suitesMap) + return new TestRunResult(resultsPath, suites, rspec.summary.duration) + } + + private processTest(suite: TestSuiteResult, test: RspecExample, result: TestExecutionResult): void { + const groupName = + test.full_description !== test.description + ? test.full_description.substr(0, test.full_description.length - test.description.length).trimEnd() + : null + + let group = suite.groups.find(grp => grp.name === groupName) + if (group === undefined) { + group = new TestGroupResult(groupName, []) + suite.groups.push(group) + } + + const error = this.getTestCaseError(test) + const testCase = new TestCaseResult(test.full_description, result, test.run_time ?? 0, error) + group.tests.push(testCase) + } + + private getTestCaseError(test: RspecExample): TestCaseError | undefined { + const backtrace = test.exception?.backtrace + const message = test.exception?.message + if (backtrace === undefined) { + return undefined + } + + let path + let line + const details = backtrace.join('\n') + + const src = this.getExceptionSource(backtrace) + if (src) { + path = src.path + line = src.line + } + + return { + path, + line, + message, + details + } + } + + private getExceptionSource(backtrace: string[]): {path: string; line: number} | undefined { + const re = /^(.*?):(\d+):/ + + for (const str of backtrace) { + const match = str.match(re) + if (match !== null) { + const [_, path, lineStr] = match + if (path.startsWith('./')) { + const line = parseInt(lineStr) + return {path, line} + } + } + } + return undefined + } +} diff --git a/src/parsers/rspec-json/rspec-json-types.ts b/src/parsers/rspec-json/rspec-json-types.ts new file mode 100644 index 0000000..495af89 --- /dev/null +++ b/src/parsers/rspec-json/rspec-json-types.ts @@ -0,0 +1,34 @@ +export interface RspecJson { + version: number + examples: RspecExample[] + summary: RspecSummary + summary_line: string +} + +export interface RspecExample { + id: string + description: string + full_description: string + status: TestStatus + file_path: string + line_number: number + run_time: number + pending_message: string | null + exception?: RspecException +} + +type TestStatus = 'passed' | 'failed' | 'pending'; + +export interface RspecException { + class: string + message: string + backtrace: string[] +} + +export interface RspecSummary { + duration: number + example_count: number + failure_count: number + pending_count: number + errors_outside_of_examples_count: number +}