Skip to content

Commit

Permalink
feat(allure-cypress): make fullName CWD-independent
Browse files Browse the repository at this point in the history
  • Loading branch information
delatrie committed Sep 16, 2024
1 parent 7cca6b1 commit cacf0eb
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 40 deletions.
4 changes: 2 additions & 2 deletions packages/allure-cypress/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export type CypressTestStartMessage = {
type: "cypress_test_start";
data: {
name: string;
fullName: string;
fullNameSuffix: string;
start: number;
labels: Label[];
};
Expand Down Expand Up @@ -161,7 +161,6 @@ export type CypressMessage =

export type SpecContext = {
specPath: string;
package: string;
test: string | undefined;
fixture: string | undefined;
commandSteps: string[];
Expand All @@ -183,6 +182,7 @@ export type AllureSpecState = {
};
initialized: boolean;
testPlan: TestPlanV1 | null | undefined;
projectDir?: string;
messages: CypressMessage[];
currentTest?: CypressTest;
};
Expand Down
27 changes: 18 additions & 9 deletions packages/allure-cypress/src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type Cypress from "cypress";
import path from "node:path";
import { ContentType, Stage, Status } from "allure-js-commons";
import type { TestResult } from "allure-js-commons";
import type { RuntimeMessage } from "allure-js-commons/sdk";
Expand All @@ -11,6 +10,9 @@ import {
getHostLabel,
getLanguageLabel,
getPackageLabel,
getPosixPath,
getProjectRoot,
getRelativePath,
getSuiteLabels,
getThreadLabel,
parseTestPlan,
Expand Down Expand Up @@ -281,15 +283,20 @@ export class AllureCypress {
}
};

#startTest = (context: SpecContext, { data }: CypressTestStartMessage) => {
#startTest = (context: SpecContext, { data: { fullNameSuffix, ...testResultData } }: CypressTestStartMessage) => {
this.#emitPreviousTestScope(context);
const testScope = this.allureRuntime.startScope();
context.testScope = testScope;
context.test = this.#addNewTestResult(context, data, [context.videoScope, ...context.suiteScopes, testScope]);
context.test = this.#addNewTestResult(context, fullNameSuffix, testResultData, [
context.videoScope,
...context.suiteScopes,
testScope,
]);
};

#addNewTestResult = (
context: SpecContext,
fullNameSuffix: string,
{ labels: metadataLabels = [], ...otherTestData }: Partial<TestResult>,
scopes: string[],
) =>
Expand All @@ -299,14 +306,14 @@ export class AllureCypress {
labels: [
getLanguageLabel(),
getFrameworkLabel("cypress"),

...getSuiteLabels(context.suiteNames),
...metadataLabels,
...getEnvironmentLabels(),
getHostLabel(),
getThreadLabel(),
getPackageLabel(context.specPath),
],
fullName: `${getPosixPath(context.specPath)}#${fullNameSuffix}`,
...otherTestData,
},
scopes,
Expand Down Expand Up @@ -351,13 +358,13 @@ export class AllureCypress {

#addSkippedTest = (
context: SpecContext,
{ data: { suites, duration, retries, ...testResultData } }: CypressSkippedTestMessage,
{ data: { fullNameSuffix, suites, duration, retries, ...testResultData } }: CypressSkippedTestMessage,
) => {
// Tests skipped because of a hook error may share all suites of the current context
// or just a part thereof (if it's from a sibling suite).
const scopes = suites.map((s) => context.suiteIdToScope.get(s)).filter((s): s is string => Boolean(s));

const testUuid = this.#addNewTestResult(context, testResultData, [context.videoScope, ...scopes]);
const testUuid = this.#addNewTestResult(context, fullNameSuffix, testResultData, [context.videoScope, ...scopes]);
this.#stopExistingTestResult(testUuid, { duration, retries });
this.allureRuntime.writeTest(testUuid);
};
Expand Down Expand Up @@ -470,10 +477,9 @@ export class AllureCypress {
};

#initializeSpecContext = (absolutePath: string) => {
const specPath = path.relative(process.cwd(), absolutePath);
const specPath = getRelativePath(absolutePath);
const context: SpecContext = {
specPath,
package: specPath.replaceAll("/", "."),
test: undefined,
fixture: undefined,
commandSteps: [],
Expand All @@ -494,6 +500,7 @@ const createRuntimeState = (allureConfig?: AllureCypressConfig): AllureSpecState
initialized: false,
messages: [],
testPlan: parseTestPlan(),
projectDir: getProjectRoot(),
});

const getRuntimeConfigDefaults = ({
Expand Down Expand Up @@ -544,10 +551,12 @@ export const allureCypress = (
allureConfig = cypressConfig as AllureCypressConfig;
}

const hasCypressConfig = cypressConfig && "env" in cypressConfig;

const allureCypressReporter = new AllureCypress(allureConfig);
allureCypressReporter.attachToCypress(on);

if (cypressConfig && "env" in cypressConfig) {
if (hasCypressConfig) {
initializeRuntimeState(cypressConfig, allureConfig);
}

Expand Down
3 changes: 3 additions & 0 deletions packages/allure-cypress/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const getAllureState = () => {
messages: [],
testPlan: undefined,
currentTest: undefined,
projectDir: undefined,
};
Cypress.env("allure", state);
}
Expand All @@ -34,6 +35,8 @@ export const enqueueRuntimeMessage = (message: CypressMessage) => {

export const getAllureTestPlan = () => getAllureState().testPlan;

export const getProjectDir = () => getAllureState().projectDir;

export const getCurrentTest = () => getAllureState().currentTest;

export const setCurrentTest = (test: CypressTest) => {
Expand Down
28 changes: 20 additions & 8 deletions packages/allure-cypress/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { extractMetadataFromString, getMessageAndTraceFromError, getStatusFromEr
import type { TestPlanV1 } from "allure-js-commons/sdk";
import { ALLURE_REPORT_STEP_COMMAND, ALLURE_REPORT_SYSTEM_HOOK } from "./model.js";
import type { CypressCommand, CypressHook, CypressSuite, CypressTest } from "./model.js";
import { getAllureTestPlan } from "./state.js";
import { getAllureTestPlan, getProjectDir } from "./state.js";

export const DEFAULT_RUNTIME_CONFIG = {
stepsFromCommands: {
Expand Down Expand Up @@ -74,14 +74,19 @@ export const last = <T = unknown>(arr: T[]): T | undefined => {
return arr[arr.length - 1];
};

const resolveSpecRelativePath = (spec: Cypress.Spec) => {
const projectDir = getProjectDir();
const specPath = projectDir ? spec.absolute.substring(projectDir.length + 1) : spec.relative;
const win = Cypress.platform === "win32";
return win ? specPath.replaceAll("\\", "/") : specPath;
};

export const getNamesAndLabels = (spec: Cypress.Spec, test: CypressTest) => {
const rawName = test.title;
const { cleanTitle: name, labels } = extractMetadataFromString(rawName);
const suites = test.titlePath().slice(0, -1);
const win = Cypress.platform === "win32";
const specPath = win ? spec.relative.replaceAll("\\", "/") : spec.relative;
const fullName = `${specPath}#${[...suites, name].join(" ")}`;
return { name, labels, fullName };
const fullNameSuffix = `${[...suites, name].join(" ")}`;
return { name, labels, fullNameSuffix };
};

export const getTestStartData = (test: CypressTest) => ({
Expand All @@ -101,8 +106,9 @@ export const getTestSkipData = () => ({
export const applyTestPlan = (spec: Cypress.Spec, root: CypressSuite) => {
const testPlan = getAllureTestPlan();
if (testPlan) {
const specPath = resolveSpecRelativePath(spec);
for (const suite of iterateSuites(root)) {
const indicesToRemove = getIndicesOfDeselectedTests(testPlan, spec, suite.tests);
const indicesToRemove = getIndicesOfDeselectedTests(testPlan, spec, specPath, suite.tests);
removeSortedIndices(suite.tests, indicesToRemove);
}
}
Expand Down Expand Up @@ -150,10 +156,16 @@ export const isRootAfterAllHook = (hook: CypressHook) => hook.parent!.root && ho
const includedInTestPlan = (testPlan: TestPlanV1, fullName: string, allureId: string | undefined): boolean =>
testPlan.tests.some((test) => (allureId && test.id?.toString() === allureId) || test.selector === fullName);

const getIndicesOfDeselectedTests = (testPlan: TestPlanV1, spec: Cypress.Spec, tests: readonly CypressTest[]) => {
const getIndicesOfDeselectedTests = (
testPlan: TestPlanV1,
spec: Cypress.Spec,
specPath: string,
tests: readonly CypressTest[],
) => {
const indicesToRemove: number[] = [];
tests.forEach((test, index) => {
const { fullName, labels } = getNamesAndLabels(spec, test);
const { fullNameSuffix, labels } = getNamesAndLabels(spec, test);
const fullName = `${specPath}#${fullNameSuffix}`;
const allureId = labels.find(({ name }) => name === LabelName.ALLURE_ID)?.value;

if (!includedInTestPlan(testPlan, fullName, allureId)) {
Expand Down
69 changes: 69 additions & 0 deletions packages/allure-cypress/test/spec/cwd.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { TestPlanV1 } from "allure-js-commons/sdk";
import { runCypressInlineTest } from "../utils.js";

describe("fullName, and package", () => {
describe("with testplan", () => {
it("should not depend on a CWD", async () => {
const testPlan: TestPlanV1 = {
version: "1.0",
tests: [{ selector: "cypress/e2e/sample.cy.js#foo" }],
};
const { tests } = await runCypressInlineTest(
{
"cypress/e2e/sample.cy.js": () => `
it("foo", () => {});
`,
"testplan.json": () => JSON.stringify(testPlan),
},
{
cwd: "cypress",
env: (testDir) => ({ ALLURE_TESTPLAN_PATH: path.join(testDir, "testplan.json") }),
},
);

expect(tests).toHaveLength(1);
expect(tests[0].labels).toEqual(expect.arrayContaining([{ name: "package", value: "cypress.e2e.sample.cy.js" }]));
expect(tests[0].fullName).toEqual("cypress/e2e/sample.cy.js#foo");
});
});

describe("when no Cypress config provided", () => {
it("should not depend on a CWD", async () => {
const { tests } = await runCypressInlineTest(
{
"cypress.config.js": ({ allureCypressReporterModulePath, supportFilePath, specPattern, allureDirPath }) => `
const { allureCypress } = require("${allureCypressReporterModulePath}");
module.exports = {
e2e: {
baseUrl: "https://allurereport.org",
supportFile: "${supportFilePath}",
specPattern: "${specPattern}",
viewportWidth: 1240,
setupNodeEvents: (on, config) => {
allureCypress(on, {
resultsDir: "${allureDirPath}",
});
return config;
},
},
};
`,
"cypress/e2e/sample.cy.js": () => `
it("foo", () => {});
`,
},
{
cwd: "cypress",
},
);

expect(tests).toHaveLength(1);
expect(tests[0].labels).toEqual(expect.arrayContaining([{ name: "package", value: "cypress.e2e.sample.cy.js" }]));
expect(tests[0].fullName).toEqual("cypress/e2e/sample.cy.js#foo");
});
});
});
10 changes: 6 additions & 4 deletions packages/allure-cypress/test/spec/labels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ it("should add labels from env variables", async () => {
});
`,
},
() => ({
ALLURE_LABEL_A: "a",
ALLURE_LABEL_B: "b",
}),
{
env: () => ({
ALLURE_LABEL_A: "a",
ALLURE_LABEL_B: "b",
}),
},
);

expect(tests).toHaveLength(1);
Expand Down
24 changes: 15 additions & 9 deletions packages/allure-cypress/test/spec/testplan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ it("respects testplan", async () => {
});
`,
},
(testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
{
env: (testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
},
);

expect(tests).toHaveLength(3);
Expand Down Expand Up @@ -121,9 +123,11 @@ it("should deselect all tests not in test plan", async () => {
it('test 2', () => {});
`,
},
(testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
{
env: (testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
},
);

expect(tests).toEqual([]);
Expand Down Expand Up @@ -156,9 +160,11 @@ it("should not trigger hooks", async () => {
it("bar", () => {});
`,
},
(testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
{
env: (testDir) => ({
ALLURE_TESTPLAN_PATH: join(testDir, testPlanFilename),
}),
},
);

expect(tests).toEqual([
Expand Down
Loading

0 comments on commit cacf0eb

Please sign in to comment.