Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add retry filter #46

Merged
merged 1 commit into from
Dec 23, 2024
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
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
Loading