Skip to content

Commit

Permalink
Merge branch 'main' into renovate/cypress-14.x
Browse files Browse the repository at this point in the history
  • Loading branch information
epszaw authored Jan 28, 2025
2 parents 41f3778 + ecad277 commit b8f33c1
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 23 deletions.
18 changes: 15 additions & 3 deletions packages/allure-cypress/src/browser/commandLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const setupScreenshotAttachmentStep = (originalName: string | undefined,

export const startCommandLogStep = (entry: CypressLogEntry) => {
const currentLogEntry = getCurrentLogEntry();

if (typeof currentLogEntry !== "undefined" && shouldStopCurrentLogStep(currentLogEntry.log, entry)) {
stopCommandLogStep(currentLogEntry.log.attributes.id);
}
Expand All @@ -65,12 +66,14 @@ export const stopCommandLogStep = (entryId: string) => findAndStopStepWithSubste
const pushLogEntry = (entry: CypressLogEntry) => {
const id = entry.attributes.id;
const stepDescriptor: LogStepDescriptor = { id, type: "log", log: entry };

pushStep(stepDescriptor);

// Some properties of some Command Log entries are undefined at the time the entry is stopped. An example is the
// Yielded property of some queries. We defer converting them to Allure step parameters until the test/hook ends.
setupStepFinalization(stepDescriptor, (data) => {
data.parameters = getCommandLogStepParameters(entry);

if (stepDescriptor.attachmentName) {
// Rename the step to match the attachment name. Once the names are the same, Allure will render the
// attachment in the place of the step.
Expand Down Expand Up @@ -146,18 +149,27 @@ const getLogProps = (entry: CypressLogEntry) => {
attributes: { consoleProps },
} = entry;
const isAssertionWithMessage = !!maybeGetAssertionLogMessage(entry);
const { props, name } = consoleProps();

// accessing LocalStorage after the page reload can stick the test runner
// to avoid the issue, we just need to log the command manually
// the problem potentially can happen with other storage related commands, like `clearAllLocalStorage`, `clearAllSessionStorage`, `getAllLocalStorage`, `getAllSessionStorage`, `setLocalStorage`, `setSessionStorage`
// but probably, we don't need to silent them all at this moment
// the context: https://github.com/allure-framework/allure-js/issues/1222
if (["clearLocalStorage"].includes(name)) {
return [] as [string, unknown][];
}

// For assertion logs, we interpolate the 'Message' property, which contains unformatted assertion description,
// directly into the step's name.
// No need to keep the exact same information in the step's parameters.
return Object.entries(consoleProps().props).filter(
([k, v]) => isDefined(v) && !(isAssertionWithMessage && k === "Message"),
);
return Object.entries(props).filter(([k, v]) => isDefined(v) && !(isAssertionWithMessage && k === "Message"));
};

const maybeGetAssertionLogMessage = (entry: CypressLogEntry) => {
if (isAssertLog(entry)) {
const message = entry.attributes.consoleProps().props.Message;

if (message && typeof message === "string") {
return message;
}
Expand Down
1 change: 1 addition & 0 deletions packages/allure-cypress/src/browser/events/cypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const onAfterScreenshot = (
...[, { name: originalName, path }]: Parameters<Cypress.ScreenshotDefaultsOptions["onAfterScreenshot"]>
) => {
const name = originalName ?? getFileNameFromPath(path);

reportScreenshot(path, name);
setupScreenshotAttachmentStep(originalName, name);
};
Expand Down
3 changes: 3 additions & 0 deletions packages/allure-cypress/src/browser/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DEFAULT_RUNTIME_CONFIG, last, toReversed } from "../utils.js";

export const getAllureState = () => {
let state = Cypress.env("allure") as AllureSpecState;

if (!state) {
state = {
config: DEFAULT_RUNTIME_CONFIG,
Expand All @@ -15,8 +16,10 @@ export const getAllureState = () => {
stepsToFinalize: [],
nextApiStepId: 0,
};

Cypress.env("allure", state);
}

return state;
};

Expand Down
5 changes: 4 additions & 1 deletion packages/allure-cypress/src/browser/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export const uint8ArrayToBase64 = (data: unknown) => {

export const getTestStartData = (test: CypressTest) => ({
...getNamesAndLabels(Cypress.spec, test),
start: test.wallClockStartedAt?.getTime() || Date.now(),
start:
typeof test.wallClockStartedAt === "string"
? Date.parse(test.wallClockStartedAt)
: test.wallClockStartedAt?.getTime?.() || Date.now(),
});

export const getTestStopData = (test: CypressTest) => ({
Expand Down
21 changes: 21 additions & 0 deletions packages/allure-cypress/test/spec/security.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, it } from "vitest";
import { Stage, Status } from "allure-js-commons";
import { runCypressInlineTest } from "../utils.js";

it("shouldn't break the flow when access storage after the page reload", async () => {
const { tests } = await runCypressInlineTest({
"cypress/e2e/sample.cy.js": () => `
it("passed", () => {
cy.visit("https://allurereport.org");
cy.clearLocalStorage();
cy.wait(200);
cy.reload();
cy.wait(200);
});
`,
});

expect(tests).toHaveLength(1);
expect(tests[0].status).toBe(Status.PASSED);
expect(tests[0].stage).toBe(Stage.FINISHED);
});
92 changes: 73 additions & 19 deletions packages/allure-js-commons/src/sdk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ export const stripAnsi = (str: string): string => {
return str.replace(regex, "");
};

export const getMessageAndTraceFromError = (error: Error | { message?: string; stack?: string }): StatusDetails => {
export const getMessageAndTraceFromError = (
error:
| Error
| {
message?: string;
stack?: string;
},
): StatusDetails => {
const { message, stack } = error;
const actual = "actual" in error && error.actual !== undefined ? { actual: serialize(error.actual) } : {};
const expected = "expected" in error && error.expected !== undefined ? { expected: serialize(error.expected) } : {};
Expand All @@ -56,13 +63,40 @@ export const getMessageAndTraceFromError = (error: Error | { message?: string; s
};
};

export const allureIdRegexp = /(?:^|\s)@?allure\.id[:=](?<id>[^\s]+)/;
export const allureIdRegexpGlobal = new RegExp(allureIdRegexp, "g");
type AllureTitleMetadataMatch = RegExpMatchArray & {
groups: {
type?: string;
v1?: string;
v2?: string;
v3?: string;
v4?: string;
};
};

export const allureTitleMetadataRegexp = /(?:^|\s)@?allure\.(?<type>\S+)[:=]("[^"]+"|'[^']+'|`[^`]+`|\S+)/;
export const allureTitleMetadataRegexpGlobal = new RegExp(allureTitleMetadataRegexp, "g");
export const allureIdRegexp = /(?:^|\s)@?allure\.id[:=](?<id>\S+)/;
export const allureLabelRegexp = /(?:^|\s)@?allure\.label\.(?<name>[^:=\s]+)[:=](?<value>[^\s]+)/;
export const allureLabelRegexpGlobal = new RegExp(allureLabelRegexp, "g");

export const getTypeFromAllureTitleMetadataMatch = (match: AllureTitleMetadataMatch) => {
return match?.[1];
};

export const getValueFromAllureTitleMetadataMatch = (match: AllureTitleMetadataMatch) => {
const quotesRegexp = /['"`]/;
const quoteOpenRegexp = new RegExp(`^${quotesRegexp.source}`);
const quoteCloseRegexp = new RegExp(`${quotesRegexp.source}$`);
const matchedValue = match?.[2] ?? "";

if (quoteOpenRegexp.test(matchedValue) && quoteCloseRegexp.test(matchedValue)) {
return matchedValue.slice(1, -1);
}

return matchedValue;
};

export const isMetadataTag = (tag: string) => {
return allureIdRegexp.test(tag) || allureLabelRegexp.test(tag);
return allureTitleMetadataRegexp.test(tag);
};

export const extractMetadataFromString = (
Expand All @@ -72,25 +106,45 @@ export const extractMetadataFromString = (
cleanTitle: string;
} => {
const labels = [] as Label[];

title.split(" ").forEach((val) => {
const idValue = val.match(allureIdRegexp)?.groups?.id;

if (idValue) {
labels.push({ name: LabelName.ALLURE_ID, value: idValue });
const metadata = title.matchAll(allureTitleMetadataRegexpGlobal);
const cleanTitle = title
.replaceAll(allureTitleMetadataRegexpGlobal, "")
.split(" ")
.filter(Boolean)
.reduce((acc, word) => {
if (/^[\n\r]/.test(word)) {
return acc + word;
}

return `${acc} ${word}`;
}, "")
.trim();

for (const m of metadata) {
const match = m as AllureTitleMetadataMatch;
const type = getTypeFromAllureTitleMetadataMatch(match);
const value = getValueFromAllureTitleMetadataMatch(match);

if (!type || !value) {
continue;
}

const labelMatch = val.match(allureLabelRegexp);
const { name, value } = labelMatch?.groups || {};
const [subtype, name] = type.split(".");

if (name && value) {
labels?.push({ name, value });
switch (subtype) {
case "id":
labels.push({ name: LabelName.ALLURE_ID, value });
break;
case "label":
labels.push({ name, value });
break;
}
});

const cleanTitle = title.replace(allureLabelRegexpGlobal, "").replace(allureIdRegexpGlobal, "").trim();
}

return { labels, cleanTitle };
return {
labels,
cleanTitle,
};
};

export const isAnyStepFailed = (item: StepResult | TestResult | FixtureResult): boolean => {
Expand Down
43 changes: 43 additions & 0 deletions packages/allure-js-commons/test/sdk/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,49 @@ describe("extractMetadataFromString", () => {
],
});
});

it("should support values in single quotes", () => {
expect(
extractMetadataFromString("foo @allure.label.l1='foo bar baz' and bar @allure.id=beep @allure.label.l1=boop"),
).toEqual({
cleanTitle: "foo and bar",
labels: [
{ name: "l1", value: "foo bar baz" },
{ name: LabelName.ALLURE_ID, value: "beep" },
{ name: "l1", value: "boop" },
],
});
});

it("should support values in double quotes", () => {
expect(extractMetadataFromString('foo @allure.label.l1="foo bar baz"')).toEqual({
cleanTitle: "foo",
labels: [{ name: "l1", value: "foo bar baz" }],
});
});

it("should support values in backticks", () => {
expect(extractMetadataFromString("foo @allure.label.l1=`foo bar baz`")).toEqual({
cleanTitle: "foo",
labels: [{ name: "l1", value: "foo bar baz" }],
});
});

it("should support mixed values at the same time", () => {
expect(
extractMetadataFromString(
"foo @allure.label.l1=foo @allure.label.l1=`foo 1` bar @allure.label.l1='foo 2' baz @allure.label.l1=\"foo 3\"",
),
).toEqual({
cleanTitle: "foo bar baz",
labels: [
{ name: "l1", value: "foo" },
{ name: "l1", value: "foo 1" },
{ name: "l1", value: "foo 2" },
{ name: "l1", value: "foo 3" },
],
});
});
});

describe("isMetadataTag", () => {
Expand Down

0 comments on commit b8f33c1

Please sign in to comment.