diff --git a/.eslintrc.js b/.eslintrc.js index eb48eef..0b7a3f9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -77,7 +77,10 @@ module.exports = { "coverage", "dist", "e2e", + "package-e2e", "website", - "/*.js" + "/*.js", + "/*.mjs", + "/*.d.ts" ] }; diff --git a/api.js b/api.js new file mode 100644 index 0000000..4bba306 --- /dev/null +++ b/api.js @@ -0,0 +1 @@ +module.exports = require('./dist/api'); diff --git a/api.mjs b/api.mjs new file mode 100644 index 0000000..e2688af --- /dev/null +++ b/api.mjs @@ -0,0 +1 @@ +export * from './dist/api.js'; diff --git a/e2e/configs/default.js b/e2e/configs/default.js index d256ad9..cdc5d89 100644 --- a/e2e/configs/default.js +++ b/e2e/configs/default.js @@ -1,6 +1,5 @@ // eslint-disable-next-line node/no-extraneous-require,@typescript-eslint/no-var-requires,import/no-extraneous-dependencies const _ = require('lodash'); -const { Status } = require('jest-allure2-reporter'); const PRESET = process.env.ALLURE_PRESET ?? 'default'; /** @type {import('jest-allure2-reporter').ReporterOptions} */ @@ -9,12 +8,12 @@ const jestAllure2ReporterOptions = { categories: [ { name: 'Snapshot mismatches', - matchedStatuses: [Status.FAILED], + matchedStatuses: ['failed'], messageRegex: /.*\btoMatch(?:[A-Za-z]+)?Snapshot\b.*/, }, { name: 'Timeouts', - matchedStatuses: [Status.BROKEN], + matchedStatuses: ['broken'], messageRegex: /.*Exceeded timeout of.*/, }, ], diff --git a/e2e/src/programmatic/grouping/client/auth/ForgotPasswordScreen.test.ts b/e2e/src/programmatic/grouping/client/auth/ForgotPasswordScreen.test.ts index 527d7e9..7c176df 100644 --- a/e2e/src/programmatic/grouping/client/auth/ForgotPasswordScreen.test.ts +++ b/e2e/src/programmatic/grouping/client/auth/ForgotPasswordScreen.test.ts @@ -1,5 +1,3 @@ -import { $Epic, $Feature, $Story, $Tag } from 'jest-allure2-reporter'; - $Tag('client'); $Epic('Authentication'); $Feature('Restore account'); diff --git a/e2e/src/programmatic/grouping/client/auth/LoginScreen.test.ts b/e2e/src/programmatic/grouping/client/auth/LoginScreen.test.ts index 5a3f772..c66a4d8 100644 --- a/e2e/src/programmatic/grouping/client/auth/LoginScreen.test.ts +++ b/e2e/src/programmatic/grouping/client/auth/LoginScreen.test.ts @@ -1,4 +1,3 @@ -import { $Epic, $Feature, $Story, $Tag } from 'jest-allure2-reporter'; import LoginHelper from '../../../../utils/LoginHelper'; /** diff --git a/e2e/src/programmatic/grouping/client/utils/validators.test.ts b/e2e/src/programmatic/grouping/client/utils/validators.test.ts index 2f10604..d808fae 100644 --- a/e2e/src/programmatic/grouping/client/utils/validators.test.ts +++ b/e2e/src/programmatic/grouping/client/utils/validators.test.ts @@ -1,5 +1,3 @@ -import { $Tag } from 'jest-allure2-reporter'; - $Tag('client'); describe('Validators', () => { describe('emailValidator', () => { diff --git a/e2e/src/programmatic/grouping/server/controllers/forgotPassword.test.ts b/e2e/src/programmatic/grouping/server/controllers/forgotPassword.test.ts index 0bc9c11..0fd8f04 100644 --- a/e2e/src/programmatic/grouping/server/controllers/forgotPassword.test.ts +++ b/e2e/src/programmatic/grouping/server/controllers/forgotPassword.test.ts @@ -1,5 +1,3 @@ -import { $Epic, $Feature, $Story, $Tag } from 'jest-allure2-reporter'; - $Tag('server'); $Epic('Authentication'); $Feature('Restore account'); diff --git a/e2e/src/programmatic/grouping/server/controllers/login.test.ts b/e2e/src/programmatic/grouping/server/controllers/login.test.ts index d46d844..544b289 100644 --- a/e2e/src/programmatic/grouping/server/controllers/login.test.ts +++ b/e2e/src/programmatic/grouping/server/controllers/login.test.ts @@ -1,5 +1,3 @@ -import { $Epic, $Feature, $Story, $Tag } from 'jest-allure2-reporter'; - $Tag('server'); $Epic('Authentication'); $Feature('Login'); diff --git a/e2e/src/programmatic/grouping/server/controllers/resetPassword.test.ts b/e2e/src/programmatic/grouping/server/controllers/resetPassword.test.ts index 165786e..7dc4b54 100644 --- a/e2e/src/programmatic/grouping/server/controllers/resetPassword.test.ts +++ b/e2e/src/programmatic/grouping/server/controllers/resetPassword.test.ts @@ -1,5 +1,3 @@ -import { $Epic, $Feature, $Story, $Tag } from 'jest-allure2-reporter'; - $Tag('server'); $Epic('Authentication'); $Feature('Restore account'); diff --git a/e2e/src/utils/LoginHelper.ts b/e2e/src/utils/LoginHelper.ts index 6eed31d..0c35352 100644 --- a/e2e/src/utils/LoginHelper.ts +++ b/e2e/src/utils/LoginHelper.ts @@ -1,5 +1,3 @@ -import {Attachment, FileAttachment, Step} from 'jest-allure2-reporter'; - class LoginHelper { #email?: string; #password?: string; diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 124fc80..0421c42 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -4,6 +4,7 @@ "module": "node16", "ignoreDeprecations": "5.0", "experimentalDecorators": true, - "noEmit": true + "noEmit": true, + "types": ["node", "jest", "jest-allure2-reporter/globals"] } } diff --git a/environment-jsdom.js b/environment-jsdom.js index 1ff4fd2..890151d 100644 --- a/environment-jsdom.js +++ b/environment-jsdom.js @@ -1,2 +1 @@ -// Fallback for Jest 27.x.x -module.exports = require('./dist/environment/jsdom'); +module.exports = require('./dist/environment/jsdom').default; diff --git a/environment-jsdom.mjs b/environment-jsdom.mjs new file mode 100644 index 0000000..df635f9 --- /dev/null +++ b/environment-jsdom.mjs @@ -0,0 +1,5 @@ +import jsdom from './dist/environment/jsdom.js'; + +const { default: TestEnvironment } = jsdom; + +export default TestEnvironment; diff --git a/environment-listener.js b/environment-listener.js index 2cba366..b925f1c 100644 --- a/environment-listener.js +++ b/environment-listener.js @@ -1,2 +1 @@ -// Fallback for Jest 27.x.x -module.exports = require('./dist/environment/listener'); +module.exports = require('./dist/environment/listener').default; diff --git a/environment-listener.mjs b/environment-listener.mjs new file mode 100644 index 0000000..44358ff --- /dev/null +++ b/environment-listener.mjs @@ -0,0 +1,5 @@ +import environmentListener from './dist/environment/listener.js'; + +const { default: listener } = environmentListener; + +export default listener; diff --git a/environment-node.js b/environment-node.js index 2995a6b..f8aae5d 100644 --- a/environment-node.js +++ b/environment-node.js @@ -1,2 +1 @@ -// Fallback for Jest 27.x.x -module.exports = require('./dist/environment/node'); +module.exports = require('./dist/environment/node').default; diff --git a/environment-node.mjs b/environment-node.mjs new file mode 100644 index 0000000..d81fcdb --- /dev/null +++ b/environment-node.mjs @@ -0,0 +1,5 @@ +import node from './dist/environment/node.js'; + +const { default: TestEnvironment } = node; + +export default TestEnvironment; diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..251f924 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,45 @@ +import * as api from './dist/api'; + +declare global { + namespace NodeJS { + interface Global { + // Runtime API + allure: typeof api.allure; + // Pseudo-annotations + $Description: typeof api.$Description; + $DescriptionHtml: typeof api.$DescriptionHtml; + $Epic: typeof api.$Epic; + $Feature: typeof api.$Feature; + $Issue: typeof api.$Issue; + $Link: typeof api.$Link; + $Owner: typeof api.$Owner; + $Severity: typeof api.$Severity; + $Story: typeof api.$Story; + $Tag: typeof api.$Tag; + $TmsLink: typeof api.$TmsLink; + // Decorators + Attachment: typeof api.Attachment; + FileAttachment: typeof api.FileAttachment; + Step: typeof api.Step; + } + } + + // Runtime API + var allure: typeof api.allure; + // Pseudo-annotations + var $Description: typeof api.$Description; + var $DescriptionHtml: typeof api.$DescriptionHtml; + var $Epic: typeof api.$Epic; + var $Feature: typeof api.$Feature; + var $Issue: typeof api.$Issue; + var $Link: typeof api.$Link; + var $Owner: typeof api.$Owner; + var $Severity: typeof api.$Severity; + var $Story: typeof api.$Story; + var $Tag: typeof api.$Tag; + var $TmsLink: typeof api.$TmsLink; + // Decorators + var Attachment: typeof api.Attachment; + var FileAttachment: typeof api.FileAttachment; + var Step: typeof api.Step; +} diff --git a/globals.js b/globals.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/globals.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/globals.mjs b/globals.mjs new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/globals.mjs @@ -0,0 +1 @@ +export default {}; diff --git a/index.d.ts b/index.d.ts index eb51528..adbaec9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,24 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import type { Config, TestCaseResult, TestResult } from '@jest/reporters'; -import type { - Attachment, - Category, - ExecutorInfo, - Label, - LabelName, - Link, - LinkType, - Parameter, - ParameterOptions, - Severity, - Stage, - Status, - StatusDetails, -} from '@noomorph/allure-js-commons'; - -import type { Function_, MaybePromise } from './utils/types'; -import type { JestAllure2Reporter } from './src'; +import JestMetadataReporter from 'jest-metadata/reporter'; declare module 'jest-allure2-reporter' { /** @@ -64,6 +47,14 @@ declare module 'jest-allure2-reporter' { * @default 'allure-results' */ resultsDir?: string; + /** + * Inject Allure's global variables into the test environment. + * Those who don't want to pollute the global scope can disable this option + * and import the `jest-allure2-reporter/api` module in their test files. + * + * @default true + */ + injectGlobals?: boolean; /** * Configures how external attachments are attached to the report. */ @@ -108,6 +99,7 @@ declare module 'jest-allure2-reporter' { export type ReporterConfig = { overwrite: boolean; resultsDir: string; + injectGlobals: boolean; attachments: Required; categories: CategoriesCustomizer; environment: EnvironmentCustomizer; @@ -118,11 +110,6 @@ declare module 'jest-allure2-reporter' { plugins: Promise; }; - export type SharedReporterConfig = Pick< - ReporterConfig, - 'resultsDir' | 'overwrite' | 'attachments' - >; - export type _LabelName = | 'package' | 'testClass' @@ -346,12 +333,6 @@ declare module 'jest-allure2-reporter' { parameters: TestFileExtractor; } - /** - * Global customizations for how test cases are reported - * @inheritDoc - */ - export type TestCaseCustomizer = GenericTestCaseCustomizer; - export type ResolvedTestFileCustomizer = Required & { labels: TestFileExtractor; links: TestFileExtractor; @@ -519,7 +500,7 @@ declare module 'jest-allure2-reporter' { export interface GlobalExtractorContextAugmentation { detectLanguage?(filePath: string, contents: string): string | undefined; - processMarkdown?(markdown: string): MaybePromise; + processMarkdown?(markdown: string): Promise; // This should be extended by plugins } @@ -583,125 +564,89 @@ declare module 'jest-allure2-reporter' { | 'testCaseContext' | 'testStepContext'; - export interface IAllureRuntime { - flush(): Promise; - - description(value: string): void; - - descriptionHtml(value: string): void; - - status(status: Status, statusDetails?: StatusDetails): void; - - statusDetails(statusDetails: StatusDetails): void; - - label(name: LabelName | string, value: string): void; - - link(name: string, url: string, type?: string): void; - - parameter(name: string, value: unknown, options?: ParameterOptions): void; - - parameters(parameters: Record): void; - - attachment( - name: string, - content: MaybePromise, - mimeType?: string, - ): typeof content; - - createAttachment( - function_: Function_>, - name: string, - ): typeof function_; - createAttachment( - function_: Function_>, - options: AttachmentOptions, - ): typeof function_; - - fileAttachment(filePath: string, name?: string): string; - fileAttachment(filePath: string, options?: AttachmentOptions): string; - fileAttachment( - filePathPromise: Promise, - name?: string, - ): Promise; - fileAttachment( - filePathPromise: Promise, - options?: AttachmentOptions, - ): Promise; - - createFileAttachment( - function_: Function_>, - ): typeof function_; - createFileAttachment( - function_: Function_>, - name: string, - ): typeof function_; - createFileAttachment( - function_: Function_>, - options: AttachmentOptions, - ): typeof function_; - - createStep(name: string, function_: F): F; - createStep( - name: string, - arguments_: ParameterOrString[], - function_: F, - ): F; - - step(name: string, function_: () => T): T; + //region Allure types + + export interface Attachment { + name: string; + type: string; + source: string; } - export type ParameterOrString = string | Omit; + export interface Category { + name?: string; + description?: string; + descriptionHtml?: string; + messageRegex?: string | RegExp; + traceRegex?: string | RegExp; + matchedStatuses?: Status[]; + flaky?: boolean; + } + + export interface ExecutorInfo { + name?: string; + type?: + | 'jenkins' + | 'bamboo' + | 'teamcity' + | 'gitlab' + | 'github' + | 'circleci' + | string; + url?: string; + buildOrder?: number; + buildName?: string; + buildUrl?: string; + reportUrl?: string; + reportName?: string; + } - export type AttachmentContent = Buffer | string; + export interface Label { + name: LabelName | string; + value: string; + } - export type AttachmentOptions = { + export type LabelName = + | 'epic' + | 'feature' + | 'owner' + | 'package' + | 'parentSuite' + | 'severity' + | 'story' + | 'subSuite' + | 'suite' + | 'tag' + | 'testClass' + | 'testMethod' + | 'thread'; + + export interface Link { name?: string; - mimeType?: string; - }; + url: string; + type?: LinkType | string; + } + + export type LinkType = 'issue' | 'tms'; + + export interface Parameter { + name: string; + value: string; + excluded?: boolean; + mode?: 'hidden' | 'masked' | 'default'; + } + + export type Severity = 'blocker' | 'critical' | 'normal' | 'minor' | 'trivial'; + export type Stage = 'scheduled' | 'running' | 'finished' | 'pending' | 'interrupted'; + export type Status = 'failed' | 'broken' | 'passed' | 'skipped' | 'unknown'; + + export interface StatusDetails { + message?: string; + trace?: string; + } + + //endregion +} - // Reporter - export const reporter: JestAllure2Reporter; - export default reporter; - - // Runtime - export const allure: IAllureRuntime; - - // Pseudo-annotations - export const $Description: (description: string) => void; - export const $DescriptionHtml: (descriptionHtml: string) => void; - export const $Epic: (epic: string) => void; - export const $Feature: (feature: string) => void; - export const $Issue: (issue: string) => void; - export const $Link: - | ((link: Link) => void) - | ((url: string, name?: string) => void); - export const $Owner: (owner: string) => void; - export const $Severity: (severity: Severity[keyof Severity]) => void; - export const $Story: (story: string) => void; - export const $Tag: (...tagNames: string[]) => void; - export const $TmsLink: (tmsLink: string) => void; - - // Decorators - export function Attachment(name: string, mimeType?: string): MethodDecorator; - export function FileAttachment( - name: string, - mimeType?: string, - ): MethodDecorator; - export function Step( - name: string, - arguments_?: ParameterOrString[], - ): MethodDecorator; - - // Common types - export { - Category, - Link, - LinkType, - Parameter, - ParameterOptions, - ExecutorInfo, - Severity, - Status, - Stage, - } from '@noomorph/allure-js-commons'; +export default class JestAllure2Reporter extends JestMetadataReporter { + constructor(globalConfig: Config.GlobalConfig, options: ReporterOptions); } diff --git a/index.js b/index.js index f4f0e99..7b20598 100644 --- a/index.js +++ b/index.js @@ -1 +1 @@ -module.exports = require('./dist/index'); +module.exports = require('./dist/index').default; diff --git a/index.mjs b/index.mjs new file mode 100644 index 0000000..1cf4bee --- /dev/null +++ b/index.mjs @@ -0,0 +1,5 @@ +import reporter from './dist/index.js'; + +const { default: JestAllure2Reporter } = reporter; + +export default JestAllure2Reporter; diff --git a/package-e2e/.npmrc b/package-e2e/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/package-e2e/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/package-e2e/api.test.ts b/package-e2e/api.test.ts new file mode 100644 index 0000000..d1e7cc3 --- /dev/null +++ b/package-e2e/api.test.ts @@ -0,0 +1,137 @@ +import { + Attachment, + FileAttachment, + Step, + $Description, + $DescriptionHtml, + $Epic, + $Feature, + $Issue, + $Link, + $Owner, + $Severity, + $Story, + $Tag, + $TmsLink, + allure, +} from 'jest-allure2-reporter/api'; +import {Status} from "@noomorph/allure-js-commons"; +import {take} from "lodash"; + +declare var test: (name: string, fn: () => unknown) => void; + +$Description('This is _a test description_'); +$DescriptionHtml('This is a test description'); +$Epic('BDD epic'); +$Feature('BDD feature'); +$Story('BDD story'); +$Issue('JIRA-123'); +$Link('https://example.com', 'Example link'); +$Link({ + name: 'Example link', + url: 'https://example.com', + type: 'custom-type', +}); +$Owner('Yaroslav Serhieiev'); +$Severity('critical'); +$Tag('e2e'); +$TmsLink('TEST-456'); +test('typings of jest-allure2-reporter/api', async () => { + allure.description('This is a _description_ generated in runtime'); + allure.descriptionHtml('This is a description generated in runtime'); + allure.status('failed'); + allure.status('passed'); + allure.status('broken'); + allure.status('skipped'); + allure.status('unknown'); + allure.statusDetails({}); + allure.statusDetails({ message: 'Test failed due to a runtime error' }); + allure.statusDetails({ message: 'Test failed due to a runtime error', trace: 'Here is a trace' }); + allure.label('owner', 'Dynamic owner'); + allure.link('https://jira.example.com', 'JIRA-123', 'issue'); + allure.link('https://example.com', 'Custom link'); + allure.link('https://just-a-link.com'); + allure.parameters({ + Browser: 'Chrome', + Resolution: '1024x768', + }); + + allure.parameter('Login', 'admin'); + allure.parameter('Password', 'qwerty', { mode: 'masked' }); + allure.parameter('Deprecated', 'value', { excluded: true }); + + allure.attachment('file.txt', 'Example log content'); + allure.attachment('screenshot.png', Buffer.from(''), 'image/png'); + + await allure.attachment('file-async.txt', Promise.resolve('Example log content')); + + const takeScreenshotA1 = allure.createAttachment((s: string) => Buffer.from(s), 'screenshot.png'); + const takeScreenshotA2 = allure.createAttachment(async (s: string) => Buffer.from(s), { + name: 'Screenshot', + mimeType: 'image/png', + }); + const takeScreenshotB1 = allure.createFileAttachment((file: string) => `./${file}.png`); + const takeScreenshotB2 = allure.createFileAttachment(async (file: string) => `./${file}.png`, 'Screenshot'); + const takeScreenshotB3 = allure.createFileAttachment(async (file: string, ext: string) => `./${file}${ext}`, { + name: 'Screenshot', + mimeType: 'image/png', + }); + + assertType(takeScreenshotA1('1')); + assertType>(takeScreenshotA2('2')); + assertType(takeScreenshotB1('file1')); + assertType>(takeScreenshotB2('file2')); + assertType>(takeScreenshotB3('file3', '.png')); + + assertType(allure.step('Simple step', () => {})); + assertType>(allure.step('Simple step', async () => {})); + + const login1 = allure.createStep('Login', (username: string, password: string) => { + console.log('Login executed via %s and %s', username, password); + }); + + const login2 = allure.createStep('Login', [ + 'username', + { name: 'password', mode: 'masked'}, + ], async (username: string, password: string) => { + console.log('Login executed via %s and %s', username, password); + }); + + assertType(login1('admin', 'qwerty')); + assertType>(login2('admin', 'qwerty')); + + + class LoginHelper { + @Attachment('%s', 'image/png') + async takeScreenshot1(name: string) { + return Buffer.from(name); + } + + @FileAttachment('%s', 'image/png') + takeScreenshot2(name: string) { + return `/path/to/${name}.png`; + } + + @Step('Restore password for %s') + restorePassword(login: string) { + console.log('Restore password for %s', login); + return login; + } + + @Step('Login as %s', [ + { name: 'username' }, + { name: 'password', mode: 'masked' }, + ]) + async login(username: string, password: string) { + console.log('Login as %s via %s', username, password); + } + } + + const helper = new LoginHelper(); + assertType>(helper.takeScreenshot1('hello')); + assertType(helper.takeScreenshot2('world')); + assertType(helper.restorePassword('admin')); + assertType>(helper.login('admin', 'qwerty')); +}); + +function assertType(_value: T): void {} diff --git a/package-e2e/globals.test.ts b/package-e2e/globals.test.ts new file mode 100644 index 0000000..6b73527 --- /dev/null +++ b/package-e2e/globals.test.ts @@ -0,0 +1,26 @@ +import 'jest-allure2-reporter/globals'; + +// Pseudo-annotations +assertEqualType($Description, global.$Description); +assertEqualType($DescriptionHtml, global.$DescriptionHtml); +assertEqualType($Epic, global.$Epic); +assertEqualType($Feature, global.$Feature); +assertEqualType($Story, global.$Story); +assertEqualType($Issue, global.$Issue); +assertEqualType($Link, global.$Link); +assertEqualType($Owner, global.$Owner); +assertEqualType($Severity, global.$Severity); +assertEqualType($Tag, global.$Tag); +assertEqualType($TmsLink, global.$TmsLink); + +// Runtime API +assertEqualType(allure, global.allure); + +// Decorators +assertEqualType(Attachment, global.Attachment); +assertEqualType(FileAttachment, global.FileAttachment); +assertEqualType(Step, global.Step); + +function assertEqualType(_a: T, _b: T): void { + // no-op +} diff --git a/package-e2e/isolation.test.js b/package-e2e/isolation.test.js new file mode 100644 index 0000000..e475988 --- /dev/null +++ b/package-e2e/isolation.test.js @@ -0,0 +1,28 @@ +"use strict"; +exports.__esModule = true; +require("jest-allure2-reporter"); +require("jest-allure2-reporter/api"); +// @ts-expect-error TS2304: Cannot find name '$Description'. +$Description; +// @ts-expect-error TS2304: Cannot find name '$DescriptionHtml'. +$DescriptionHtml; +// @ts-expect-error TS2304: Cannot find name '$Epic'. +$Epic; +// @ts-expect-error TS2304: Cannot find name '$Feature'. +$Feature; +// @ts-expect-error TS2304: Cannot find name '$Story'. +$Story; +// @ts-expect-error TS2304: Cannot find name '$Issue'. +$Issue; +// @ts-expect-error TS2304: Cannot find name '$Link'. +$Link; +// @ts-expect-error TS2304: Cannot find name '$Owner'. +$Owner; +// @ts-expect-error TS2304: Cannot find name '$Severity'. +$Severity; +// @ts-expect-error TS2304: Cannot find name '$Tag'. +$Tag; +// @ts-expect-error TS2304: Cannot find name '$TmsLink'. +$TmsLink; +// @ts-expect-error TS2304: Cannot find name 'allure'. +allure; diff --git a/package-e2e/isolation.test.ts b/package-e2e/isolation.test.ts new file mode 100644 index 0000000..18e9f89 --- /dev/null +++ b/package-e2e/isolation.test.ts @@ -0,0 +1,27 @@ +import 'jest-allure2-reporter'; +import 'jest-allure2-reporter/api'; + +// @ts-expect-error TS2304: Cannot find name '$Description'. +$Description; +// @ts-expect-error TS2304: Cannot find name '$DescriptionHtml'. +$DescriptionHtml; +// @ts-expect-error TS2304: Cannot find name '$Epic'. +$Epic; +// @ts-expect-error TS2304: Cannot find name '$Feature'. +$Feature; +// @ts-expect-error TS2304: Cannot find name '$Story'. +$Story; +// @ts-expect-error TS2304: Cannot find name '$Issue'. +$Issue; +// @ts-expect-error TS2304: Cannot find name '$Link'. +$Link; +// @ts-expect-error TS2304: Cannot find name '$Owner'. +$Owner; +// @ts-expect-error TS2304: Cannot find name '$Severity'. +$Severity; +// @ts-expect-error TS2304: Cannot find name '$Tag'. +$Tag; +// @ts-expect-error TS2304: Cannot find name '$TmsLink'. +$TmsLink; +// @ts-expect-error TS2304: Cannot find name 'allure'. +allure; diff --git a/package-e2e/package.json b/package-e2e/package.json new file mode 100644 index 0000000..ed9cbeb --- /dev/null +++ b/package-e2e/package.json @@ -0,0 +1,15 @@ +{ + "name": "@jest-allure2-reporter/package-e2e", + "private": true, + "version": "1.0.0", + "description": "", + "scripts": { + "test": "node test.cjs && node test.mjs && tsc", + "test:cjs": "node test.cjs", + "test:mjs": "node test.mjs", + "test:ts": "tsc && tsc -p tsconfig.isolated.json" + }, + "dependencies": { + "jest-allure2-reporter": ".." + } +} diff --git a/package-e2e/test.cjs b/package-e2e/test.cjs new file mode 100644 index 0000000..6ead794 --- /dev/null +++ b/package-e2e/test.cjs @@ -0,0 +1,23 @@ +const assert = require('assert'); + +const JestAllure2Reporter = require('jest-allure2-reporter'); +assert(isClass(JestAllure2Reporter), 'jest-allure2-reporter should export a class as its default export'); +// assert(typeof JestAllure2Reporter.query === 'object', 'jest-allure2-reporter/reporter class should export .query helper'); + +const environmentListener = require('jest-allure2-reporter/environment-listener'); +assert(typeof environmentListener === 'function', 'jest-allure2-reporter/environment-listener should export a class as its default export'); + +const JsdomTestEnvironment = require('jest-allure2-reporter/environment-jsdom'); +assert(isClass(JsdomTestEnvironment), 'jest-allure2-reporter/environment-jsdom should export a class as its default export'); + +const NodeTestEnvironment = require('jest-allure2-reporter/environment-node'); +assert(isClass(NodeTestEnvironment), 'jest-allure2-reporter/environment-node should export a class as its default export'); + +const { Attachment, $Owner, allure } = require('jest-allure2-reporter/api'); +assert(typeof Attachment === 'function', 'jest-allure2-reporter should export Attachment decorator as a named export'); +assert(typeof $Owner === 'function', 'jest-allure2-reporter should export $Owner function as a named export'); +assert(typeof allure === 'object', 'jest-allure2-reporter should export allure object as a named export'); + +function isClass(obj) { + return typeof obj === 'function' && /^class\s/.test(Function.prototype.toString.call(obj)); +} diff --git a/package-e2e/test.mjs b/package-e2e/test.mjs new file mode 100644 index 0000000..4147d4c --- /dev/null +++ b/package-e2e/test.mjs @@ -0,0 +1,18 @@ +import assert from 'assert'; +import JestAllure2Reporter from 'jest-allure2-reporter'; +import environmentListener from 'jest-allure2-reporter/environment-listener'; +import JsdomTestEnvironment from 'jest-allure2-reporter/environment-jsdom'; +import NodeTestEnvironment from 'jest-allure2-reporter/environment-node'; +import { Attachment, $Owner, allure } from 'jest-allure2-reporter/api'; + +assert(isClass(JestAllure2Reporter), 'jest-allure2-reporter should export a class as its default export'); +assert(typeof environmentListener === 'function', 'jest-allure2-reporter/environment-listener should export a class as its default export'); +assert(isClass(JsdomTestEnvironment), 'jest-allure2-reporter/environment-jsdom should export a class as its default export'); +assert(isClass(NodeTestEnvironment), 'jest-allure2-reporter/environment-node should export a class as its default export'); +assert(typeof allure === 'object', 'jest-allure2-reporter should export allure object as a named export'); +assert(typeof Attachment === 'function', 'jest-allure2-reporter should export Attachment decorator as a named export'); +assert(typeof $Owner === 'function', 'jest-allure2-reporter should export $Owner function as a named export'); + +function isClass(obj) { + return typeof obj === 'function' && /^class\s/.test(Function.prototype.toString.call(obj)); +} diff --git a/package-e2e/test.ts b/package-e2e/test.ts new file mode 100644 index 0000000..9958770 --- /dev/null +++ b/package-e2e/test.ts @@ -0,0 +1,27 @@ +import Reporter from 'jest-allure2-reporter'; +import type { ReporterOptions } from 'jest-allure2-reporter'; +import JsdomTestEnvironment from 'jest-allure2-reporter/environment-jsdom'; +import NodeTestEnvironment from 'jest-allure2-reporter/environment-node'; +import environmentListener from 'jest-allure2-reporter/environment-listener'; + +function assertType(_actual: T): void { + // no-op +} + +const reporter = new Reporter({} as any, {} as any); +assertType>(reporter.onRunStart({} as any, {} as any)); +assertType(reporter); +// assertType(Reporter.query); +assertType({ + overwrite: false, + resultsDir: 'allure-results', + attachments: { + subDir: 'attachments', + fileHandler: 'copy', + }, +}); + +assertType($Owner); +assertType(JsdomTestEnvironment); +assertType(NodeTestEnvironment); +assertType(environmentListener); diff --git a/package-e2e/tsconfig.isolated.json b/package-e2e/tsconfig.isolated.json new file mode 100644 index 0000000..314b051 --- /dev/null +++ b/package-e2e/tsconfig.isolated.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["isolation.test.ts"] +} diff --git a/package-e2e/tsconfig.json b/package-e2e/tsconfig.json new file mode 100644 index 0000000..0acf017 --- /dev/null +++ b/package-e2e/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "strict": true, + "noEmit": true + }, + "files": [ + "api.test.ts", + "globals.test.ts", + "test.ts" + ], +} diff --git a/package.json b/package.json index 0a798ec..36e94ee 100644 --- a/package.json +++ b/package.json @@ -24,34 +24,41 @@ "typings": "./index.d.ts", "exports": { ".": { - "import": "./index.js", + "import": "./index.mjs", "require": "./index.js", "types": "./index.d.ts" }, - "./environment-decorator": { - "import": "./dist/environment/decorator.js", - "require": "./dist/environment/decorator.js", - "types": "./dist/environment/decorator.d.ts" + "./api": { + "import": "./api.mjs", + "require": "./api.js", + "types": "./dist/api.d.ts" + }, + "./globals": { + "import": "./globals.mjs", + "require": "./globals.js", + "types": "./globals.d.ts" }, "./environment-listener": { - "import": "./dist/environment/listener.js", - "require": "./dist/environment/listener.js", + "import": "./environment-listener.mjs", + "require": "./environment-listener.js", "types": "./dist/environment/listener.d.ts" }, "./environment-jsdom": { - "import": "./dist/environment/jsdom.js", - "require": "./dist/environment/jsdom.js", + "import": "./environment-jsdom.mjs", + "require": "./environment-jsdom.js", "types": "./dist/environment/jsdom.d.ts" }, "./environment-node": { - "import": "./dist/environment/node.js", - "require": "./dist/environment/node.js", + "import": "./environment-node.mjs", + "require": "./environment-node.js", "types": "./dist/environment/node.d.ts" }, "./package.json": "./package.json" }, "files": [ "*.js", + "*.mjs", + "*.d.ts", "!.*.js", "!*.config.js", "src", @@ -77,7 +84,7 @@ "@types/jest": "^29.0.0", "@types/lodash": "^4.14.186", "@types/lodash.snakecase": "^4.0.0", - "@types/node": "^14.18.46", + "@types/node": "^16.0.0", "@types/node-fetch": "^2.6.9", "@types/rimraf": "^4.0.5", "@typescript-eslint/eslint-plugin": "^5.28.0", @@ -96,6 +103,7 @@ "glob": "^8.0.3", "husky": "^8.0.1", "jest": "^29.0.0", + "jest-environment-jsdom": "^29.0.0", "lerna": "^6.6.2", "lint-staged": "^14.0.1", "lodash": "^4.17.21", @@ -108,7 +116,7 @@ "dependencies": { "@noomorph/allure-js-commons": "^2.3.0", "ci-info": "^3.8.0", - "jest-metadata": "^1.3.0", + "jest-metadata": "^1.3.1", "lodash.snakecase": "^4.1.1", "node-fetch": "^2.6.7", "pkg-up": "^3.1.0", diff --git a/src/annotations/$Epic.ts b/src/annotations/$Epic.ts index a4516bc..bf35e25 100644 --- a/src/annotations/$Epic.ts +++ b/src/annotations/$Epic.ts @@ -1,5 +1,3 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Epic = (value: string) => $Label(LabelName.EPIC, value); +export const $Epic = (value: string) => $Label('epic', value); diff --git a/src/annotations/$Feature.ts b/src/annotations/$Feature.ts index 8eb17a9..75fb731 100644 --- a/src/annotations/$Feature.ts +++ b/src/annotations/$Feature.ts @@ -1,5 +1,3 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Feature = (value: string) => $Label(LabelName.FEATURE, value); +export const $Feature = (value: string) => $Label('feature', value); diff --git a/src/annotations/$Label.ts b/src/annotations/$Label.ts index 8ab662a..a6a27a8 100644 --- a/src/annotations/$Label.ts +++ b/src/annotations/$Label.ts @@ -1,5 +1,5 @@ -import type { LabelName } from '@noomorph/allure-js-commons'; import { $Push } from 'jest-metadata'; +import type { LabelName } from 'jest-allure2-reporter'; import { LABELS } from '../constants'; diff --git a/src/annotations/$Link.ts b/src/annotations/$Link.ts index e2428a1..75394b6 100644 --- a/src/annotations/$Link.ts +++ b/src/annotations/$Link.ts @@ -1,5 +1,5 @@ import { $Push } from 'jest-metadata'; -import type { Link } from '@noomorph/allure-js-commons'; +import type { Link } from 'jest-allure2-reporter'; import { LINKS } from '../constants'; diff --git a/src/annotations/$Owner.ts b/src/annotations/$Owner.ts index 97ae399..e688951 100644 --- a/src/annotations/$Owner.ts +++ b/src/annotations/$Owner.ts @@ -1,5 +1,3 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Owner = (value: string) => $Label(LabelName.OWNER, value); +export const $Owner = (value: string) => $Label('owner', value); diff --git a/src/annotations/$Severity.ts b/src/annotations/$Severity.ts index db431c8..5779401 100644 --- a/src/annotations/$Severity.ts +++ b/src/annotations/$Severity.ts @@ -1,5 +1,10 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Severity = (value: string) => $Label(LabelName.SEVERITY, value); +export type SeverityType = + | 'blocker' + | 'critical' + | 'normal' + | 'minor' + | 'trivial'; + +export const $Severity = (value: SeverityType) => $Label('severity', value); diff --git a/src/annotations/$Story.ts b/src/annotations/$Story.ts index cee932b..e111bce 100644 --- a/src/annotations/$Story.ts +++ b/src/annotations/$Story.ts @@ -1,5 +1,3 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Story = (value: string) => $Label(LabelName.STORY, value); +export const $Story = (value: string) => $Label('story', value); diff --git a/src/annotations/$Tag.ts b/src/annotations/$Tag.ts index ab55851..bd77db9 100644 --- a/src/annotations/$Tag.ts +++ b/src/annotations/$Tag.ts @@ -1,6 +1,3 @@ -import { LabelName } from '@noomorph/allure-js-commons'; - import { $Label } from './$Label'; -export const $Tag = (...tagNames: string[]) => - $Label(LabelName.TAG, ...tagNames); +export const $Tag = (...tagNames: string[]) => $Label('tag', ...tagNames); diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..e243f4a --- /dev/null +++ b/src/api.ts @@ -0,0 +1,14 @@ +import type { IAllureRuntime } from './runtime'; +import realm from './realms'; + +export * from './annotations'; +export * from './decorators'; + +export const allure = realm.runtime as IAllureRuntime; + +export type { + AttachmentContent, + AttachmentOptions, + IAllureRuntime, + ParameterOrString, +} from './runtime'; diff --git a/src/builtin-plugins/docblock.ts b/src/builtin-plugins/docblock.ts index d84376d..e51955b 100644 --- a/src/builtin-plugins/docblock.ts +++ b/src/builtin-plugins/docblock.ts @@ -4,7 +4,7 @@ import fs from 'node:fs/promises'; import type { Plugin, PluginConstructor } from 'jest-allure2-reporter'; import { state } from 'jest-metadata'; import type { Metadata } from 'jest-metadata'; -import type { Label } from '@noomorph/allure-js-commons'; +import type { Label } from 'jest-allure2-reporter'; import { CODE, DESCRIPTION, LABELS } from '../constants'; import { splitDocblock } from '../utils/splitDocblock'; diff --git a/src/builtin-plugins/remark.ts b/src/builtin-plugins/remark.ts index 43bbd22..7663a28 100644 --- a/src/builtin-plugins/remark.ts +++ b/src/builtin-plugins/remark.ts @@ -22,9 +22,8 @@ export const remarkPlugin: PluginConstructor = () => { .use(rehypeHighlight.default) .use(rehypeStringify.default); - context.processMarkdown = (markdown: string) => { - const result = processor.processSync(markdown); - return String(result); + context.processMarkdown = async (markdown: string) => { + return processor.process(markdown).then((result) => result.toString()); }; }, }; diff --git a/src/decorators/Step.ts b/src/decorators/Step.ts index 5987892..e9ae02e 100644 --- a/src/decorators/Step.ts +++ b/src/decorators/Step.ts @@ -1,5 +1,4 @@ -import type { ParameterOrString } from 'jest-allure2-reporter'; - +import type { ParameterOrString } from '../runtime'; import realm from '../realms'; const allure = realm.runtime; diff --git a/src/environment/listener.ts b/src/environment/listener.ts index 60b9565..c6b5c59 100644 --- a/src/environment/listener.ts +++ b/src/environment/listener.ts @@ -1,9 +1,9 @@ import type { Circus } from '@jest/types'; import { state } from 'jest-metadata'; -import { Stage, Status } from '@noomorph/allure-js-commons'; import type { AllureTestCaseMetadata, AllureTestStepMetadata, + Status, } from 'jest-allure2-reporter'; import type { EnvironmentListenerFn, @@ -11,8 +11,10 @@ import type { TestEnvironmentSetupEvent, } from 'jest-environment-emit'; -import { CODE, PREFIX, WORKER_ID } from '../constants'; +import * as api from '../api'; +import { CODE, PREFIX, SHARED_CONFIG, WORKER_ID } from '../constants'; import realm from '../realms'; +import type { SharedReporterConfig } from '../runtime'; const listener: EnvironmentListenerFn = (context) => { context.testEvents @@ -20,6 +22,14 @@ const listener: EnvironmentListenerFn = (context) => { 'test_environment_setup', function ({ env }: TestEnvironmentSetupEvent) { env.global.__ALLURE__ = realm; + const { injectGlobals } = state.get( + SHARED_CONFIG, + ) as SharedReporterConfig; + + if (injectGlobals) { + Object.assign(env.global, api); + } + state.currentMetadata.set(WORKER_ID, process.env.JEST_WORKER_ID); }, ) @@ -62,7 +72,7 @@ const listener: EnvironmentListenerFn = (context) => { function executableStart({}: TestEnvironmentCircusEvent) { const metadata: AllureTestStepMetadata = { start: Date.now(), - stage: Stage.RUNNING, + stage: 'running', }; state.currentMetadata.assign(PREFIX, metadata); @@ -75,8 +85,8 @@ function executableFailure({ >) { const metadata: AllureTestStepMetadata = { stop: Date.now(), - stage: Stage.INTERRUPTED, - status: Status.FAILED, + stage: 'interrupted', + status: 'failed', }; if (event.error) { @@ -93,8 +103,8 @@ function executableFailure({ function executableSuccess({}: TestEnvironmentCircusEvent) { const metadata: AllureTestStepMetadata = { stop: Date.now(), - stage: Stage.FINISHED, - status: Status.PASSED, + stage: 'finished', + status: 'passed', }; state.currentMetadata.assign(PREFIX, metadata); @@ -103,8 +113,8 @@ function executableSuccess({}: TestEnvironmentCircusEvent) { function testSkip() { const metadata: AllureTestCaseMetadata = { stop: Date.now(), - stage: Stage.PENDING, - status: Status.SKIPPED, + stage: 'pending', + status: 'skipped', }; state.currentMetadata.assign(PREFIX, metadata); @@ -114,18 +124,18 @@ function testDone({ event, }: TestEnvironmentCircusEvent) { const hasErrors = event.test.errors.length > 0; - const errorStatus = event.test.errors.some((errors) => { + const errorStatus: Status = event.test.errors.some((errors) => { return Array.isArray(errors) ? errors.some(isMatcherError) : isMatcherError(errors); }) - ? Status.FAILED - : Status.BROKEN; + ? 'failed' + : 'broken'; const metadata: AllureTestCaseMetadata = { stop: Date.now(), - stage: hasErrors ? Stage.INTERRUPTED : Stage.FINISHED, - status: hasErrors ? errorStatus : Status.PASSED, + stage: hasErrors ? 'interrupted' : 'finished', + status: hasErrors ? errorStatus : 'passed', }; state.currentMetadata.assign(PREFIX, metadata); diff --git a/src/index.ts b/src/index.ts index e311c00..01d2be1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,24 +1 @@ -/// - -import type { IAllureRuntime } from 'jest-allure2-reporter'; - -import realm from './realms'; - -export { JestAllure2Reporter } from './reporter/JestAllure2Reporter'; -export { JestAllure2Reporter as default } from './reporter/JestAllure2Reporter'; -export * from './annotations'; -export * from './decorators'; - -export { - Category, - Link, - LinkType, - Parameter, - ParameterOptions, - ExecutorInfo, - Severity, - Status, - Stage, -} from '@noomorph/allure-js-commons'; - -export const allure = realm.runtime as IAllureRuntime; +export { JestAllure2Reporter as default } from './reporter'; diff --git a/src/options/compose-options/index.ts b/src/options/compose-options/index.ts index 88471ca..0982b87 100644 --- a/src/options/compose-options/index.ts +++ b/src/options/compose-options/index.ts @@ -27,6 +27,7 @@ export function composeOptions( overwrite: custom.overwrite ?? base.overwrite, resultsDir: custom.resultsDir ?? base.resultsDir, + injectGlobals: custom.injectGlobals ?? base.injectGlobals, attachments: composeAttachments(base.attachments, custom.attachments), testCase: composeTestCaseCustomizers(base.testCase, custom.testCase), testFile: composeTestFileCustomizers(base.testFile, custom.testFile), diff --git a/src/options/default-options/categories.ts b/src/options/default-options/categories.ts index f845f45..cf2e854 100644 --- a/src/options/default-options/categories.ts +++ b/src/options/default-options/categories.ts @@ -1,7 +1,6 @@ -import type { Category } from '@noomorph/allure-js-commons'; -import { Status } from '@noomorph/allure-js-commons'; +import type { Category } from 'jest-allure2-reporter'; export const categories: () => Category[] = () => [ - { name: 'Product defects', matchedStatuses: [Status.FAILED] }, - { name: 'Test defects', matchedStatuses: [Status.BROKEN] }, + { name: 'Product defects', matchedStatuses: ['failed'] }, + { name: 'Test defects', matchedStatuses: ['broken'] }, ]; diff --git a/src/options/default-options/executor.ts b/src/options/default-options/executor.ts index 0b9589b..fc633f2 100644 --- a/src/options/default-options/executor.ts +++ b/src/options/default-options/executor.ts @@ -1,7 +1,7 @@ import os from 'node:os'; import type { ExecutorCustomizer } from 'jest-allure2-reporter'; -import type { ExecutorInfo } from '@noomorph/allure-js-commons'; +import type { ExecutorInfo } from 'jest-allure2-reporter'; export function executor(): ExecutorCustomizer { if (process.env.GITHUB_ACTIONS) return githubActions; diff --git a/src/options/default-options/index.ts b/src/options/default-options/index.ts index 3cf429e..1cfde17 100644 --- a/src/options/default-options/index.ts +++ b/src/options/default-options/index.ts @@ -17,6 +17,7 @@ export function defaultOptions(context: PluginContext): ReporterConfig { return { overwrite: true, resultsDir: 'allure-results', + injectGlobals: true, attachments: { subDir: 'attachments', fileHandler: 'ref', diff --git a/src/options/default-options/testCase.ts b/src/options/default-options/testCase.ts index 30048b3..4b6cea4 100644 --- a/src/options/default-options/testCase.ts +++ b/src/options/default-options/testCase.ts @@ -1,14 +1,18 @@ import path from 'node:path'; import type { TestCaseResult } from '@jest/reporters'; -import type { Label, StatusDetails } from '@noomorph/allure-js-commons'; -import { Stage, Status } from '@noomorph/allure-js-commons'; import type { ExtractorContext, ResolvedTestCaseCustomizer, TestCaseCustomizer, TestCaseExtractorContext, } from 'jest-allure2-reporter'; +import type { + Label, + Stage, + Status, + StatusDetails, +} from 'jest-allure2-reporter'; import { aggregateLabelCustomizers, @@ -71,21 +75,21 @@ function getTestCaseStatus(testCase: TestCaseResult): Status { const hasErrors = testCase.failureMessages?.length > 0; switch (testCase.status) { case 'passed': { - return Status.PASSED; + return 'passed'; } case 'failed': { - return Status.FAILED; + return 'failed'; } case 'skipped': { - return Status.SKIPPED; + return 'skipped'; } case 'pending': case 'todo': case 'disabled': { - return Status.SKIPPED; + return 'skipped'; } case 'focused': { - return hasErrors ? Status.FAILED : Status.PASSED; + return hasErrors ? 'failed' : 'passed'; } default: { return 'unknown' as Status; @@ -98,16 +102,16 @@ function getTestCaseStage(testCase: TestCaseResult): Stage { case 'passed': case 'focused': case 'failed': { - return Stage.FINISHED; + return 'finished'; } case 'todo': case 'disabled': case 'pending': case 'skipped': { - return Stage.PENDING; + return 'pending'; } default: { - return Stage.INTERRUPTED; + return 'interrupted'; } } } diff --git a/src/options/default-options/testFile.ts b/src/options/default-options/testFile.ts index d1ff918..d3d20f3 100644 --- a/src/options/default-options/testFile.ts +++ b/src/options/default-options/testFile.ts @@ -2,14 +2,13 @@ import fs from 'node:fs'; import path from 'node:path'; import type { TestResult } from '@jest/reporters'; -import type { Label, Link, StatusDetails } from '@noomorph/allure-js-commons'; -import { Stage, Status } from '@noomorph/allure-js-commons'; import type { ExtractorContext, TestFileExtractorContext, ResolvedTestFileCustomizer, TestCaseCustomizer, } from 'jest-allure2-reporter'; +import type { Label, Link, StatusDetails } from 'jest-allure2-reporter'; import { aggregateLabelCustomizers, @@ -38,9 +37,9 @@ export const testFile: ResolvedTestFileCustomizer = { descriptionHtml: () => void 0, start: ({ testFileMetadata }) => testFileMetadata.start, stop: ({ testFileMetadata }) => testFileMetadata.stop, - stage: () => Stage.FINISHED, + stage: () => 'finished', status: ({ testFile }: TestFileExtractorContext) => - testFile.testExecError ? Status.BROKEN : Status.PASSED, + testFile.testExecError ? 'broken' : 'passed', statusDetails: ({ testFile }) => stripStatusDetails(getTestFileStatusDetails(testFile)), attachments: ({ testFileMetadata }) => testFileMetadata.attachments ?? [], diff --git a/src/options/utils/aggregateLabelCustomizers.ts b/src/options/utils/aggregateLabelCustomizers.ts index 35d776f..f45ed99 100644 --- a/src/options/utils/aggregateLabelCustomizers.ts +++ b/src/options/utils/aggregateLabelCustomizers.ts @@ -1,11 +1,11 @@ /* eslint-disable unicorn/no-array-reduce */ -import type { Label } from '@noomorph/allure-js-commons'; import type { Extractor, ExtractorContext, TestFileCustomizer, TestCaseCustomizer, } from 'jest-allure2-reporter'; +import type { Label } from 'jest-allure2-reporter'; import { asExtractor } from './asExtractor'; diff --git a/src/options/utils/aggregateLinkCustomizers.ts b/src/options/utils/aggregateLinkCustomizers.ts index db0a883..20f75ff 100644 --- a/src/options/utils/aggregateLinkCustomizers.ts +++ b/src/options/utils/aggregateLinkCustomizers.ts @@ -1,10 +1,10 @@ -import type { Link } from '@noomorph/allure-js-commons'; import type { Extractor, ExtractorContext, TestFileCustomizer, TestCaseCustomizer, } from 'jest-allure2-reporter'; +import type { Link } from 'jest-allure2-reporter'; type Customizer = TestFileCustomizer | TestCaseCustomizer; diff --git a/src/options/utils/stripStatusDetails.ts b/src/options/utils/stripStatusDetails.ts index 3669520..2f58719 100644 --- a/src/options/utils/stripStatusDetails.ts +++ b/src/options/utils/stripStatusDetails.ts @@ -1,5 +1,5 @@ -import type { StatusDetails } from '@noomorph/allure-js-commons'; import stripAnsi from 'strip-ansi'; +import type { StatusDetails } from 'jest-allure2-reporter'; export function stripStatusDetails( statusDetails?: StatusDetails, diff --git a/src/realms/AllureRealm.ts b/src/realms/AllureRealm.ts index f400ca3..c3787a2 100644 --- a/src/realms/AllureRealm.ts +++ b/src/realms/AllureRealm.ts @@ -1,9 +1,9 @@ import { state } from 'jest-metadata'; -import type { SharedReporterConfig } from 'jest-allure2-reporter'; import { AllureRuntime } from '../runtime'; import { SHARED_CONFIG } from '../constants'; -import { AttachmentsHandler } from '../runtime/AttachmentsHandler'; +import { AttachmentsHandler } from '../runtime'; +import type { SharedReporterConfig } from '../runtime'; export class AllureRealm { runtime = new AllureRuntime({ diff --git a/src/reporter/JestAllure2Reporter.ts b/src/reporter/JestAllure2Reporter.ts index 13b6dbf..c2d5c4a 100644 --- a/src/reporter/JestAllure2Reporter.ts +++ b/src/reporter/JestAllure2Reporter.ts @@ -11,18 +11,8 @@ import type { TestResult, } from '@jest/reporters'; import { state } from 'jest-metadata'; -import { JestMetadataReporter, query } from 'jest-metadata/reporter'; +import JestMetadataReporter from 'jest-metadata/reporter'; import rimraf from 'rimraf'; -import type { - Attachment, - ExecutableItemWrapper, - Label, - Link, - Parameter, - Stage, - Status, - StatusDetails, -} from '@noomorph/allure-js-commons'; import { AllureRuntime } from '@noomorph/allure-js-commons'; import type { AllureTestStepMetadata, @@ -32,15 +22,26 @@ import type { ReporterConfig, ReporterOptions, ResolvedTestStepCustomizer, - SharedReporterConfig, TestCaseExtractorContext, TestStepExtractorContext, TestFileExtractorContext, } from 'jest-allure2-reporter'; +import type { + Attachment, + Category, + ExecutableItemWrapper, + Label, + Link, + Parameter, + Stage, + Status, + StatusDetails, +} from '@noomorph/allure-js-commons'; import { resolveOptions } from '../options'; import { MetadataSquasher, StepExtractor } from '../metadata'; import { SHARED_CONFIG, START, STOP, WORKER_ID } from '../constants'; +import type { SharedReporterConfig } from '../runtime'; import { ThreadService } from '../utils/ThreadService'; import md5 from '../utils/md5'; @@ -66,6 +67,7 @@ export class JestAllure2Reporter extends JestMetadataReporter { resultsDir: this._config.resultsDir, overwrite: this._config.overwrite, attachments: this._config.attachments, + injectGlobals: this._config.injectGlobals, } as SharedReporterConfig); } @@ -86,7 +88,7 @@ export class JestAllure2Reporter extends JestMetadataReporter { onTestFileStart(test: Test) { super.onTestFileStart(test); - const testFileMetadata = query.test(test); + const testFileMetadata = JestAllure2Reporter.query.test(test); const threadId = this._threadService.allocateThread(test.path); testFileMetadata.set(WORKER_ID, String(1 + threadId)); testFileMetadata.set(START, Date.now()); @@ -95,7 +97,8 @@ export class JestAllure2Reporter extends JestMetadataReporter { onTestCaseResult(test: Test, testCaseResult: TestCaseResult) { const now = Date.now(); super.onTestCaseResult(test, testCaseResult); - const metadata = query.testCaseResult(testCaseResult).lastInvocation!; + const metadata = + JestAllure2Reporter.query.testCaseResult(testCaseResult).lastInvocation!; const stop = metadata.get(STOP, Number.NaN); if (Number.isNaN(stop)) { metadata.set(STOP, now); @@ -109,7 +112,7 @@ export class JestAllure2Reporter extends JestMetadataReporter { ) { this._threadService.freeThread(test.path); - const testFileMetadata = query.test(test); + const testFileMetadata = JestAllure2Reporter.query.test(test); testFileMetadata.set(STOP, Date.now()); return super.onTestFileResult(test, testResult, aggregatedResult); @@ -143,7 +146,7 @@ export class JestAllure2Reporter extends JestMetadataReporter { const categories = config.categories(globalContext); if (categories) { - this._allure.writeCategoriesDefinitions(categories); + this._allure.writeCategoriesDefinitions(categories as Category[]); } const squasher = new MetadataSquasher(); @@ -165,7 +168,9 @@ export class JestAllure2Reporter extends JestMetadataReporter { const testFileContext: TestFileExtractorContext = { ...beforeTestFileContext, - testFileMetadata: squasher.testFile(query.testResult(testResult)), + testFileMetadata: squasher.testFile( + JestAllure2Reporter.query.testResult(testResult), + ), }; await this._callPlugins('testFileContext', testFileContext); @@ -182,9 +187,9 @@ export class JestAllure2Reporter extends JestMetadataReporter { fullName: config.testFile.fullName(testFileContext), description: config.testFile.description(testFileContext), descriptionHtml: config.testFile.descriptionHtml(testFileContext), - status: config.testFile.status(testFileContext), + status: config.testFile.status(testFileContext) as string as Status, statusDetails: config.testFile.statusDetails(testFileContext), - stage: config.testFile.stage(testFileContext), + stage: config.testFile.stage(testFileContext) as string as Stage, links: config.testFile.links(testFileContext), labels: config.testFile.labels(testFileContext), parameters: config.testFile.parameters(testFileContext), @@ -195,7 +200,8 @@ export class JestAllure2Reporter extends JestMetadataReporter { for (const testCaseResult of testResult.testResults) { const allInvocations = - query.testCaseResult(testCaseResult).invocations ?? []; + JestAllure2Reporter.query.testCaseResult(testCaseResult) + .invocations ?? []; for (const testInvocationMetadata of allInvocations) { const testCaseMetadata = squasher.testInvocation( @@ -223,9 +229,11 @@ export class JestAllure2Reporter extends JestMetadataReporter { fullName: config.testCase.fullName(testCaseContext), description: config.testCase.description(testCaseContext), descriptionHtml: config.testCase.descriptionHtml(testCaseContext), - status: config.testCase.status(testCaseContext), + status: config.testCase.status( + testCaseContext, + ) as string as Status, statusDetails: config.testCase.statusDetails(testCaseContext), - stage: config.testCase.stage(testCaseContext), + stage: config.testCase.stage(testCaseContext) as string as Stage, links: config.testCase.links(testCaseContext), labels: config.testCase.labels(testCaseContext), parameters: config.testCase.parameters(testCaseContext), @@ -361,9 +369,12 @@ export class JestAllure2Reporter extends JestMetadataReporter { executable.name = customize.name(testStepContext) ?? executable.name; executable.wrappedItem.start = customize.start(testStepContext); executable.wrappedItem.stop = customize.stop(testStepContext); - executable.stage = customize.stage(testStepContext) ?? executable.stage; + executable.stage = + (customize.stage(testStepContext) as string as Stage) ?? + executable.stage; executable.status = - customize.status(testStepContext) ?? executable.status; + (customize.status(testStepContext) as string as Status) ?? + executable.status; executable.statusDetails = customize.statusDetails(testStepContext) ?? {}; executable.wrappedItem.attachments = customize diff --git a/src/reporter/index.ts b/src/reporter/index.ts new file mode 100644 index 0000000..74e6eae --- /dev/null +++ b/src/reporter/index.ts @@ -0,0 +1 @@ +export * from './JestAllure2Reporter'; diff --git a/src/runtime/AllureRuntime.ts b/src/runtime/AllureRuntime.ts index ff2df28..a90f636 100644 --- a/src/runtime/AllureRuntime.ts +++ b/src/runtime/AllureRuntime.ts @@ -1,20 +1,19 @@ import path from 'node:path'; +import type { Metadata } from 'jest-metadata'; import type { + AllureTestStepMetadata, LabelName, - ParameterOptions, + Stage, + Status, StatusDetails, -} from '@noomorph/allure-js-commons'; -import { Stage, Status } from '@noomorph/allure-js-commons'; -import type { Metadata } from 'jest-metadata'; +} from 'jest-allure2-reporter'; + import type { - AllureTestStepMetadata, AttachmentContent, AttachmentOptions, - IAllureRuntime, ParameterOrString, -} from 'jest-allure2-reporter'; - +} from '../runtime'; import { CURRENT_STEP, DESCRIPTION, @@ -31,6 +30,7 @@ import { processMaybePromise } from '../utils/processMaybePromise'; import { wrapFunction } from '../utils/wrapFunction'; import { formatString } from '../utils/formatString'; +import type { IAllureRuntime, ParameterOptions } from './IAllureRuntime'; import type { IAttachmentsHandler } from './AttachmentsHandler'; export type AllureRuntimeConfig = { @@ -73,7 +73,7 @@ export class AllureRuntime implements IAllureRuntime { this.#metadata.push(LABELS, [{ name, value }]); } - link(name: string, url: string, type?: string) { + link(url: string, name = url, type?: string) { this.#metadata.push(LINKS, [{ name, url, type }]); } @@ -151,20 +151,20 @@ export class AllureRuntime implements IAllureRuntime { result = function_(); if (isPromiseLike(result)) { - this.#updateStep(Stage.RUNNING); + this.#updateStep('running'); result.then( - () => end(Status.PASSED), + () => end('passed'), (error) => - end(Status.FAILED, { message: error.message, trace: error.stack }), + end('failed', { message: error.message, trace: error.stack }), ); } else { - end(Status.PASSED); + end('passed'); } return result; } catch (error: unknown) { - end(Status.FAILED, { + end('failed', { message: (error as Error).message, trace: (error as Error).stack, }); @@ -218,7 +218,7 @@ export class AllureRuntime implements IAllureRuntime { this.#metadata.push(this.#localPath('steps'), [ { name, - stage: Stage.SCHEDULED, + stage: 'scheduled', start: this.#now(), code: function_.toString(), }, @@ -231,7 +231,7 @@ export class AllureRuntime implements IAllureRuntime { const existing = this.#metadata.get(this.#localPath(), {} as any); this.#metadata.assign(this.#localPath(), { - stage: Stage.FINISHED, + stage: 'finished', status: existing.status ?? status, statusDetails: existing.statusDetails ?? statusDetails, stop: this.#now(), diff --git a/src/runtime/AttachmentsHandler.ts b/src/runtime/AttachmentsHandler.ts index 97a914d..7c54b47 100644 --- a/src/runtime/AttachmentsHandler.ts +++ b/src/runtime/AttachmentsHandler.ts @@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto'; import fs from 'node:fs'; import os from 'node:os'; -import type { SharedReporterConfig } from 'jest-allure2-reporter'; +import type { SharedReporterConfig } from './SharedReporterConfig'; export interface IAttachmentsHandler { placeAttachment(name: string, content?: Buffer | string): string; @@ -27,6 +27,7 @@ export class AttachmentsHandler implements IAttachmentsHandler { getSharedConfig() ?? { resultsDir: path.join(os.tmpdir(), 'jest_allure2_reporter'), overwrite: false, + injectGlobals: true, attachments: { subDir: 'attachments', fileHandler: 'ref', diff --git a/src/runtime/IAllureRuntime.ts b/src/runtime/IAllureRuntime.ts new file mode 100644 index 0000000..c80d7ec --- /dev/null +++ b/src/runtime/IAllureRuntime.ts @@ -0,0 +1,94 @@ +import type { + LabelName, + LinkType, + Parameter, + Status, + StatusDetails, +} from 'jest-allure2-reporter'; + +import type { Function_, MaybePromise } from '../utils/types'; + +export interface IAllureRuntime { + // TODO: hide this method + flush(): Promise; + + description(value: string): void; + + descriptionHtml(value: string): void; + + status(status: Status, statusDetails?: StatusDetails): void; + + statusDetails(statusDetails: StatusDetails): void; + + label(name: LabelName, value: string): void; + + link(url: string, name?: string, type?: LinkType | string): void; + + parameter(name: string, value: unknown, options?: ParameterOptions): void; + + parameters(parameters: Record): void; + + attachment( + name: string, + content: MaybePromise, + mimeType?: string, + ): typeof content; + + createAttachment< + T extends AttachmentContent, + F extends Function_>, + >( + function_: F, + name: string, + ): F; + createAttachment< + T extends AttachmentContent, + F extends Function_>, + >( + function_: F, + options: AttachmentOptions, + ): typeof function_; + + fileAttachment(filePath: string, name?: string): string; + fileAttachment(filePath: string, options?: AttachmentOptions): string; + fileAttachment( + filePathPromise: Promise, + name?: string, + ): Promise; + fileAttachment( + filePathPromise: Promise, + options?: AttachmentOptions, + ): Promise; + + createFileAttachment>>( + function_: F, + ): F; + createFileAttachment>>( + function_: F, + name: string, + ): F; + createFileAttachment>>( + function_: F, + options: AttachmentOptions, + ): F; + + createStep(name: string, function_: F): F; + createStep( + name: string, + arguments_: ParameterOrString[], + function_: F, + ): F; + + step(name: string, function_: () => T): T; +} + +export type AttachmentContent = Buffer | string; + +export type AttachmentOptions = { + name?: string; + mimeType?: string; +}; + +export type ParameterOrString = string | Omit; + +export type ParameterOptions = Pick; diff --git a/src/runtime/SharedReporterConfig.ts b/src/runtime/SharedReporterConfig.ts new file mode 100644 index 0000000..a96bbf1 --- /dev/null +++ b/src/runtime/SharedReporterConfig.ts @@ -0,0 +1,6 @@ +import type { ReporterConfig } from 'jest-allure2-reporter'; + +export type SharedReporterConfig = Pick< + ReporterConfig, + 'resultsDir' | 'overwrite' | 'attachments' | 'injectGlobals' +>; diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 61480b4..68b3165 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1 +1,10 @@ +export { + IAllureRuntime, + ParameterOrString, + AttachmentOptions, + AttachmentContent, +} from './IAllureRuntime'; + export * from './AllureRuntime'; +export * from './AttachmentsHandler'; +export * from './SharedReporterConfig';