Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions packages/allure-jest/src/environmentFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import type { Circus } from "@jest/types";
import * as allure from "allure-js-commons";
import { Stage, Status, type StatusDetails, type TestResult } from "allure-js-commons";
import { type RuntimeMessage, type TestPlanV1, serialize } from "allure-js-commons/sdk";
import { extractMetadataFromString, getMessageAndTraceFromError, getStatusFromError } from "allure-js-commons/sdk";
import {
extractMetadataFromString,
getMessageAndTraceFromError,
getStatusFromError,
isPromise,
} from "allure-js-commons/sdk";
import {
ReporterRuntime,
createDefaultWriter,
Expand Down Expand Up @@ -45,6 +50,30 @@ const createJestEnvironment = <T extends typeof JestEnvironment>(Base: T): T =>
constructor(config: AllureJestConfig | AllureJestProjectConfig, context: EnvironmentContext) {
super(config as JestEnvironmentConfig, context);

const handleTestEvent = this.handleTestEvent?.bind(this) as
| ((event: Circus.Event, state: Circus.State) => void | PromiseLike<void>)
| undefined;

// Preserve any event handler defined by the base or custom environment.
this.handleTestEvent = (event: Circus.Event, state: Circus.State) => {
const handleAllureEvent = () => this.#handleTestEvent(event, state);

// Keep Allure's lifecycle in sync even if the custom environment fails,
// then rethrow the original error so Jest keeps its native behavior.
try {
const result = handleTestEvent?.(event, state);

if (isPromise(result)) {
return Promise.resolve(result).finally(handleAllureEvent);
}
} catch (error) {
handleAllureEvent();
throw error;
}

handleAllureEvent();
};

const projectConfig = "projectConfig" in config ? config.projectConfig : config;
const { resultsDir, ...restConfig } = projectConfig?.testEnvironmentOptions || {};

Expand Down Expand Up @@ -81,7 +110,7 @@ const createJestEnvironment = <T extends typeof JestEnvironment>(Base: T): T =>
}
}

handleTestEvent = (event: Circus.Event) => {
#handleTestEvent(event: Circus.Event, state: Circus.State) {
switch (event.name) {
case "hook_start":
this.#handleHookStart(event.hook);
Expand Down Expand Up @@ -125,7 +154,7 @@ const createJestEnvironment = <T extends typeof JestEnvironment>(Base: T): T =>
default:
break;
}
};
}

#getTestFullName(test: Circus.TestEntry, testTitle: string = test.name) {
const newTestSuitePath = getTestPath(test.parent);
Expand Down
92 changes: 92 additions & 0 deletions packages/allure-jest/test/spec/simple.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,95 @@ it("should set full name", async () => {
]),
);
});

it("preserves async handleTestEvent from a custom environment", async () => {
const { tests } = await runJestInlineTest({
"jest.config.js": () => `
const config = {
bail: false,
testEnvironment: "./custom-environment.js",
};

module.exports = config;
`,
"custom-environment.js": ({ allureJestFactoryPath }) => `
const { TestEnvironment } = require("jest-environment-node");
const { createJestEnvironment } = require("${allureJestFactoryPath}");

class CustomEnvironment extends TestEnvironment {
async handleTestEvent(event) {
if (event.name === "test_fn_failure") {
this.global.__failedTestNames = [...(this.global.__failedTestNames ?? []), event.test.name];
}
}
}

module.exports = createJestEnvironment(CustomEnvironment);
`,
"sample.spec.js": `
it("fails", () => {
throw new Error("boom");
});

it("observes the custom environment event", () => {
expect(global.__failedTestNames).toEqual(["fails"]);
});
`,
});

expect(tests).toHaveLength(2);
expect(tests).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "fails",
status: Status.BROKEN,
}),
expect.objectContaining({
name: "observes the custom environment event",
status: Status.PASSED,
}),
]),
);
});

it("preserves sync handleTestEvent from a custom environment", async () => {
const { tests } = await runJestInlineTest({
"jest.config.js": () => `
const config = {
bail: false,
testEnvironment: "./custom-environment.js",
};

module.exports = config;
`,
"custom-environment.js": ({ allureJestFactoryPath }) => `
const { TestEnvironment } = require("jest-environment-node");
const { createJestEnvironment } = require("${allureJestFactoryPath}");

class CustomEnvironment extends TestEnvironment {
handleTestEvent(event) {
if (event.name === "test_start") {
this.global.__startedTestNames = [...(this.global.__startedTestNames ?? []), event.test.name];
}
}
}

module.exports = createJestEnvironment(CustomEnvironment);
`,
"sample.spec.js": `
it("runs first", () => {
expect(global.__startedTestNames).toEqual(["runs first"]);
});
`,
});

expect(tests).toHaveLength(1);
expect(tests).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "runs first",
status: Status.PASSED,
}),
]),
);
});
5 changes: 4 additions & 1 deletion packages/allure-jest/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { attachment, step } from "allure-js-commons";
import type { AllureResults } from "allure-js-commons/sdk";
import { MessageReader, getPosixPath } from "allure-js-commons/sdk/reporter";

type TestFileWriter = (opts: { allureJestNodePath: string }) => string;
type TestFileWriter = (opts: { allureJestFactoryPath: string; allureJestNodePath: string }) => string;

type TestFiles = Record<string, string | TestFileWriter>;

Expand All @@ -31,7 +31,9 @@ export const runJestInlineTest = async (
const testDir = join(__dirname, "fixtures", randomUUID());
const configFileName = "jest.config.js";
const configFilePath = join(testDir, configFileName);
const allureJestFactory = require.resolve("allure-jest/factory");
const allureJestNode = require.resolve("allure-jest/node");
const allureJestFactoryPath = getPosixPath(relative(testDir, allureJestFactory));
const allureJestNodePath = getPosixPath(relative(testDir, allureJestNode));
const testFilesToWrite: TestFiles = {
[configFileName]: `
Expand Down Expand Up @@ -77,6 +79,7 @@ export const runJestInlineTest = async (
testFileContent = testFilesToWrite[testFile] as string;
} else {
testFileContent = (testFilesToWrite[testFile] as TestFileWriter)({
allureJestFactoryPath,
allureJestNodePath,
});
}
Expand Down
Loading