Skip to content

Commit

Permalink
make utility to create trees based on labels more abstract
Browse files Browse the repository at this point in the history
add retry flag to the awesome test result
fix retry awesome filter
  • Loading branch information
epszaw committed Dec 23, 2024
1 parent 3e0745c commit 52972d4
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 97 deletions.
80 changes: 80 additions & 0 deletions packages/e2e/test/allure-awesome/tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,86 @@ test.describe("commons", () => {
});
});

test.describe("filters", () => {
test.describe("retry", () => {
test.beforeAll(async () => {
bootstrap = await boostrapReport({
reportConfig: {
name: "Sample allure report",
appendHistory: false,
history: undefined,
historyPath: undefined,
knownIssuesPath: undefined,
},
testResults: [
{
name: "0 sample test",
fullName: "sample.js#0 sample test",
historyId: "foo",
status: Status.FAILED,
stage: Stage.FINISHED,
start: 0,
statusDetails: {
message: "Assertion error: Expected 1 to be 2",
trace: "failed test trace",
},
},
{
name: "0 sample test",
fullName: "sample.js#0 sample test",
historyId: "foo",
status: Status.FAILED,
stage: Stage.FINISHED,
start: 1000,
statusDetails: {
message: "Assertion error: Expected 1 to be 2",
trace: "failed test trace",
},
},
{
name: "0 sample test",
fullName: "sample.js#0 sample test",
historyId: "foo",
status: Status.PASSED,
stage: Stage.FINISHED,
start: 2000,
},
{
name: "1 sample test",
fullName: "sample.js#1 sample test",
historyId: "bar",
status: Status.PASSED,
stage: Stage.FINISHED,
start: 3000,
},
{
name: "2 sample test",
fullName: "sample.js#2 sample test",
historyId: "baz",
status: Status.PASSED,
stage: Stage.FINISHED,
start: 4000,
},
],
});
});

test("shows only tests with retries", async ({ page }) => {
const treeLeaves = page.getByTestId("tree-leaf");

await expect(treeLeaves).toHaveCount(3);
await page.getByTestId("filters-button").click();
await page.getByTestId("retry-filter").click();

await expect(treeLeaves).toHaveCount(1);

await page.getByTestId("retry-filter").click();

await expect(treeLeaves).toHaveCount(3);
});
});
});

test.describe("suites", () => {
test.beforeAll(async () => {
bootstrap = await boostrapReport({
Expand Down
54 changes: 34 additions & 20 deletions packages/plugin-api/src/utils/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type WithChildren,
findByLabelName,
} from "@allurereport/core-api";
import { emptyStatistic, incrementStatistic } from "@allurereport/core-api";
import { emptyStatistic } from "@allurereport/core-api";
import { md5 } from "./misc.js";

const addLeaf = (node: WithChildren, nodeId: string) => {
Expand Down Expand Up @@ -108,26 +108,40 @@ export const filterTreeLabels = (data: TestResult[], labelNames: string[]) => {
.reverse();
};

export const createTreeByLabels = (data: TestResult[], labelNames: string[]) => {
return createTree<TestResult, DefaultTreeLeaf, DefaultTreeGroup>(
export const createTreeByLabels = <T = TestResult, L = DefaultTreeLeaf, G = DefaultTreeGroup>(
data: T[],
labelNames: string[],
leafFactory?: (item: T) => TreeLeaf<L>,
groupFactory?: (parentGroup: string | undefined, groupClassifier: string) => TreeGroup<G>,
addLeafToGroup: (group: TreeGroup<G>, leaf: TreeLeaf<L>) => void = () => {},
) => {
const leafFactoryFn =
leafFactory ??
((tr: T) => {
const { id, name, status, duration } = tr as TestResult;

return {
nodeId: id,
name,
status,
duration,
} as unknown as TreeLeaf<L>;
});
const groupFactoryFn =
groupFactory ??
((parentId, groupClassifier) =>
({
nodeId: md5((parentId ? `${parentId}.` : "") + groupClassifier),
name: groupClassifier,
statistic: emptyStatistic(),
}) as unknown as TreeGroup<G>);

return createTree<T, L, G>(
data,
(item) => byLabels(item, labelNames),
({ id, name, status, duration, flaky, start }) => ({
nodeId: id,
name,
status,
duration,
flaky,
start,
}),
(parentId, groupClassifier) => ({
nodeId: md5((parentId ? `${parentId}.` : "") + groupClassifier),
name: groupClassifier,
statistic: emptyStatistic(),
}),
(group, leaf) => {
incrementStatistic(group.statistic, leaf.status);
},
(item) => byLabels(item as TestResult, labelNames),
leafFactoryFn,
groupFactoryFn,
addLeafToGroup,
);
};

Expand Down
15 changes: 11 additions & 4 deletions packages/plugin-api/test/tree.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TestResult, TreeData, compareBy, nullsLast, ordinal } from "@allurereport/core-api";
import { type TestResult, type TreeData, compareBy, nullsLast, ordinal } from "@allurereport/core-api";
import { randomUUID } from "node:crypto";
import { describe, expect, it } from "vitest";
import { createTreeByLabels, filterTree, filterTreeLabels, sortTree, transformTree } from "../src/index.js";
Expand Down Expand Up @@ -39,6 +39,13 @@ const sampleTree = {
g2: { nodeId: "g2", name: "2", groups: [], leaves: ["l5", "l6"] },
},
};
const sampleLeafFactory = (tr: TestResult) => ({
nodeId: tr.id,
name: tr.name,
status: tr.status,
duration: tr.duration,
flaky: tr.flaky,
});

describe("tree builder", () => {
it("should create empty tree", async () => {
Expand All @@ -54,7 +61,7 @@ describe("tree builder", () => {
it("should create tree without groups", async () => {
const tr1 = itResult({ name: "first" });
const tr2 = itResult({ name: "second" });
const treeByLabels = createTreeByLabels([tr1, tr2], []);
const treeByLabels = createTreeByLabels([tr1, tr2], [], sampleLeafFactory);

expect(treeByLabels.root.groups).toHaveLength(0);
expect(treeByLabels.root.leaves).toContain(tr1.id);
Expand Down Expand Up @@ -92,7 +99,7 @@ describe("tree builder", () => {
{ name: "story", value: "A" },
],
});
const treeByLabels = createTreeByLabels([tr1, tr2, tr3], ["feature"]);
const treeByLabels = createTreeByLabels([tr1, tr2, tr3], ["feature"], sampleLeafFactory);

expect(treeByLabels.root.groups).toHaveLength(2);
const rootGroup1 = treeByLabels.root.groups![0];
Expand Down Expand Up @@ -152,7 +159,7 @@ describe("tree builder", () => {
{ name: "story", value: "A" },
],
});
const treeByLabels = createTreeByLabels([tr1, tr2, tr3], ["feature"]);
const treeByLabels = createTreeByLabels([tr1, tr2, tr3], ["feature"], sampleLeafFactory);

expect(treeByLabels.root.leaves).toHaveLength(1);
expect(treeByLabels.root.leaves).toContain(tr2.id);
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-awesome/src/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const convertTestResult = (tr: TestResult): AllureAwesomeTestResult => {
history: [],
retries: [],
breadcrumbs: [],
retry: false,
};
};

Expand Down
29 changes: 26 additions & 3 deletions packages/plugin-awesome/src/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
type AttachmentLink,
type EnvironmentItem,
type Statistic,
type TestResult,
compareBy,
incrementStatistic,
nullsLast,
ordinal,
} from "@allurereport/core-api";
Expand All @@ -13,6 +13,8 @@ import type {
AllureAwesomeFixtureResult,
AllureAwesomeReportOptions,
AllureAwesomeTestResult,
AllureAwesomeTreeGroup,
AllureAwesomeTreeLeaf,
} from "@allurereport/web-awesome";
import {
createBaseUrlScript,
Expand Down Expand Up @@ -118,6 +120,7 @@ export const generateTestResults = async (writer: AllureAwesomeDataWriter, store

convertedTr.history = await store.historyByTrId(tr.id);
convertedTr.retries = await store.retriesByTrId(tr.id);
convertedTr.retry = convertedTr.retries.length > 0;
convertedTr.setup = convertedTrFixtures.filter((f) => f.type === "before");
convertedTr.teardown = convertedTrFixtures.filter((f) => f.type === "after");
// FIXME: the type is correct, but typescript still shows an error
Expand All @@ -144,16 +147,36 @@ export const generateTestResults = async (writer: AllureAwesomeDataWriter, store
"nav.json",
convertedTrs.filter(({ hidden }) => !hidden).map(({ id }) => id),
);

return convertedTrs;
};

export const generateTree = async (
writer: AllureAwesomeDataWriter,
treeName: string,
labels: string[],
tests: TestResult[],
tests: AllureAwesomeTestResult[],
) => {
const visibleTests = tests.filter((test) => !test.hidden);
const tree = createTreeByLabels(visibleTests, labels);
const tree = createTreeByLabels<AllureAwesomeTestResult, AllureAwesomeTreeLeaf, AllureAwesomeTreeGroup>(
visibleTests,
labels,
({ id, name, status, duration, flaky, start, retries }) => {
return {
nodeId: id,
retry: !!retries?.length,
name,
status,
duration,
flaky,
start,
};
},
undefined,
(group, leaf) => {
incrementStatistic(group.statistic, leaf.status);
},
);

// @ts-ignore
filterTree(tree, (leaf) => !leaf.hidden);
Expand Down
8 changes: 4 additions & 4 deletions packages/plugin-awesome/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ export class AllureAwesomePlugin implements Plugin {
const { singleFile, groupBy } = this.options ?? {};
const environmentItems = await store.metadataByKey<EnvironmentItem[]>("allure_environment");
const statistic = await store.testsStatistic();
const allTr = await store.allTestResults({ includeHidden: true });
const attachments = await store.allAttachments();

await generateStatistic(this.#writer!, statistic);
await generatePieChart(this.#writer!, statistic);

const convertedTrs = await generateTestResults(this.#writer!, store);

await generateTree(
this.#writer!,
"tree",
groupBy?.length ? groupBy : ["parentSuite", "suite", "subSuite"],
allTr,
convertedTrs,
);

await generateTestResults(this.#writer!, store);
await generateHistoryDataPoints(this.#writer!, store);

if (environmentItems?.length) {
Expand Down
4 changes: 1 addition & 3 deletions packages/sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
"type": "module",
"scripts": {
"pret": "rimraf ./allure-results",
"t": "vitest run",
"prereport": "rimraf ./allure-report",
"report": "yarn allure generate ./allure-results"
"test": "yarn allure run -- vitest run"
},
"devDependencies": {
"@allurereport/plugin-csv": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const Filters = () => {
size="m"
style="outline"
isActive={isOpened}
data-testid="filters-button"
onClick={onClick}
/>
</div>
Expand All @@ -43,6 +44,7 @@ export const Filters = () => {
focusable={false}
value={flaky}
label={t("enable-filter", { filter: t("flaky") })}
data-testid="flaky-filter"
onChange={(value) => setTreeFilter("flaky", value)}
/>
</div>
Expand All @@ -61,6 +63,7 @@ export const Filters = () => {
focusable={false}
value={retry}
label={t("enable-filter", { filter: t("retry") })}
data-testid="retry-filter"
onChange={(value) => setTreeFilter("retry", value)}
/>
</div>
Expand All @@ -79,6 +82,7 @@ export const Filters = () => {
focusable={false}
value={isNew}
label={t("enable-filter", { filter: t("new") })}
data-testid="new-filter"
onChange={(value) => setTreeFilter("new", value)}
/>
</div>
Expand Down
3 changes: 2 additions & 1 deletion packages/web-awesome/src/components/commons/Toggle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Props = {
};

export const Toggle = (props: Props) => {
const { value, label, onChange, focusable = true } = props;
const { value, label, onChange, focusable = true, ...rest } = props;

const handleChange = (e: Event) => {
const newValue = !(e.target as HTMLInputElement).checked;
Expand All @@ -17,6 +17,7 @@ export const Toggle = (props: Props) => {

return (
<input
{...rest}
tabIndex={focusable ? 0 : -1}
className={styles.toggle}
role="switch"
Expand Down
7 changes: 5 additions & 2 deletions packages/web-awesome/src/utils/treeFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const filterLeaves = (
const statusMatched =
!filterOptions?.status || filterOptions?.status === "total" || leaf.status === filterOptions.status;
const flakyMatched = !filterOptions?.filter?.flaky || leaf.flaky;
const retryMatched = !filterOptions?.filter?.retry || leaf?.retries?.length > 0;
const retryMatched = !filterOptions?.filter?.retry || leaf.retry;
// TODO: at this moment we don't have a new field implementation even in the generator
// const newMatched = !filterOptions?.filter?.new || leaf.new;

Expand Down Expand Up @@ -66,10 +66,13 @@ export const createRecursiveTree = (payload: {
filterOptions?: TreeFiltersState;
}): AllureAwesomeRecursiveTree => {
const { group, groupsById, leavesById, filterOptions } = payload;
const groupLeaves = group.leaves ?? [];

return {
...group,
leaves: filterLeaves(group.leaves, leavesById, filterOptions),
// FIXME: don't have any idea, why eslint marks next line as unsafe because it actually has a correct type
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
leaves: filterLeaves(groupLeaves, leavesById, filterOptions),
trees: group?.groups
?.filter((groupId) => {
const subGroup = groupsById[groupId];
Expand Down
Loading

0 comments on commit 52972d4

Please sign in to comment.