Skip to content

Commit 24de585

Browse files
committed
feat(awesome): indicate retry status changes separately
1 parent d311c78 commit 24de585

53 files changed

Lines changed: 539 additions & 24 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎packages/core-api/src/aggregate.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type Statistic = DiffStatistic & {
1313
unknown?: number;
1414
total: number;
1515
retries?: number;
16+
retriesStatusChange?: number;
1617
flaky?: number;
1718
resolutions?: {
1819
issues?: number;

‎packages/core-api/src/categories.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ export type CategoryNodeItem = {
143143
value?: string;
144144
historyId?: string;
145145
retriesCount?: number;
146+
retriesStatusChange?: boolean;
146147
transition?: TestStatusTransition;
147148
tooltips?: Record<string, string>;
148149
statistic?: Statistic;

‎packages/core-api/src/utils/status.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import type { TestStatus } from "../model.js";
1+
import type { TestResult, TestStatus } from "../model.js";
2+
3+
const retryStatusChangeStatuses = new Set<TestStatus>(["failed", "broken", "passed"]);
24

35
export const StatusByPriority: TestStatus[] = ["failed", "broken", "passed", "skipped", "unknown"];
46

@@ -17,3 +19,10 @@ export const getWorstStatus = (items: TestStatus[]): TestStatus | undefined => {
1719

1820
return items.sort((a, b) => statusToPriority(a) - statusToPriority(b))[0];
1921
};
22+
23+
export const hasRetriesStatusChange = (
24+
current: Pick<TestResult, "status">,
25+
retries: readonly Pick<TestResult, "status">[],
26+
): boolean =>
27+
retryStatusChangeStatuses.has(current.status) &&
28+
retries.some(({ status }) => retryStatusChangeStatuses.has(status) && status !== current.status);

‎packages/core-api/test/utils/status.test.ts‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { epic, feature, label, story } from "allure-js-commons";
22
import { beforeEach, describe, expect, it } from "vitest";
33

44
import type { TestStatus } from "../../src/model.js";
5-
import { getWorstStatus, statusToPriority } from "../../src/utils/status.js";
5+
import { getWorstStatus, hasRetriesStatusChange, statusToPriority } from "../../src/utils/status.js";
66

77
beforeEach(async () => {
88
await epic("coverage");
@@ -46,3 +46,31 @@ describe("getWorstStatus", () => {
4646
expect(result).toBeUndefined();
4747
});
4848
});
49+
50+
describe("hasRetriesStatusChange", () => {
51+
const significantStatuses = ["passed", "failed", "broken"] as const satisfies readonly TestStatus[];
52+
53+
it.each([
54+
...significantStatuses.flatMap((currentStatus) =>
55+
significantStatuses.map(
56+
(retryStatus) =>
57+
[currentStatus, [retryStatus], retryStatus !== currentStatus] satisfies [TestStatus, TestStatus[], boolean],
58+
),
59+
),
60+
["passed", ["unknown", "skipped"], false],
61+
["unknown", ["passed", "failed"], false],
62+
["passed", [], false],
63+
["passed", ["unknown", "failed", "passed"], true],
64+
["passed", ["failed", "passed", "broken", "failed"], true],
65+
] satisfies [TestStatus, TestStatus[], boolean][])(
66+
"detects change for current status %s and retry statuses %j: %s",
67+
(currentStatus, retryStatuses, expected) => {
68+
expect(
69+
hasRetriesStatusChange(
70+
{ status: currentStatus },
71+
retryStatuses.map((status) => ({ status })),
72+
),
73+
).toBe(expected);
74+
},
75+
);
76+
});

‎packages/core/src/store/store.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
createDictionary,
3333
getHistoryIdCandidates,
3434
getWorstStatus,
35+
hasRetriesStatusChange,
3536
normalizeHistoryDataPoint,
3637
ordinal,
3738
reverse,
@@ -1418,6 +1419,10 @@ export class DefaultAllureStore implements AllureStore, ResultsVisitor {
14181419
statistic.retries = (statistic.retries ?? 0) + 1;
14191420
}
14201421

1422+
if (hasRetriesStatusChange(tr, retries)) {
1423+
statistic.retriesStatusChange = (statistic.retriesStatusChange ?? 0) + 1;
1424+
}
1425+
14211426
if (tr.flaky) {
14221427
statistic.flaky = (statistic.flaky ?? 0) + 1;
14231428
}

‎packages/core/test/store/store.test.ts‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,37 @@ describe("test results", () => {
873873
);
874874
});
875875

876+
it("should count retried tests whose significant status changed independently from all retried tests", async () => {
877+
const store = new DefaultAllureStore();
878+
879+
await store.visitTestResult({ name: "changed", status: "failed", testId: "tc-changed", start: 100 }, { readerId });
880+
await store.visitTestResult({ name: "changed", status: "passed", testId: "tc-changed", start: 200 }, { readerId });
881+
await store.visitTestResult(
882+
{ name: "unchanged", status: "failed", testId: "tc-unchanged", start: 100 },
883+
{ readerId },
884+
);
885+
await store.visitTestResult(
886+
{ name: "unchanged", status: "failed", testId: "tc-unchanged", start: 200 },
887+
{ readerId },
888+
);
889+
await store.visitTestResult({ name: "ignored", status: "skipped", testId: "tc-ignored", start: 100 }, { readerId });
890+
await store.visitTestResult({ name: "ignored", status: "passed", testId: "tc-ignored", start: 200 }, { readerId });
891+
892+
await expect(store.testsStatistic()).resolves.toMatchObject({
893+
total: 3,
894+
retries: 3,
895+
retriesStatusChange: 1,
896+
passed: 2,
897+
failed: 1,
898+
});
899+
await expect(store.testsStatistic((tr) => tr.status === "passed")).resolves.toMatchObject({
900+
total: 2,
901+
retries: 2,
902+
retriesStatusChange: 1,
903+
passed: 2,
904+
});
905+
});
906+
876907
it("should not group class-level fixture failures with AS_ID placeholder as retries", async () => {
877908
const store = new DefaultAllureStore();
878909
const testA: RawTestResult = {

‎packages/e2e/test/allure-awesome/features/retries.test.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,23 @@ test.describe("retries", () => {
8585
await expect(treePage.leafLocator).toHaveCount(3);
8686
});
8787

88-
test("should show retry icon in the tree for tests with retries", async ({ page }) => {
88+
test("should show retry or status change icon in the tree for tests with retries", async ({ page }) => {
8989
await page.goto(bootstrap.url);
9090

9191
const retryIcons = page.getByTestId("tree-leaf-retries");
92+
const retriesStatusChangeIcons = page.getByTestId("tree-leaf-retries-status-change");
9293

93-
await expect(retryIcons).toHaveCount(2);
94+
await expect(retryIcons).toHaveCount(1);
95+
await expect(retriesStatusChangeIcons).toHaveCount(1);
9496

95-
const firstTestWithRetriesIcon = treePage.getLeafByTitle(firstTestWithRetries).getByTestId("tree-leaf-retries");
97+
const firstTestWithRetriesIcon = treePage
98+
.getLeafByTitle(firstTestWithRetries)
99+
.getByTestId("tree-leaf-retries-status-change");
96100
const anotherTestWithRetriesIcon = treePage
97101
.getLeafByTitle(secondTestWithRetriesName)
98102
.getByTestId("tree-leaf-retries");
99103

100-
await expect(firstTestWithRetriesIcon).toContainText("2");
104+
await expect(firstTestWithRetriesIcon).toBeVisible();
101105
await expect(anotherTestWithRetriesIcon).toContainText("1");
102106
});
103107

‎packages/plugin-allure2/src/converters.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
TestStatus,
77
TestStepResult,
88
} from "@allurereport/core-api";
9-
import { isStep, redactParameters } from "@allurereport/core-api";
9+
import { hasRetriesStatusChange, isStep, redactParameters } from "@allurereport/core-api";
1010

1111
import { matchCategories } from "./categories.js";
1212
import type {
@@ -240,9 +240,7 @@ export const convertTestResult = (context: ConvertContext, test: TestResult): Al
240240
},
241241
}));
242242

243-
const retriesStatusChange =
244-
isImportantStatus(status) &&
245-
retries.find((retry) => isImportantStatus(retry.status) && retry.status !== status) !== undefined;
243+
const retriesStatusChange = hasRetriesStatusChange(test, context.retries);
246244

247245
const historyItems: Allure2HistoryItem[] = context.legacyHistory
248246
? context.legacyHistory.items.map((item) => ({

‎packages/plugin-allure2/test/converters.test.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,26 @@ describe("convertTestResult", () => {
259259
},
260260
});
261261
});
262+
263+
it.each([
264+
["passed", ["passed", "passed"]],
265+
["passed", ["skipped", "unknown"]],
266+
["unknown", ["passed", "failed"]],
267+
] satisfies [TestResult["status"], TestResult["status"][]][])(
268+
"should not mark current status %s as changed for retries %j",
269+
(currentStatus, retryStatuses) => {
270+
const result = convertTestResult(
271+
{
272+
attachmentMap: new Map(),
273+
fixtures: [],
274+
categories: [],
275+
retries: retryStatuses.map((status, index) => createTestResult({ id: `retry-${index}`, status })),
276+
history: [],
277+
},
278+
createTestResult({ status: currentStatus }),
279+
);
280+
281+
expect(result.retriesStatusChange).toBe(false);
282+
},
283+
);
262284
});

‎packages/plugin-api/src/report.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export type ReportTestResult = Omit<
102102
history: HistoryTestResult[];
103103
retries?: TestResult[];
104104
retriesCount?: number;
105+
retriesStatusChange?: boolean;
105106
groupedLabels: Record<string, string[]>;
106107
attachments?: AttachmentTestStepResult[];
107108
breadcrumbs: ReportBreadcrumbItem[];
@@ -127,6 +128,7 @@ export type ReportTreeLeaf = Pick<
127128
| "transition"
128129
| "retry"
129130
| "retriesCount"
131+
| "retriesStatusChange"
130132
| "resolution"
131133
> & {
132134
nodeId: string;
@@ -177,6 +179,7 @@ export type ReportResolutionTestResult = Pick<
177179
| "transition"
178180
| "retry"
179181
| "retriesCount"
182+
| "retriesStatusChange"
180183
| "resolution"
181184
| "tooltips"
182185
> & {

0 commit comments

Comments
 (0)