From 60767e951ad5e7507598150af53864bb24e748f0 Mon Sep 17 00:00:00 2001 From: Dmitry Baev Date: Fri, 9 Aug 2024 19:39:16 +0100 Subject: [PATCH] feat(allure-codeceptjs): rework codeceptjs integration (via #1107) --- .pnp.cjs | 50 +++- packages/allure-codeceptjs/package.json | 3 +- packages/allure-codeceptjs/src/helpers.ts | 28 -- packages/allure-codeceptjs/src/index.ts | 20 +- packages/allure-codeceptjs/src/model.ts | 4 - packages/allure-codeceptjs/src/reporter.ts | 259 ++++------------- packages/allure-codeceptjs/src/runtime.ts | 17 -- .../test/samples/codecept.conf.js | 1 + .../allure-codeceptjs/test/samples/helper.js | 8 + .../test/samples/hooksHelper.js | 29 -- .../allure-codeceptjs/test/spec/bdd.test.ts | 37 +++ .../allure-codeceptjs/test/spec/hooks.test.ts | 84 +++++- .../test/spec/retries.test.ts | 17 +- .../test/spec/runtime/legacy/steps.test.ts | 38 --- .../spec/runtime/modern/helperHooks.test.ts | 261 ++++++++++++++++++ .../test/spec/runtime/modern/steps.test.ts | 38 --- .../test/spec/screenshotOnFail.test.ts | 109 ++++++++ .../test/spec/simple.test.ts | 24 +- .../test/spec/skipped.test.ts | 39 ++- .../allure-codeceptjs/test/spec/steps.test.ts | 218 +++++++++++++++ .../allure-codeceptjs/test/spec/tags.test.ts | 104 +++---- .../test/spec/testplan.test.ts | 66 +++++ .../allure-codeceptjs/test/spec/tryto.test.ts | 92 ++++++ packages/allure-codeceptjs/test/utils.ts | 3 + packages/allure-js-commons/src/sdk/index.ts | 1 + packages/allure-js-commons/src/sdk/utils.ts | 4 + .../allure-js-commons/test/sdk/utils.spec.ts | 22 ++ .../allure-mocha/src/AllureMochaReporter.ts | 28 +- packages/allure-mocha/src/utils.ts | 11 +- yarn.lock | 3 +- 30 files changed, 1142 insertions(+), 476 deletions(-) delete mode 100644 packages/allure-codeceptjs/src/helpers.ts delete mode 100644 packages/allure-codeceptjs/src/runtime.ts delete mode 100644 packages/allure-codeceptjs/test/samples/hooksHelper.js create mode 100644 packages/allure-codeceptjs/test/spec/bdd.test.ts create mode 100644 packages/allure-codeceptjs/test/spec/runtime/modern/helperHooks.test.ts create mode 100644 packages/allure-codeceptjs/test/spec/screenshotOnFail.test.ts create mode 100644 packages/allure-codeceptjs/test/spec/steps.test.ts create mode 100644 packages/allure-codeceptjs/test/spec/testplan.test.ts create mode 100644 packages/allure-codeceptjs/test/spec/tryto.test.ts diff --git a/.pnp.cjs b/.pnp.cjs index 1a9a0f7bc..92942e95a 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -65,7 +65,7 @@ const RAW_RUNTIME_STATE = ["allure-jest", ["workspace:packages/allure-jest"]],\ ["allure-js", ["workspace:."]],\ ["allure-js-commons", ["virtual:3b04c8c38dde7165df844f9cd74e94cc47d78164564600cf56ba5f8c6011d25ef0d3afc13d610f3da970f6807c830914053ff96a43c86ba17f3ec650dfd687d3#workspace:packages/allure-js-commons", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-js-commons", "workspace:packages/allure-js-commons"]],\ - ["allure-mocha", ["workspace:packages/allure-mocha"]],\ + ["allure-mocha", ["virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-mocha", "workspace:packages/allure-mocha"]],\ ["allure-playwright", ["workspace:packages/allure-playwright"]],\ ["allure-vitest", ["virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-vitest", "workspace:packages/allure-vitest"]],\ ["newman-reporter-allure", ["workspace:packages/newman-reporter-allure"]]\ @@ -6070,6 +6070,7 @@ const RAW_RUNTIME_STATE = ["@typescript-eslint/parser", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:8.0.0"],\ ["allure-commandline", "npm:2.29.0"],\ ["allure-js-commons", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-js-commons"],\ + ["allure-mocha", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-mocha"],\ ["allure-vitest", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-vitest"],\ ["babel-plugin-add-module-exports", "npm:1.0.4"],\ ["codeceptjs", "npm:3.5.14"],\ @@ -6195,7 +6196,7 @@ const RAW_RUNTIME_STATE = ["allure-js-commons", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-js-commons"],\ ["allure-vitest", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-vitest"],\ ["babel-plugin-add-module-exports", "npm:1.0.4"],\ - ["babel-plugin-transform-import-meta", "virtual:e64d7f26127b0c38f2abe71db23f0a141b0538db2f417ca2c220511e19f5e4f807ad3daa66e38a4a857f9c6bfd539c632538c990ab69f65c09d763e405a3f457#npm:2.2.1"],\ + ["babel-plugin-transform-import-meta", "virtual:8777974d67ca967787ddf661a32d550d0663f57378df24bfab492231e53787c89acb197ff79f0e441260027390ebee7bd28e363b51b41022d36965a24dd5a3ca#npm:2.2.1"],\ ["eslint", "npm:8.57.0"],\ ["eslint-config-prettier", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:9.1.0"],\ ["eslint-plugin-import", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:2.29.1"],\ @@ -6394,6 +6395,43 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["allure-mocha", [\ + ["virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-mocha", {\ + "packageLocation": "./.yarn/__virtual__/allure-mocha-virtual-8777974d67/1/packages/allure-mocha/",\ + "packageDependencies": [\ + ["allure-mocha", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-mocha"],\ + ["@babel/cli", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#npm:7.24.6"],\ + ["@babel/core", "npm:7.24.6"],\ + ["@babel/preset-env", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#npm:7.24.6"],\ + ["@babel/preset-typescript", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#npm:7.24.6"],\ + ["@stylistic/eslint-plugin", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:2.6.1"],\ + ["@types/babel__core", "npm:7.20.5"],\ + ["@types/babel__preset-env", "npm:7.9.6"],\ + ["@types/eslint", "npm:8.56.11"],\ + ["@types/mocha", "npm:10.0.6"],\ + ["@types/node", "npm:20.14.9"],\ + ["@typescript-eslint/eslint-plugin", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:8.0.0"],\ + ["@typescript-eslint/parser", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:8.0.0"],\ + ["allure-commandline", "npm:2.29.0"],\ + ["allure-js-commons", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-js-commons"],\ + ["allure-vitest", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-vitest"],\ + ["babel-plugin-add-module-exports", "npm:1.0.4"],\ + ["babel-plugin-transform-import-meta", "virtual:8777974d67ca967787ddf661a32d550d0663f57378df24bfab492231e53787c89acb197ff79f0e441260027390ebee7bd28e363b51b41022d36965a24dd5a3ca#npm:2.2.1"],\ + ["eslint", "npm:8.57.0"],\ + ["eslint-config-prettier", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:9.1.0"],\ + ["eslint-plugin-import", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:2.29.1"],\ + ["eslint-plugin-jsdoc", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:50.0.0"],\ + ["eslint-plugin-n", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:17.10.1"],\ + ["eslint-plugin-no-null", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:1.0.2"],\ + ["eslint-plugin-prefer-arrow", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:1.2.3"],\ + ["glob", "npm:11.0.0"],\ + ["mocha", "npm:10.5.1"],\ + ["npm-run-all2", "npm:6.1.2"],\ + ["rimraf", "npm:6.0.0"],\ + ["typescript", "patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7"],\ + ["vitest", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#npm:1.6.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ ["workspace:packages/allure-mocha", {\ "packageLocation": "./packages/allure-mocha/",\ "packageDependencies": [\ @@ -6414,7 +6452,7 @@ const RAW_RUNTIME_STATE = ["allure-js-commons", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-js-commons"],\ ["allure-vitest", "virtual:4dfda025008308960858af02ce2de23e6fbf02744b9548c2fa0efde5067623eaf08fec37555d32d6a86a61adf128d2c2890eef9d442aef417f3e2d1f5492c52d#workspace:packages/allure-vitest"],\ ["babel-plugin-add-module-exports", "npm:1.0.4"],\ - ["babel-plugin-transform-import-meta", "virtual:e64d7f26127b0c38f2abe71db23f0a141b0538db2f417ca2c220511e19f5e4f807ad3daa66e38a4a857f9c6bfd539c632538c990ab69f65c09d763e405a3f457#npm:2.2.1"],\ + ["babel-plugin-transform-import-meta", "virtual:8777974d67ca967787ddf661a32d550d0663f57378df24bfab492231e53787c89acb197ff79f0e441260027390ebee7bd28e363b51b41022d36965a24dd5a3ca#npm:2.2.1"],\ ["eslint", "npm:8.57.0"],\ ["eslint-config-prettier", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:9.1.0"],\ ["eslint-plugin-import", "virtual:a59b12f7fe7bf3b80fc61d73eaaa33af60483f6ce31789d384fbe8ef169791f667d2559ec5f2fbae1a273a658ce021f1f5f1ea0718c56f81b30ad4e95a5668dd#npm:2.29.1"],\ @@ -7110,10 +7148,10 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "SOFT"\ }],\ - ["virtual:e64d7f26127b0c38f2abe71db23f0a141b0538db2f417ca2c220511e19f5e4f807ad3daa66e38a4a857f9c6bfd539c632538c990ab69f65c09d763e405a3f457#npm:2.2.1", {\ - "packageLocation": "./.yarn/__virtual__/babel-plugin-transform-import-meta-virtual-fd84808a15/0/cache/babel-plugin-transform-import-meta-npm-2.2.1-0927fef047-d6db38b379.zip/node_modules/babel-plugin-transform-import-meta/",\ + ["virtual:8777974d67ca967787ddf661a32d550d0663f57378df24bfab492231e53787c89acb197ff79f0e441260027390ebee7bd28e363b51b41022d36965a24dd5a3ca#npm:2.2.1", {\ + "packageLocation": "./.yarn/__virtual__/babel-plugin-transform-import-meta-virtual-c9f9e8dfd1/0/cache/babel-plugin-transform-import-meta-npm-2.2.1-0927fef047-d6db38b379.zip/node_modules/babel-plugin-transform-import-meta/",\ "packageDependencies": [\ - ["babel-plugin-transform-import-meta", "virtual:e64d7f26127b0c38f2abe71db23f0a141b0538db2f417ca2c220511e19f5e4f807ad3daa66e38a4a857f9c6bfd539c632538c990ab69f65c09d763e405a3f457#npm:2.2.1"],\ + ["babel-plugin-transform-import-meta", "virtual:8777974d67ca967787ddf661a32d550d0663f57378df24bfab492231e53787c89acb197ff79f0e441260027390ebee7bd28e363b51b41022d36965a24dd5a3ca#npm:2.2.1"],\ ["@babel/core", "npm:7.24.6"],\ ["@babel/template", "npm:7.24.7"],\ ["@types/babel__core", "npm:7.20.5"],\ diff --git a/packages/allure-codeceptjs/package.json b/packages/allure-codeceptjs/package.json index 08bd42d94..7aed2d008 100644 --- a/packages/allure-codeceptjs/package.json +++ b/packages/allure-codeceptjs/package.json @@ -40,7 +40,8 @@ "test": "vitest run" }, "dependencies": { - "allure-js-commons": "workspace:*" + "allure-js-commons": "workspace:*", + "allure-mocha": "workspace:*" }, "devDependencies": { "@babel/cli": "^7.24.6", diff --git a/packages/allure-codeceptjs/src/helpers.ts b/packages/allure-codeceptjs/src/helpers.ts deleted file mode 100644 index 81932b50a..000000000 --- a/packages/allure-codeceptjs/src/helpers.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Label } from "allure-js-commons"; -import { LabelName } from "allure-js-commons"; -import type { CodeceptTest } from "./model.js"; - -const allureIdRegexp = /@?allure.id[:=](?[^\s]+)/; -const allureLabelRegexp = /@?allure.label.(?[^\s]+?)[:=](?[^\s]+)/; - -export const extractMeta = (test: CodeceptTest & { tags: string[] }) => { - const labels: Label[] = test.tags.map((tag) => { - const idMatch = tag.match(allureIdRegexp); - const idValue = idMatch?.groups?.id; - - if (idValue) { - return { name: LabelName.ALLURE_ID, value: idValue }; - } - - const labelMatch = tag.match(allureLabelRegexp); - const { name, value } = labelMatch?.groups || {}; - - if (name && value) { - return { name, value }; - } - - return { name: LabelName.TAG, value: tag.startsWith("@") ? tag.substring(1) : tag }; - }); - - return { labels }; -}; diff --git a/packages/allure-codeceptjs/src/index.ts b/packages/allure-codeceptjs/src/index.ts index 5ba67e8b3..1d8ac1bb2 100644 --- a/packages/allure-codeceptjs/src/index.ts +++ b/packages/allure-codeceptjs/src/index.ts @@ -1,17 +1,21 @@ +import { container } from "codeceptjs"; +import { attachment } from "allure-js-commons"; import type { ReporterConfig } from "allure-js-commons/sdk/reporter"; -import { setGlobalTestRuntime } from "allure-js-commons/sdk/runtime"; import { allureCodeceptJsLegacyApi } from "./legacy.js"; import { AllureCodeceptJsReporter } from "./reporter.js"; -import { AllureCodeceptJsTestRuntime } from "./runtime.js"; const allurePlugin = (config: ReporterConfig) => { - const reporter = new AllureCodeceptJsReporter(config); - const testRuntime = new AllureCodeceptJsTestRuntime(reporter); + const mocha = container.mocha(); + mocha.reporter(AllureCodeceptJsReporter.prototype.constructor, { ...config }); - // @ts-ignore - setGlobalTestRuntime(testRuntime); - - return allureCodeceptJsLegacyApi; + return { + ...allureCodeceptJsLegacyApi, + // this method is used by various bundled codeceptjs plugins, e.g. by screenshotOnFail + addAttachment: (name: string, content: Buffer | string, contentType: string) => { + // wrap it in attachmentStep. Since we use Mocha, Runtime API is sync, so no awaits is fine + attachment(name, content, contentType); + }, + }; }; export default allurePlugin; diff --git a/packages/allure-codeceptjs/src/model.ts b/packages/allure-codeceptjs/src/model.ts index a346f9488..f83d74686 100644 --- a/packages/allure-codeceptjs/src/model.ts +++ b/packages/allure-codeceptjs/src/model.ts @@ -21,7 +21,3 @@ export interface CodeceptStep { metaStep: CodeceptStep; toString: () => string; } - -export type CodeceptHook = Mocha.Hook; - -export type CodeceptTest = Mocha.Runnable; diff --git a/packages/allure-codeceptjs/src/reporter.ts b/packages/allure-codeceptjs/src/reporter.ts index 474bca1c4..20c83c6af 100644 --- a/packages/allure-codeceptjs/src/reporter.ts +++ b/packages/allure-codeceptjs/src/reporter.ts @@ -1,215 +1,67 @@ -import { event } from "codeceptjs"; -import path from "node:path"; +import { event, recorder } from "codeceptjs"; +import type * as Mocha from "mocha"; +import { env } from "node:process"; import { LabelName, Stage, Status, type StepResult } from "allure-js-commons"; -import { type RuntimeMessage, extractMetadataFromString, getMessageAndTraceFromError } from "allure-js-commons/sdk"; -import { ReporterRuntime, createDefaultWriter, getEnvironmentLabels, md5 } from "allure-js-commons/sdk/reporter"; -import type { ReporterConfig } from "allure-js-commons/sdk/reporter"; -import { extractMeta } from "./helpers.js"; -import type { CodeceptError, CodeceptHook, CodeceptStep, CodeceptTest } from "./model.js"; +import { getMessageAndTraceFromError, getStatusFromError, isMetadataTag } from "allure-js-commons/sdk"; +import AllureMochaReporter from "allure-mocha"; +import type { CodeceptError, CodeceptStep } from "./model.js"; -export class AllureCodeceptJsReporter { - allureRuntime: ReporterRuntime; - currentTestUuid?: string; - currentFixtureUuid?: string; - scopeUuids: string[] = []; - currentTest: CodeceptTest | null = null; - config!: ReporterConfig; - - constructor(config: ReporterConfig = {}) { +export class AllureCodeceptJsReporter extends AllureMochaReporter { + constructor(runner: Mocha.Runner, opts: Mocha.MochaOptions, isInWorker: boolean) { + super(runner, opts, isInWorker); this.registerEvents(); - this.config = config; - this.allureRuntime = new ReporterRuntime({ - ...config, - writer: createDefaultWriter(config), - }); - } - - private closeCurrentAllureTest(test: CodeceptTest) { - if (!this.currentTestUuid) { - return; - } - - this.allureRuntime.updateTest(this.currentTestUuid, (result) => { - result.stage = Stage.FINISHED; - - // @ts-ignore - if (test._retries <= 0) { - return; - } - - // @ts-ignore - result.parameters.push({ name: "Repetition", value: `${test.retryNum + 1}` }); - }); - - this.allureRuntime.stopTest(this.currentTestUuid); - this.allureRuntime.writeTest(this.currentTestUuid); - this.currentTestUuid = undefined; - } - - private startAllureTest(test: CodeceptTest) { - const relativeFile = path.relative(codecept_dir, test.file!).split(path.sep).join("/"); - const fullName = `${relativeFile}#${test.title}`; - const titleMetadata = extractMetadataFromString(test.title); - // @ts-ignore - const { labels } = extractMeta(test); - - this.currentTestUuid = this.allureRuntime.startTest( - { - name: titleMetadata.cleanTitle, - fullName, - testCaseId: md5(fullName), - }, - this.scopeUuids, - ); - - this.allureRuntime.updateTest(this.currentTestUuid, (result) => { - result.labels.push(...labels); - result.labels.push(...titleMetadata.labels); - result.labels.push({ name: LabelName.LANGUAGE, value: "javascript" }); - result.labels.push({ name: LabelName.FRAMEWORK, value: "codeceptjs" }); - result.labels.push(...getEnvironmentLabels()); - - if (test?.parent?.title) { - result.labels.push({ - name: LabelName.SUITE, - value: test.parent.title, - }); - } - }); } registerEvents() { - // Hooks - event.dispatcher.on(event.hook.started, this.hookStarted.bind(this)); - event.dispatcher.on(event.hook.passed, this.hookPassed.bind(this)); - // Suite - event.dispatcher.on(event.suite.before, this.suiteBefore.bind(this)); - event.dispatcher.on(event.suite.after, this.suiteAfter.bind(this)); // Test - event.dispatcher.on(event.test.started, this.testStarted.bind(this)); - event.dispatcher.on(event.test.skipped, this.testSkipped.bind(this)); - event.dispatcher.on(event.test.passed, this.testPassed.bind(this)); - event.dispatcher.on(event.test.failed, this.testFailed.bind(this)); + event.dispatcher.on(event.test.before, this.testStarted.bind(this)); // Step event.dispatcher.on(event.step.started, this.stepStarted.bind(this)); event.dispatcher.on(event.step.passed, this.stepPassed.bind(this)); event.dispatcher.on(event.step.failed, this.stepFailed.bind(this)); event.dispatcher.on(event.step.comment, this.stepComment.bind(this)); - // run - event.dispatcher.on(event.all.after, this.afterAll.bind(this)); - } - - suiteBefore() { - const scopeUuid = this.allureRuntime.startScope(); - this.scopeUuids.push(scopeUuid); - } - - suiteAfter() { - const suiteUuid = this.scopeUuids.pop(); - if (suiteUuid) { - this.allureRuntime.writeScope(suiteUuid); - } - } - - hookStarted(hook: CodeceptHook) { - const currentRunnable = hook?.ctx?.test; - const hookType = currentRunnable!.title.match(/^"(?.+)" hook/)!.groups!.hookType; - const fixtureType = /before/.test(hookType) ? "before" : "after"; - - const scopeUuid = this.scopeUuids.length > 0 ? this.scopeUuids[this.scopeUuids.length - 1] : undefined; - if (!scopeUuid) { - return; - } - - this.currentFixtureUuid = this.allureRuntime.startFixture(scopeUuid, fixtureType, { - name: currentRunnable!.title, - stage: Stage.RUNNING, - start: Date.now(), - }); - } - - hookPassed() { - if (!this.currentFixtureUuid) { - return; - } - this.allureRuntime.updateFixture(this.currentFixtureUuid, (result) => { - result.status = Status.PASSED; - result.stage = Stage.FINISHED; - }); - - this.allureRuntime.stopFixture(this.currentFixtureUuid); - } - - testStarted(test: CodeceptTest & { tags: string[] }) { - // test has been already started - if (this.currentTestUuid) { - return; - } - - this.startAllureTest(test); - } - - testFailed(test: CodeceptTest, err: CodeceptError) { - if (!this.currentTestUuid) { - return; - } - - this.allureRuntime.updateTest(this.currentTestUuid, (result) => { - result.status = Status.FAILED; - // @ts-ignore - result.statusDetails = getMessageAndTraceFromError(err); - }); - this.closeCurrentAllureTest(test); } - testPassed(test: CodeceptTest) { - if (!this.currentTestUuid) { + testStarted(test: { tags?: string[] }) { + if (!this.currentTest) { return; } - this.allureRuntime.updateTest(this.currentTestUuid, (result) => { - result.status = Status.PASSED; - }); - this.closeCurrentAllureTest(test); - } + const tags = test.tags || []; + const extraTagLabels = tags + .filter((tag) => tag && !isMetadataTag(tag)) + .map((tag) => (tag.startsWith("@") ? tag.substring(1) : tag)) + .map((tag) => tag.trim()) + .filter((tag) => tag.length > 0) + .map((tag) => ({ name: LabelName.TAG, value: tag })); - testSkipped( - test: CodeceptTest & { - opts: { - skipInfo: { - message: string; - isFastSkipped: boolean; - }; - }; - }, - ) { - if (!this.currentTestUuid) { - return; - } - - this.allureRuntime.updateTest(this.currentTestUuid, (result) => { - result.status = Status.SKIPPED; - - if (test.opts.skipInfo) { - result.statusDetails = { message: test.opts.skipInfo.message }; - } + this.runtime.updateTest(this.currentTest, (tr) => { + tr.labels.push(...extraTagLabels); }); - this.closeCurrentAllureTest(test); } stepStarted(step: CodeceptStep) { - if (!this.currentTestUuid) { + const root = this.currentHook ?? this.currentTest; + if (!root) { return; } - this.allureRuntime.startStep(this.currentTestUuid, undefined, { + this.runtime.startStep(root, undefined, { name: `${step.actor} ${step.name}`, + parameters: step.args?.map((arg, index) => ({ name: `arg${index}`, value: `${arg}` })), }); } - stepFailed() { + // according to the docs, codeceptjs supposed to report the error, + // but actually it's never reported + stepFailed(_: CodeceptJS.Step, error?: CodeceptError) { this.stopCurrentStep((result) => { - result.status = Status.FAILED; result.stage = Stage.FINISHED; + if (error) { + result.status = getStatusFromError({ message: error.message } as Error); + result.statusDetails = getMessageAndTraceFromError(error); + } else { + result.status = env.TRY_TO === "true" ? Status.BROKEN : Status.FAILED; + } }); } @@ -228,36 +80,29 @@ export class AllureCodeceptJsReporter { } stopCurrentStep(updateFunc: (result: StepResult) => void) { - const currentStep = this.currentTestUuid ? this.allureRuntime.currentStep(this.currentTestUuid) : undefined; + const root = this.currentHook ?? this.currentTest; + const currentStep = root ? this.runtime.currentStep(root) : undefined; if (!currentStep) { return; } - this.allureRuntime.updateStep(currentStep, updateFunc); - this.allureRuntime.stopStep(currentStep); - } + const promise = recorder.promise(); + // @ts-ignore + if (promise) { + promise.catch((err) => { + if (err instanceof Error) { + this.runtime.updateStep(currentStep, (step) => { + step.status = getStatusFromError(err); + step.statusDetails = { ...step.statusDetails, ...getMessageAndTraceFromError(err) }; + }); + } + return Promise.reject(err); + }); + } - afterAll() { - this.allureRuntime.writeEnvironmentInfo(); - this.allureRuntime.writeCategoriesDefinitions(); + this.runtime.updateStep(currentStep, updateFunc); + this.runtime.stopStep(currentStep); } - // TODO: not implemented in the new version at all - // addScreenDiff(name: string, expectedImg: string, actualImg: string, diffImg: string) { - // const screenDiff = { - // name, - // expected: `data:image/png;base64,${expectedImg}`, - // actual: `data:image/png;base64,${actualImg}`, - // diff: `data:image/png;base64,${diffImg}`, - // }; - // this.addAttachment(name, JSON.stringify(screenDiff), "application/vnd.allure.image.diff"); - // } - - handleRuntimeMessage(message: RuntimeMessage) { - const rootUuid = this.currentFixtureUuid ?? this.currentTestUuid; - if (!rootUuid) { - return; - } - this.allureRuntime.applyRuntimeMessages(rootUuid, [message]); - } + protected getFrameworkName = () => "codeceptjs"; } diff --git a/packages/allure-codeceptjs/src/runtime.ts b/packages/allure-codeceptjs/src/runtime.ts deleted file mode 100644 index 8d75f3765..000000000 --- a/packages/allure-codeceptjs/src/runtime.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { RuntimeMessage } from "allure-js-commons/sdk"; -import { MessageTestRuntime } from "allure-js-commons/sdk/runtime"; -import type { AllureCodeceptJsReporter } from "./reporter.js"; - -export class AllureCodeceptJsTestRuntime extends MessageTestRuntime { - private readonly reporter: AllureCodeceptJsReporter; - - constructor(reporter: AllureCodeceptJsReporter) { - super(); - this.reporter = reporter; - } - - async sendMessage(message: RuntimeMessage) { - this.reporter.handleRuntimeMessage(message); - await Promise.resolve(); - } -} diff --git a/packages/allure-codeceptjs/test/samples/codecept.conf.js b/packages/allure-codeceptjs/test/samples/codecept.conf.js index 1bcaccb48..12180c146 100644 --- a/packages/allure-codeceptjs/test/samples/codecept.conf.js +++ b/packages/allure-codeceptjs/test/samples/codecept.conf.js @@ -28,5 +28,6 @@ exports.config = { CustomHelper: { require: "./helper.js", }, + Expect: {}, }, }; diff --git a/packages/allure-codeceptjs/test/samples/helper.js b/packages/allure-codeceptjs/test/samples/helper.js index 2fd5480be..625c78656 100644 --- a/packages/allure-codeceptjs/test/samples/helper.js +++ b/packages/allure-codeceptjs/test/samples/helper.js @@ -5,6 +5,14 @@ class MyHelper extends Helper { await Promise.resolve(); } + async next() { + await Promise.resolve(); + } + + async parameters(page, element) { + await Promise.resolve(); + } + async fail() { await Promise.reject(new Error("an error")); } diff --git a/packages/allure-codeceptjs/test/samples/hooksHelper.js b/packages/allure-codeceptjs/test/samples/hooksHelper.js deleted file mode 100644 index 446be37dd..000000000 --- a/packages/allure-codeceptjs/test/samples/hooksHelper.js +++ /dev/null @@ -1,29 +0,0 @@ -const Helper = require("@codeceptjs/helper"); - -class MyHooksHelper extends Helper { - _init() {} - - _finishTest() {} - - _before() {} - - _after() {} - - _beforeStep() {} - - _afterStep() {} - - _beforeSuite() {} - - _afterSuite() {} - - _passed() {} - - _failed() {} - - async pass() { - await Promise.resolve(); - } -} - -module.exports = MyHooksHelper; diff --git a/packages/allure-codeceptjs/test/spec/bdd.test.ts b/packages/allure-codeceptjs/test/spec/bdd.test.ts new file mode 100644 index 000000000..dfdd92c41 --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/bdd.test.ts @@ -0,0 +1,37 @@ +import { expect, it } from "vitest"; +import { LabelName } from "allure-js-commons"; +import { runCodeceptJsInlineTest } from "../utils.js"; + +it("should add correct bdd labels", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "login.test.js": ` + Feature("bdd") + Scenario('bdd', () => { + }).tag('@allure.label.epic:WebInterface') + .tag('@allure.label.feature:EssentialFeatures') + .tag('@allure.label.story:Authentication'); + `, + }); + + expect(tests).toHaveLength(1); + expect(tests[0].labels).toEqual( + expect.arrayContaining([ + { + name: LabelName.EPIC, + value: "WebInterface", + }, + { + name: LabelName.FEATURE, + value: "EssentialFeatures", + }, + { + name: LabelName.STORY, + value: "Authentication", + }, + ]), + ); + + expect(tests[0].labels.filter((l) => l.name === LabelName.EPIC)).toHaveLength(1); + expect(tests[0].labels.filter((l) => l.name === LabelName.FEATURE)).toHaveLength(1); + expect(tests[0].labels.filter((l) => l.name === LabelName.STORY)).toHaveLength(1); +}); diff --git a/packages/allure-codeceptjs/test/spec/hooks.test.ts b/packages/allure-codeceptjs/test/spec/hooks.test.ts index 3f826e40e..9864f393d 100644 --- a/packages/allure-codeceptjs/test/spec/hooks.test.ts +++ b/packages/allure-codeceptjs/test/spec/hooks.test.ts @@ -13,7 +13,6 @@ it("handles hooks", async () => { Before(() => {}); After(() => {}); - // these hooks shouldn't be reported because codeceptjs doesn't provide any information about them BeforeSuite(() => {}); AfterSuite(() => {}); @@ -25,46 +24,115 @@ it("handles hooks", async () => { expect(tests).toHaveLength(1); expect(tests[0].steps).toHaveLength(1); - expect(groups).toHaveLength(4); expect(groups).toEqual( expect.arrayContaining([ expect.objectContaining({ - name: String.raw`"before all" hook: BeforeSuite for "sample-scenario"`, + name: String.raw`"before all" hook: codeceptjs.beforeSuite`, befores: expect.arrayContaining([ expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, + name: String.raw`"before all" hook: codeceptjs.beforeSuite`, }), ]), afters: [], }), expect.objectContaining({ - name: String.raw`"before each" hook: Before for "sample-scenario"`, + name: String.raw`"before all" hook`, befores: expect.arrayContaining([ expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, + name: String.raw`"before all" hook`, }), ]), afters: [], }), expect.objectContaining({ - name: String.raw`"after each" hook: After for "sample-scenario"`, + name: String.raw`"before all" hook: BeforeSuite`, + befores: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"before all" hook: BeforeSuite`, + }), + ]), + afters: [], + }), + expect.objectContaining({ + name: String.raw`"before each" hook: codeceptjs.before`, + befores: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"before each" hook: codeceptjs.before`, + }), + ]), + afters: [], + }), + expect.objectContaining({ + name: String.raw`"before each" hook: Before`, + befores: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"before each" hook: Before`, + }), + ]), + afters: [], + }), + expect.objectContaining({ + name: String.raw`"after each" hook: After`, + befores: [], + afters: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"after each" hook: After`, + }), + ]), + }), + expect.objectContaining({ + name: String.raw`"after each" hook: finalize codeceptjs`, + befores: [], + afters: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"after each" hook: finalize codeceptjs`, + }), + ]), + }), + expect.objectContaining({ + name: String.raw`"after all" hook`, + befores: [], + afters: expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: String.raw`"after all" hook`, + }), + ]), + }), + expect.objectContaining({ + name: String.raw`"after all" hook: AfterSuite`, befores: [], afters: expect.arrayContaining([ expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, + name: String.raw`"after all" hook: AfterSuite`, }), ]), }), expect.objectContaining({ - name: String.raw`"after all" hook: AfterSuite for "sample-scenario"`, + name: String.raw`"after all" hook: codeceptjs.afterSuite`, befores: [], afters: expect.arrayContaining([ expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, + name: String.raw`"after all" hook: codeceptjs.afterSuite`, }), ]), }), @@ -100,7 +168,7 @@ it("should support steps in before & after hooks", async () => { expect(groups).toEqual( expect.arrayContaining([ expect.objectContaining({ - name: String.raw`"before each" hook: Before for "sample-scenario"`, + name: String.raw`"before each" hook: Before`, befores: expect.arrayContaining([ expect.objectContaining({ status: Status.PASSED, @@ -120,7 +188,7 @@ it("should support steps in before & after hooks", async () => { afters: [], }), expect.objectContaining({ - name: String.raw`"after each" hook: After for "sample-scenario"`, + name: String.raw`"after each" hook: After`, befores: [], afters: expect.arrayContaining([ expect.objectContaining({ diff --git a/packages/allure-codeceptjs/test/spec/retries.test.ts b/packages/allure-codeceptjs/test/spec/retries.test.ts index f0da7bb0f..0190f6a60 100644 --- a/packages/allure-codeceptjs/test/spec/retries.test.ts +++ b/packages/allure-codeceptjs/test/spec/retries.test.ts @@ -16,19 +16,15 @@ it("handles retries", async () => { expect.arrayContaining([ expect.objectContaining({ name: "login-scenario1", - parameters: expect.arrayContaining([ - { - name: "Repetition", - value: "1", - }, - ]), + parameters: [], }), expect.objectContaining({ name: "login-scenario1", parameters: expect.arrayContaining([ { - name: "Repetition", - value: "2", + name: "Retry", + value: "1", + excluded: true, }, ]), }), @@ -36,8 +32,9 @@ it("handles retries", async () => { name: "login-scenario1", parameters: expect.arrayContaining([ { - name: "Repetition", - value: "3", + name: "Retry", + value: "2", + excluded: true, }, ]), }), diff --git a/packages/allure-codeceptjs/test/spec/runtime/legacy/steps.test.ts b/packages/allure-codeceptjs/test/spec/runtime/legacy/steps.test.ts index ced4adecd..73e294e1e 100644 --- a/packages/allure-codeceptjs/test/spec/runtime/legacy/steps.test.ts +++ b/packages/allure-codeceptjs/test/spec/runtime/legacy/steps.test.ts @@ -2,44 +2,6 @@ import { expect, it } from "vitest"; import { Stage, Status } from "allure-js-commons"; import { runCodeceptJsInlineTest } from "../../../utils.js"; -it("handles codeceptjs steps", async () => { - const { tests } = await runCodeceptJsInlineTest({ - "sample.test.js": ` - Feature("sample-feature-1"); - Scenario("passed-scenario1", async ({ I }) => { - await I.pass(); - }); - - Feature("sample-feature-2"); - Scenario("failed-scenario1", async ({ I }) => { - await I.fail(); - }); - `, - }); - - expect(tests).toHaveLength(2); - expect(tests[0].steps).toHaveLength(1); - expect(tests[0].steps).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: "I pass", - status: Status.PASSED, - stage: Stage.FINISHED, - }), - ]), - ); - expect(tests[1].steps).toHaveLength(1); - expect(tests[1].steps).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: "I fail", - status: Status.FAILED, - stage: Stage.FINISHED, - }), - ]), - ); -}); - it("handles lambda steps", async () => { const { tests } = await runCodeceptJsInlineTest({ "sample.test.js": ` diff --git a/packages/allure-codeceptjs/test/spec/runtime/modern/helperHooks.test.ts b/packages/allure-codeceptjs/test/spec/runtime/modern/helperHooks.test.ts new file mode 100644 index 000000000..78f5eb3e8 --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/runtime/modern/helperHooks.test.ts @@ -0,0 +1,261 @@ +import { expect, it } from "vitest"; +import { Status } from "allure-js-commons"; +import { runCodeceptJsInlineTest } from "../../../utils.js"; + +it("should support runtime API in helper _beforeStep & _afterStep hooks", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async () => {}); + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const allure = require("allure-js-commons"); + + class MyHooksHelper extends Helper { + async _beforeStep() { + await allure.logStep("_beforeStep"); + } + + async _afterStep() { + await allure.logStep("_afterStep"); + } + + async pass() { + await Promise.resolve(); + } + } + module.exports = MyHooksHelper; + `, + }); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario1", + steps: [ + expect.objectContaining({ + name: "_beforeStep", + }), + expect.objectContaining({ + name: "I pass", + steps: [ + expect.objectContaining({ + name: "_afterStep", + }), + ], + }), + ], + }), + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario2", + }), + ]), + ); +}); + +// it's doesn't reported in time, don't know why +it.skip("should support runtime API in helper _passed", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async ({ I }) => { + await I.fail(); + }); + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const allure = require("allure-js-commons"); + + class MyHooksHelper extends Helper { + async _passed() { + await allure.logStep("_passed"); + } + + async pass() { + await Promise.resolve(); + } + + async fail() { + await Promise.reject(new Error("should have failed")); + } + } + + module.exports = MyHooksHelper; + `, + }); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario1", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + expect.objectContaining({ + name: "_passed", + }), + ], + }), + expect.objectContaining({ + status: Status.BROKEN, + name: "login-scenario2", + steps: [ + expect.objectContaining({ + name: "I fail", + }), + ], + }), + ]), + ); +}); + +it("should support runtime API in helper _failed hook", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async ({ I }) => { + await I.fail(); + }); + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const allure = require("allure-js-commons"); + + class MyHooksHelper extends Helper { + async _passed() { + await allure.logStep("_passed"); + } + + async _failed() { + await allure.logStep("_failed"); + } + + async pass() { + await Promise.resolve(); + } + + async fail() { + await Promise.reject(new Error("should have failed")); + } + } + + module.exports = MyHooksHelper; + `, + }); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario1", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + ], + }), + expect.objectContaining({ + status: Status.BROKEN, + name: "login-scenario2", + steps: [ + expect.objectContaining({ + name: "I fail", + }), + expect.objectContaining({ + name: "_failed", + }), + ], + }), + ]), + ); +}); + +it("should support runtime API in helper _after hook", async () => { + const { tests, groups } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async ({ I }) => { + await I.fail(); + }); + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const allure = require("allure-js-commons"); + + class MyHooksHelper extends Helper { + async _after() { + await allure.logStep("_after"); + } + + async pass() { + await Promise.resolve(); + } + + async fail() { + await Promise.reject(new Error("should have failed")); + } + } + + module.exports = MyHooksHelper; + `, + }); + + expect(tests).toHaveLength(2); + const tr1 = tests.find((t) => t.name === "login-scenario1")!; + const tr2 = tests.find((t) => t.name === "login-scenario2")!; + + expect(groups).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: String.raw`"after each" hook: finalize codeceptjs`, + children: [tr1.uuid], + afters: [ + expect.objectContaining({ + name: String.raw`"after each" hook: finalize codeceptjs`, + steps: [ + expect.objectContaining({ + name: "_after", + status: Status.PASSED, + }), + ], + }), + ], + }), + expect.objectContaining({ + name: String.raw`"after each" hook: finalize codeceptjs`, + children: [tr2.uuid], + afters: [ + expect.objectContaining({ + name: String.raw`"after each" hook: finalize codeceptjs`, + steps: [ + expect.objectContaining({ + name: "_after", + status: Status.PASSED, + }), + ], + }), + ], + }), + ]), + ); +}); diff --git a/packages/allure-codeceptjs/test/spec/runtime/modern/steps.test.ts b/packages/allure-codeceptjs/test/spec/runtime/modern/steps.test.ts index c221c8d09..d3c91906c 100644 --- a/packages/allure-codeceptjs/test/spec/runtime/modern/steps.test.ts +++ b/packages/allure-codeceptjs/test/spec/runtime/modern/steps.test.ts @@ -2,44 +2,6 @@ import { expect, it } from "vitest"; import { Stage, Status } from "allure-js-commons"; import { runCodeceptJsInlineTest } from "../../../utils.js"; -it("handles codeceptjs steps", async () => { - const { tests } = await runCodeceptJsInlineTest({ - "sample.test.js": ` - Feature("sample-feature-1"); - Scenario("passed-scenario1", async ({ I }) => { - await I.pass(); - }); - - Feature("sample-feature-2"); - Scenario("failed-scenario1", async ({ I }) => { - await I.fail(); - }); - `, - }); - - expect(tests).toHaveLength(2); - expect(tests[0].steps).toHaveLength(1); - expect(tests[0].steps).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: "I pass", - status: Status.PASSED, - stage: Stage.FINISHED, - }), - ]), - ); - expect(tests[1].steps).toHaveLength(1); - expect(tests[1].steps).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: "I fail", - status: Status.FAILED, - stage: Stage.FINISHED, - }), - ]), - ); -}); - it("handles lambda steps", async () => { const { tests } = await runCodeceptJsInlineTest({ "sample.test.js": ` diff --git a/packages/allure-codeceptjs/test/spec/screenshotOnFail.test.ts b/packages/allure-codeceptjs/test/spec/screenshotOnFail.test.ts new file mode 100644 index 000000000..9e45857ea --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/screenshotOnFail.test.ts @@ -0,0 +1,109 @@ +import { expect, it } from "vitest"; +import { Status } from "allure-js-commons"; +import { runCodeceptJsInlineTest } from "../utils.js"; + +it("should support screenshotOnFail plugin", async () => { + const { tests, attachments } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + const { container } = require('codeceptjs') + + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async ({ I }) => { + await I.pass(); + await I.fail(); + }); + `, + "codecept.conf.js": ` + const path = require("node:path"); + const { setCommonPlugins } = require("@codeceptjs/configure"); + + setCommonPlugins(); + + exports.config = { + tests: "./**/*.test.js", + output: path.resolve(__dirname, "./output"), + plugins: { + allure: { + require: require.resolve("allure-codeceptjs"), + enabled: true, + }, + screenshotOnFail: { + enabled: true + } + }, + helpers: { + Playwright: { + require: "./helper.js", + }, + Expect: {}, + }, + }; + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const { writeFile } = require("fs/promises"); + const path = require("path"); + + class MyHooksHelper extends Helper { + + async pass() { + await Promise.resolve(); + } + + async fail() { + await Promise.reject(new Error("should have failed")); + } + + async saveScreenshot(fileName) { + const outputPath = path.join(global.output_dir, fileName); + await writeFile(outputPath, Buffer.from(JSON.stringify(fileName)), "utf-8"); + } + } + + module.exports = MyHooksHelper; + `, + }); + + const attachmentSources = Object.keys(attachments); + expect(attachmentSources).toHaveLength(1); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario1", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + ], + }), + expect.objectContaining({ + status: Status.BROKEN, + name: "login-scenario2", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + expect.objectContaining({ + name: "I fail", + }), + expect.objectContaining({ + name: "Main session - Last Seen Screenshot", + attachments: [ + expect.objectContaining({ + name: "Main session - Last Seen Screenshot", + type: "image/png", + source: attachmentSources[0], + }), + ], + }), + ], + }), + ]), + ); +}); diff --git a/packages/allure-codeceptjs/test/spec/simple.test.ts b/packages/allure-codeceptjs/test/spec/simple.test.ts index c135677e6..8cee7600b 100644 --- a/packages/allure-codeceptjs/test/spec/simple.test.ts +++ b/packages/allure-codeceptjs/test/spec/simple.test.ts @@ -22,34 +22,34 @@ it("handles simple scenarios", async () => { expect.objectContaining({ status: Status.PASSED, name: "logout-scenario1", - fullName: "logout.test.js#logout-scenario1", - testCaseId: "a9bfc003abdc102abd3fa50711659853", - historyId: "a9bfc003abdc102abd3fa50711659853:d41d8cd98f00b204e9800998ecf8427e", + fullName: "logout.test.js: logout-feature > logout-scenario1", + testCaseId: "0bf4f9a26f520d0d5c2aa270918dd2bd", + historyId: "0bf4f9a26f520d0d5c2aa270918dd2bd:d41d8cd98f00b204e9800998ecf8427e", }), expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, name: "logout-scenario2", - fullName: "logout.test.js#logout-scenario2", - testCaseId: "3e1ee873dfc32b563afd1b98ef26edf8", - historyId: "3e1ee873dfc32b563afd1b98ef26edf8:d41d8cd98f00b204e9800998ecf8427e", + fullName: "logout.test.js: logout-feature > logout-scenario2", + testCaseId: "5607c1c83d9b64861ff559d525106c71", + historyId: "5607c1c83d9b64861ff559d525106c71:d41d8cd98f00b204e9800998ecf8427e", }), expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, name: "login-scenario1", - fullName: "nested/login.test.js#login-scenario1", - testCaseId: "e28383a4b2bfbdfb8434afa7ad20542f", - historyId: "e28383a4b2bfbdfb8434afa7ad20542f:d41d8cd98f00b204e9800998ecf8427e", + fullName: "nested/login.test.js: login-feature > login-scenario1", + testCaseId: "b63d78080eb28db70a46f9ecccf81927", + historyId: "b63d78080eb28db70a46f9ecccf81927:d41d8cd98f00b204e9800998ecf8427e", }), expect.objectContaining({ status: Status.PASSED, stage: Stage.FINISHED, name: "login-scenario2", - fullName: "nested/login.test.js#login-scenario2", - testCaseId: "ece9c5b4007ade6cad00690df07c02d9", - historyId: "ece9c5b4007ade6cad00690df07c02d9:d41d8cd98f00b204e9800998ecf8427e", + fullName: "nested/login.test.js: login-feature > login-scenario2", + testCaseId: "32c25d09db299433cdc0cef7a9254b40", + historyId: "32c25d09db299433cdc0cef7a9254b40:d41d8cd98f00b204e9800998ecf8427e", }), ]), ); diff --git a/packages/allure-codeceptjs/test/spec/skipped.test.ts b/packages/allure-codeceptjs/test/spec/skipped.test.ts index 05b0f7433..7455edec0 100644 --- a/packages/allure-codeceptjs/test/spec/skipped.test.ts +++ b/packages/allure-codeceptjs/test/spec/skipped.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; +import { Status } from "allure-js-commons"; import { runCodeceptJsInlineTest } from "../utils.js"; it("doesn't report skipped features and steps", async () => { @@ -21,5 +22,41 @@ it("doesn't report skipped features and steps", async () => { `, }); - expect(tests).toHaveLength(0); + expect(tests).toHaveLength(4); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + fullName: "skipped_feature1.test.js: logout-feature1 > logout-scenario1", + name: "logout-scenario1", + status: Status.SKIPPED, + statusDetails: expect.objectContaining({ + message: "Test skipped", + }), + }), + expect.objectContaining({ + fullName: "skipped_feature2.test.js: logout-feature2 > logout-scenario1", + name: "logout-scenario1", + status: Status.SKIPPED, + statusDetails: expect.objectContaining({ + message: "Test skipped", + }), + }), + expect.objectContaining({ + fullName: "skipped_scenario1.test.js: logout-feature3 > logout-scenario1", + name: "logout-scenario1", + status: Status.SKIPPED, + statusDetails: expect.objectContaining({ + message: "Test skipped", + }), + }), + expect.objectContaining({ + fullName: "skipped_scenario2.test.js: logout-feature4 > logout-scenario1", + name: "logout-scenario1", + status: Status.SKIPPED, + statusDetails: expect.objectContaining({ + message: "Test skipped", + }), + }), + ]), + ); }); diff --git a/packages/allure-codeceptjs/test/spec/steps.test.ts b/packages/allure-codeceptjs/test/spec/steps.test.ts new file mode 100644 index 000000000..d51c587fa --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/steps.test.ts @@ -0,0 +1,218 @@ +import { expect, it } from "vitest"; +import { Status } from "allure-js-commons"; +import { runCodeceptJsInlineTest } from "../utils.js"; + +it("should log passed steps", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "steps.test.js": ` + Feature("a feature"); + Scenario("scenario 1", async ({ I }) => { + await I.pass(); + await I.pass(); + }); + Scenario("scenario 2", async ({ I }) => { + await I.pass(); + await I.next(); + }); + `, + }); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "scenario 1", + steps: [ + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + ], + }), + expect.objectContaining({ + status: Status.PASSED, + name: "scenario 2", + steps: [ + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + expect.objectContaining({ + name: "I next", + status: Status.PASSED, + }), + ], + }), + ]), + ); +}); + +it("should log failed steps", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "steps.test.js": ` + Feature("a feature"); + Scenario("scenario 1", async ({ I }) => { + await I.pass(); + await I.fail(); + }); + `, + }); + + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.BROKEN, + name: "scenario 1", + statusDetails: expect.objectContaining({ + message: "an error", + }), + steps: [ + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + expect.objectContaining({ + name: "I fail", + status: Status.BROKEN, + }), + ], + }), + ]), + ); +}); + +it("should log steps with parameters", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "steps.test.js": ` + Feature("a feature"); + Scenario("scenario 1", async ({ I }) => { + await I.pass(); + await I.parameters("https://example.com/", "#header"); + }); + `, + }); + + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "scenario 1", + steps: [ + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + expect.objectContaining({ + name: "I parameters", + status: Status.PASSED, + parameters: expect.arrayContaining([ + expect.objectContaining({ + name: "arg0", + value: "https://example.com/", + }), + expect.objectContaining({ + name: "arg1", + value: "#header", + }), + ]), + }), + ], + }), + ]), + ); +}); + +it("should log comments", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "steps.test.js": ` + Feature("a feature"); + Scenario("scenario 1", async ({ I }) => { + await I.pass(); + await I.say("hi"); + await I.say("bye"); + }); + `, + }); + + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "scenario 1", + steps: [ + expect.objectContaining({ + name: "I pass", + status: Status.PASSED, + }), + expect.objectContaining({ + name: "I say", + status: Status.PASSED, + parameters: expect.arrayContaining([ + expect.objectContaining({ + name: "arg0", + value: "hi", + }), + ]), + }), + expect.objectContaining({ + name: "I say", + status: Status.PASSED, + parameters: expect.arrayContaining([ + expect.objectContaining({ + name: "arg0", + value: "bye", + }), + ]), + }), + ], + }), + ]), + ); +}); + +it("should log expects", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "steps.test.js": ` + Feature("a feature"); + Scenario("scenario 1", async ({ I }) => { + await I.expectEqual(1, 2); + }); + `, + }); + + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.FAILED, + name: "scenario 1", + statusDetails: expect.objectContaining({ + message: "expected 1 to equal 2", + }), + steps: [ + expect.objectContaining({ + name: "I expectEqual", + status: Status.FAILED, + statusDetails: expect.objectContaining({ + message: "expected 1 to equal 2", + }), + parameters: expect.arrayContaining([ + expect.objectContaining({ + name: "arg0", + value: "1", + }), + expect.objectContaining({ + name: "arg1", + value: "2", + }), + ]), + }), + ], + }), + ]), + ); +}); diff --git a/packages/allure-codeceptjs/test/spec/tags.test.ts b/packages/allure-codeceptjs/test/spec/tags.test.ts index 426941e8b..3d05fc2bc 100644 --- a/packages/allure-codeceptjs/test/spec/tags.test.ts +++ b/packages/allure-codeceptjs/test/spec/tags.test.ts @@ -19,54 +19,60 @@ it("supports codecept tags", async () => { }); expect(tests).toHaveLength(1); - expect(tests[0]).toEqual( - expect.objectContaining({ - labels: expect.arrayContaining([ - { - name: LabelName.TAG, - value: "slow", - }, - { - name: LabelName.TAG, - value: "important", - }, - { - name: LabelName.OWNER, - value: "eroshenkoam", - }, - { - name: LabelName.LAYER, - value: "UI", - }, - { - name: LabelName.ALLURE_ID, - value: "228", - }, - { - name: LabelName.STORY, - value: "aga", - }, - { - name: LabelName.EPIC, - value: "aga", - }, - { - name: LabelName.SEVERITY, - value: "critical", - }, - { - name: LabelName.LANGUAGE, - value: "javascript", - }, - { - name: LabelName.FRAMEWORK, - value: "codeceptjs", - }, - { - name: LabelName.SUITE, - value: "tags", - }, - ]), - }), + expect(tests[0].labels).toEqual( + expect.arrayContaining([ + { + name: LabelName.TAG, + value: "slow", + }, + { + name: LabelName.TAG, + value: "important", + }, + { + name: LabelName.OWNER, + value: "eroshenkoam", + }, + { + name: LabelName.LAYER, + value: "UI", + }, + { + name: LabelName.ALLURE_ID, + value: "228", + }, + { + name: LabelName.STORY, + value: "aga", + }, + { + name: LabelName.EPIC, + value: "aga", + }, + { + name: LabelName.SEVERITY, + value: "critical", + }, + { + name: LabelName.LANGUAGE, + value: "javascript", + }, + { + name: LabelName.FRAMEWORK, + value: "codeceptjs", + }, + { + name: LabelName.PARENT_SUITE, + value: "tags", + }, + { + name: LabelName.HOST, + value: expect.anything(), + }, + { + name: LabelName.THREAD, + value: expect.anything(), + }, + ]), ); }); diff --git a/packages/allure-codeceptjs/test/spec/testplan.test.ts b/packages/allure-codeceptjs/test/spec/testplan.test.ts new file mode 100644 index 000000000..11bf1df96 --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/testplan.test.ts @@ -0,0 +1,66 @@ +import { expect, it } from "vitest"; +import { Stage, Status } from "allure-js-commons"; +import type { TestPlanV1 } from "allure-js-commons/sdk"; +import { runCodeceptJsInlineTest } from "../utils.js"; + +it("should support test plan", async () => { + const exampleTestPlan: TestPlanV1 = { + version: "1.0", + tests: [ + { + id: 321, + selector: "invalid", + }, + { + selector: "nested/login.test.js: login-feature > login-scenario1", + }, + { + selector: "logout.test.js: logout-feature > logout-scenario1", + }, + ], + }; + + const testPlanFilename = "example-testplan.json"; + const { tests } = await runCodeceptJsInlineTest( + { + "nested/login.test.js": ` + Feature("login-feature"); + Scenario("login-scenario1", async () => {}); + Scenario("login-scenario2", async () => {}); + Scenario("login-scenario3", async () => {}).tag("@allure.id:321"); + `, + "logout.test.js": ` + Feature("logout-feature"); + Scenario("logout-scenario1", async () => {}); + Scenario("logout-scenario2", async () => {}); + `, + [testPlanFilename]: `${JSON.stringify(exampleTestPlan)}`, + }, + { + ALLURE_TESTPLAN_PATH: `./${testPlanFilename}`, + }, + ); + + expect(tests).toHaveLength(3); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "logout-scenario1", + fullName: "logout.test.js: logout-feature > logout-scenario1", + }), + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: "login-scenario1", + fullName: "nested/login.test.js: login-feature > login-scenario1", + }), + expect.objectContaining({ + status: Status.PASSED, + stage: Stage.FINISHED, + name: "login-scenario3", + fullName: "nested/login.test.js: login-feature > login-scenario3 @allure.id:321", + }), + ]), + ); +}); diff --git a/packages/allure-codeceptjs/test/spec/tryto.test.ts b/packages/allure-codeceptjs/test/spec/tryto.test.ts new file mode 100644 index 000000000..832a5029e --- /dev/null +++ b/packages/allure-codeceptjs/test/spec/tryto.test.ts @@ -0,0 +1,92 @@ +import { expect, it } from "vitest"; +import { Status } from "allure-js-commons"; +import { runCodeceptJsInlineTest } from "../utils.js"; + +it("should support tryTo plugin", async () => { + const { tests } = await runCodeceptJsInlineTest({ + "nested/login.test.js": ` + const { container } = require('codeceptjs') + + Feature("login-feature"); + Scenario("login-scenario1", async ({ I }) => { + await I.pass(); + }); + Scenario("login-scenario2", async ({ I }) => { + await I.pass(); + await tryTo(() => I.fail()); + }); + `, + "codecept.conf.js": ` + const path = require("node:path"); + const { setCommonPlugins } = require("@codeceptjs/configure"); + + setCommonPlugins(); + + exports.config = { + tests: "./**/*.test.js", + output: path.resolve(__dirname, "./output"), + plugins: { + allure: { + require: require.resolve("allure-codeceptjs"), + enabled: true, + }, + tryTo: { + enabled: true + } + }, + helpers: { + Playwright: { + require: "./helper.js", + }, + Expect: {}, + }, + }; + `, + "helper.js": ` + const Helper = require("@codeceptjs/helper"); + const { writeFile } = require("fs/promises"); + const path = require("path"); + + class MyHooksHelper extends Helper { + + async pass() { + await Promise.resolve(); + } + + async fail() { + await Promise.reject(new Error("should have failed")); + } + } + + module.exports = MyHooksHelper; + `, + }); + + expect(tests).toHaveLength(2); + expect(tests).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario1", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + ], + }), + expect.objectContaining({ + status: Status.PASSED, + name: "login-scenario2", + steps: [ + expect.objectContaining({ + name: "I pass", + }), + expect.objectContaining({ + name: "I fail", + status: Status.BROKEN, + }), + ], + }), + ]), + ); +}); diff --git a/packages/allure-codeceptjs/test/utils.ts b/packages/allure-codeceptjs/test/utils.ts index 3e391bbaa..5db0ff978 100644 --- a/packages/allure-codeceptjs/test/utils.ts +++ b/packages/allure-codeceptjs/test/utils.ts @@ -12,6 +12,9 @@ export const runCodeceptJsInlineTest = async ( env?: Record, ): Promise => { const testFiles = { + // package.json is used to find project root in case of absolute file paths are used + // eslint-disable-next-line @stylistic/quotes + "package.json": '{ "name": "dummy"}', "codecept.conf.js": await readFile(resolvePath(__dirname, "./samples/codecept.conf.js"), "utf-8"), "helper.js": await readFile(resolvePath(__dirname, "./samples/helper.js"), "utf-8"), ...files, diff --git a/packages/allure-js-commons/src/sdk/index.ts b/packages/allure-js-commons/src/sdk/index.ts index a832d7b5f..6139f8dfa 100644 --- a/packages/allure-js-commons/src/sdk/index.ts +++ b/packages/allure-js-commons/src/sdk/index.ts @@ -16,6 +16,7 @@ export type { export { getStatusFromError, getMessageAndTraceFromError, + isMetadataTag, extractMetadataFromString, isAllStepsEnded, isAnyStepFailed, diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index b289915da..a74188a41 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -54,6 +54,10 @@ export const allureIdRegexpGlobal = new RegExp(allureIdRegexp, "g"); export const allureLabelRegexp = /(?:^|\s)@?allure\.label\.(?[^:=\s]+)[:=](?[^\s]+)/; export const allureLabelRegexpGlobal = new RegExp(allureLabelRegexp, "g"); +export const isMetadataTag = (tag: string) => { + return allureIdRegexp.test(tag) || allureLabelRegexp.test(tag); +}; + export const extractMetadataFromString = ( title: string, ): { diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index b948b6c16..04961565c 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -7,6 +7,7 @@ import { extractMetadataFromString, getStatusFromError, isAnyStepFailed, + isMetadataTag, } from "../../src/sdk/utils.js"; type Executable = StepResult | TestResult | FixtureResult; @@ -244,3 +245,24 @@ describe("extractMetadataFromString", () => { }); }); }); + +describe("isMetadataTag", () => { + it("should not match empty tag", () => { + expect(isMetadataTag("")).toBeFalsy(); + }); + it("should not match regular tag", () => { + expect(isMetadataTag("regular")).toBeFalsy(); + }); + it("should not match multi word tag", () => { + expect(isMetadataTag("some multi word tag")).toBeFalsy(); + }); + it("should match allure.id tag", () => { + expect(isMetadataTag("allure.id=123")).toBeTruthy(); + }); + it("should match allure.label tag", () => { + expect(isMetadataTag("allure.label.x=y")).toBeTruthy(); + }); + it("should match @allure.label tag", () => { + expect(isMetadataTag("@allure.label.x=y")).toBeTruthy(); + }); +}); diff --git a/packages/allure-mocha/src/AllureMochaReporter.ts b/packages/allure-mocha/src/AllureMochaReporter.ts index 7713cc427..6fd571bb2 100644 --- a/packages/allure-mocha/src/AllureMochaReporter.ts +++ b/packages/allure-mocha/src/AllureMochaReporter.ts @@ -1,8 +1,10 @@ import * as Mocha from "mocha"; -import type { AttachmentOptions, ContentType, Label, Parameter } from "allure-js-commons"; +import { env } from "node:process"; +import { type AttachmentOptions, type ContentType, type Label, LabelName, type Parameter } from "allure-js-commons"; import { Stage, Status } from "allure-js-commons"; import type { Category, RuntimeMessage } from "allure-js-commons/sdk"; import { getStatusFromError } from "allure-js-commons/sdk"; +import { getHostLabel, getThreadLabel } from "allure-js-commons/sdk/reporter"; import type { ReporterConfig } from "allure-js-commons/sdk/reporter"; import { ReporterRuntime, @@ -23,7 +25,6 @@ import { getAllureFullName, getAllureMetaLabels, getHookType, - getInitialLabels, getSuitesOfMochaTest, getTestCaseId, getTestScope, @@ -46,12 +47,12 @@ const { } = Mocha.Runner.constants; export class AllureMochaReporter extends Mocha.reporters.Base { - private readonly runtime: ReporterRuntime; - private readonly testplan?: TestPlanIndices; - private readonly testsMap: Map = new Map(); - private scopesStack: string[] = []; - private currentTest?: string; - private currentHook?: string; + protected readonly runtime: ReporterRuntime; + protected readonly testplan?: TestPlanIndices; + protected readonly testsMap: Map = new Map(); + protected scopesStack: string[] = []; + protected currentTest?: string; + protected currentHook?: string; private readonly isInWorker: boolean; constructor(runner: Mocha.Runner, opts: Mocha.MochaOptions, isInWorker: boolean = false) { @@ -172,7 +173,12 @@ export class AllureMochaReporter extends Mocha.reporters.Base { } const globalLabels = getEnvironmentLabels().filter((label) => !!label.value); - const initialLabels: Label[] = getInitialLabels(); + const initialLabels: Label[] = [ + { name: LabelName.LANGUAGE, value: "javascript" }, + { name: LabelName.FRAMEWORK, value: this.getFrameworkName() }, + getHostLabel(), + getThreadLabel(this.getWorkerId()), + ]; const metaLabels = getAllureMetaLabels(test); const labels = globalLabels.concat(initialLabels, metaLabels); @@ -305,4 +311,8 @@ export class AllureMochaReporter extends Mocha.reporters.Base { private getCurrentSuiteScope = () => this.scopesStack.length > 0 ? this.scopesStack[this.scopesStack.length - 1] : undefined; + + protected getFrameworkName = (): string => "mocha"; + + protected getWorkerId = (): string | undefined => env.MOCHA_WORKER_ID; } diff --git a/packages/allure-mocha/src/utils.ts b/packages/allure-mocha/src/utils.ts index c273f440a..e6eda7db1 100644 --- a/packages/allure-mocha/src/utils.ts +++ b/packages/allure-mocha/src/utils.ts @@ -1,12 +1,10 @@ import type * as Mocha from "mocha"; import { dirname, extname, join } from "node:path"; -import { env } from "node:process"; import { fileURLToPath } from "node:url"; -import type { Label } from "allure-js-commons"; import { LabelName } from "allure-js-commons"; import type { TestPlanV1, TestPlanV1Test } from "allure-js-commons/sdk"; import { extractMetadataFromString } from "allure-js-commons/sdk"; -import { getHostLabel, getRelativePath, getThreadLabel, md5, parseTestPlan } from "allure-js-commons/sdk/reporter"; +import { getRelativePath, md5, parseTestPlan } from "allure-js-commons/sdk/reporter"; import type { AllureMochaTestData, HookCategory, HookScope, HookType, TestPlanIndices } from "./types.js"; const filename = fileURLToPath(import.meta.url); @@ -77,13 +75,6 @@ export const getSuitesOfMochaTest = (test: Mocha.Test) => test.titlePath().slice export const resolveParallelModeSetupFile = () => join(dirname(filename), `setupAllureMochaParallel${extname(filename)}`); -export const getInitialLabels = (): Label[] => [ - { name: LabelName.LANGUAGE, value: "javascript" }, - { name: LabelName.FRAMEWORK, value: "mocha" }, - getHostLabel(), - getThreadLabel(env.MOCHA_WORKER_ID), -]; - export const getTestCaseId = (test: Mocha.Test) => { const suiteTitles = test.titlePath().slice(0, -1); return md5(JSON.stringify([...suiteTitles, getAllureDisplayName(test)])); diff --git a/yarn.lock b/yarn.lock index 0604affe4..3801e64df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4014,6 +4014,7 @@ __metadata: "@typescript-eslint/parser": "npm:^8.0.0" allure-commandline: "npm:^2.29.0" allure-js-commons: "workspace:*" + allure-mocha: "workspace:*" allure-vitest: "workspace:*" babel-plugin-add-module-exports: "npm:^1.0.4" codeceptjs: "npm:^3.5.4" @@ -4283,7 +4284,7 @@ __metadata: languageName: unknown linkType: soft -"allure-mocha@workspace:packages/allure-mocha": +"allure-mocha@workspace:*, allure-mocha@workspace:packages/allure-mocha": version: 0.0.0-use.local resolution: "allure-mocha@workspace:packages/allure-mocha" dependencies: