Skip to content

Commit aad2fbe

Browse files
committed
fix ui issues
1 parent d4715f7 commit aad2fbe

48 files changed

Lines changed: 883 additions & 64 deletions

Some content is hidden

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

packages/plugin-awesome/src/generators.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ import {
4545
} from "@allurereport/plugin-api";
4646
import type {
4747
AwesomeCategory,
48+
AwesomeExecutorInfo,
4849
AwesomeFixtureResult,
4950
AwesomeReportOptions,
51+
AwesomeRunSummary,
5052
AwesomeSearchDocument,
5153
AwesomeTestResult,
5254
AwesomeTreeGroup,
@@ -273,6 +275,22 @@ export const generateSearchIndex = async (
273275
await writer.writeWidget(filename, searchDocuments);
274276
};
275277

278+
export const getRunSummary = (testResults: Pick<TestResult, "start" | "stop">[]): AwesomeRunSummary | undefined => {
279+
let start = Infinity;
280+
let stop = -Infinity;
281+
282+
for (const { start: s, stop: e } of testResults) {
283+
if (typeof s === "number" && Number.isFinite(s) && typeof e === "number" && Number.isFinite(e)) {
284+
start = Math.min(start, s);
285+
stop = Math.max(stop, e);
286+
}
287+
}
288+
289+
return Number.isFinite(start) && Number.isFinite(stop)
290+
? { start, stop, duration: Math.max(0, stop - start) }
291+
: undefined;
292+
};
293+
276294
export const generateTree = async (
277295
writer: AwesomeDataWriter,
278296
treeFilename: string,
@@ -609,6 +627,8 @@ export const generateStaticFiles = async (
609627
reportDataFiles: ReportFile[];
610628
reportUuid: string;
611629
reportName: string;
630+
executor?: AwesomeExecutorInfo;
631+
runSummary?: AwesomeRunSummary;
612632
},
613633
) => {
614634
const {
@@ -626,6 +646,8 @@ export const generateStaticFiles = async (
626646
layout = "base",
627647
defaultSection = "",
628648
ci,
649+
executor,
650+
runSummary,
629651
stepTreeExpansion,
630652
} = payload;
631653
const manifest = await readTemplateManifest(payload.singleFile);
@@ -685,6 +707,8 @@ export const generateStaticFiles = async (
685707
groupBy: groupBy?.length ? groupBy : [],
686708
cacheKey: now.toString(),
687709
ci,
710+
executor,
711+
runSummary,
688712
layout,
689713
allureVersion,
690714
sections,

packages/plugin-awesome/src/plugin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
createPluginSummary,
88
} from "@allurereport/plugin-api";
99
import { preciseTreeLabels } from "@allurereport/plugin-api";
10+
import type { AwesomeExecutorInfo } from "@allurereport/web-awesome";
1011

1112
import { applyCategoriesToTestResults, generateCategories } from "./categories.js";
1213
import { generateTimeline } from "./generateTimeline.js";
@@ -28,6 +29,7 @@ import {
2829
generateTree,
2930
generateTreeFilters,
3031
generateVariables,
32+
getRunSummary,
3133
} from "./generators.js";
3234
import type { AwesomePluginOptions } from "./model.js";
3335
import { type AwesomeDataWriter, InMemoryReportDataWriter, ReportFileDataWriter } from "./writer.js";
@@ -72,8 +74,10 @@ export class AwesomePlugin implements Plugin {
7274
const hideLabels = context.hideLabels;
7375
const categories = context.categories ?? [];
7476
const environmentItems = await store.metadataByKey<EnvironmentItem[]>("allure_environment");
77+
const executor = await store.metadataByKey<AwesomeExecutorInfo>("allure2_executor");
7578
const attachments = await store.allAttachments();
7679
const allTrs = await store.allTestResults({ includeRetries: true, filter });
80+
const runSummary = getRunSummary(allTrs);
7781
const statistics = await store.testsStatistic(filter);
7882
const environments = await store.allEnvironmentIdentities();
7983
const envStatistics = new Map<string, Statistic>();
@@ -197,6 +201,8 @@ export class AwesomePlugin implements Plugin {
197201
reportUuid: context.reportUuid,
198202
reportName: context.reportName,
199203
ci: context.ci,
204+
executor,
205+
runSummary,
200206
reportDataFiles,
201207
});
202208
};

packages/plugin-awesome/test/generators.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
generateAttachmentsFiles,
1313
generateGlobals,
1414
generateSearchIndex,
15+
getRunSummary,
1516
} from "../src/generators.js";
1617

1718
beforeEach(async () => {
@@ -37,6 +38,35 @@ const getTestResultsStats = (trs: TestResult[], filter: (tr: TestResult) => bool
3738
);
3839
};
3940

41+
describe("getRunSummary", () => {
42+
it("should return undefined for empty result input", () => {
43+
expect(getRunSummary([])).toBeUndefined();
44+
});
45+
46+
it("should derive launch interval from valid result timings", () => {
47+
expect(
48+
getRunSummary([
49+
{ start: 1000, stop: 2000 },
50+
{ start: 500, stop: 2500 },
51+
{ start: 1500, stop: 1800 },
52+
]),
53+
).toEqual({
54+
start: 500,
55+
stop: 2500,
56+
duration: 2000,
57+
});
58+
});
59+
60+
it("should ignore results without valid timing data", () => {
61+
expect(
62+
getRunSummary([
63+
{ start: undefined, stop: 2000 },
64+
{ start: 1000, stop: undefined },
65+
]),
66+
).toBeUndefined();
67+
});
68+
});
69+
4070
const mockTestResult = (id: string, name: string, status: TestResult["status"]): TestResult =>
4171
({
4272
id,

packages/plugin-awesome/test/plugin.test.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -696,9 +696,9 @@ describe("plugin", () => {
696696
});
697697

698698
describe("report assets", () => {
699-
const makeSingleFileStore = (testResults: TestResult[]): AllureStore =>
699+
const makeSingleFileStore = (testResults: TestResult[], metadata: Record<string, unknown> = {}): AllureStore =>
700700
({
701-
metadataByKey: vi.fn().mockResolvedValue(undefined),
701+
metadataByKey: vi.fn(async (key: string) => metadata[key]),
702702
allEnvironments: vi.fn().mockResolvedValue(["default"]),
703703
allEnvironmentIdentities: vi
704704
.fn()
@@ -759,6 +759,14 @@ describe("plugin", () => {
759759
return data;
760760
};
761761

762+
const extractReportOptions = (html: string) => {
763+
const match = html.match(/window\.allureReportOptions = (\{.*?\})\s*<\/script>/s);
764+
765+
expect(match, "index.html must include report options").not.toBeNull();
766+
767+
return JSON.parse(match![1]);
768+
};
769+
762770
it("should copy every emitted multi-file asset", async () => {
763771
const addedFiles = new Map<string, Buffer>();
764772
const reportFiles: ReportFiles = {
@@ -866,5 +874,60 @@ describe("plugin", () => {
866874
// data test results file for the test must be present
867875
expect(Object.keys(embeddedData).some((k) => k.startsWith("data/test-results/"))).toBe(true);
868876
});
877+
878+
it("should include launch timing and allure2 executor metadata in report options", async () => {
879+
const testResults: TestResult[] = [
880+
{
881+
id: "tr-1",
882+
name: "passed test",
883+
status: "passed",
884+
environment: "default",
885+
start: 1000,
886+
stop: 2000,
887+
labels: [],
888+
},
889+
{
890+
id: "tr-retry",
891+
name: "failed retry",
892+
status: "failed",
893+
environment: "default",
894+
isRetry: true,
895+
start: 500,
896+
stop: 2500,
897+
labels: [],
898+
},
899+
] as TestResult[];
900+
const executor = {
901+
name: "TeamCity",
902+
type: "teamcity",
903+
buildName: "Wrike #123",
904+
buildUrl: "https://teamcity.example/build/123",
905+
reportUrl: "https://teamcity.example/report/123",
906+
};
907+
const addedFiles = new Map<string, Buffer>();
908+
const reportFiles: ReportFiles = {
909+
addFile: vi.fn(async (path: string, data: Buffer) => {
910+
addedFiles.set(path, data);
911+
return path;
912+
}),
913+
};
914+
const plugin = new AwesomePlugin({ singleFile: true });
915+
916+
await plugin.start(makeSingleFileContext(reportFiles));
917+
await plugin.done(
918+
makeSingleFileContext(reportFiles),
919+
makeSingleFileStore(testResults, { allure2_executor: executor }),
920+
);
921+
922+
const indexHtml = addedFiles.get("index.html")?.toString("utf-8") ?? "";
923+
const reportOptions = extractReportOptions(indexHtml);
924+
925+
expect(reportOptions.runSummary).toEqual({
926+
start: 500,
927+
stop: 2500,
928+
duration: 2000,
929+
});
930+
expect(reportOptions.executor).toEqual(executor);
931+
});
869932
});
870933
});

packages/web-awesome/src/components/Footer/FooterVersion.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { Text } from "@allurereport/web-components";
33
import { useState } from "preact/hooks";
44
import type { AwesomeReportOptions } from "types";
55

6-
import { currentLocaleIso } from "@/stores";
6+
import { useI18n } from "@/stores";
7+
import { timestampToDate } from "@/utils/time";
78

89
import * as styles from "./styles.scss";
910

1011
export const FooterVersion = () => {
12+
const { t } = useI18n("ui");
1113
const [createdAt] = useState(() => {
1214
const reportOptions = getReportOptions<AwesomeReportOptions>();
1315
if (reportOptions?.createdAt) {
@@ -27,18 +29,11 @@ export const FooterVersion = () => {
2729
return undefined;
2830
});
2931

30-
const formattedCreatedAt = new Date(createdAt as number).toLocaleDateString(currentLocaleIso.value as string, {
31-
month: "numeric",
32-
day: "numeric",
33-
year: "numeric",
34-
hour: "numeric",
35-
minute: "numeric",
36-
second: "numeric",
37-
});
32+
const formattedCreatedAt = timestampToDate(createdAt as number);
3833

3934
return (
4035
<Text type="paragraph" size="m" className={styles.version}>
41-
{formattedCreatedAt}
36+
{t("generated")} {formattedCreatedAt}
4237
{currentVersion && <span> Ver: {currentVersion}</span>}
4338
</Text>
4439
);
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import { LanguagePicker } from "@allurereport/web-components";
12
import type { ClassValue } from "clsx";
23
import { clsx } from "clsx";
34

45
import { FooterLogo } from "@/components/Footer/FooterLogo";
56
import { FooterVersion } from "@/components/Footer/FooterVersion";
7+
import { currentLocale, setLocale } from "@/stores/locale";
68

79
import * as styles from "@/components/BaseLayout/styles.scss";
10+
import * as footerStyles from "@/components/Footer/styles.scss";
811

912
interface FooterProps {
1013
className?: ClassValue;
@@ -13,7 +16,10 @@ export const Footer = ({ className }: FooterProps) => {
1316
return (
1417
<div className={clsx(styles.below, className)}>
1518
<FooterLogo />
16-
<FooterVersion />
19+
<div className={footerStyles["footer-controls"]}>
20+
<LanguagePicker locale={currentLocale.value} setLocale={setLocale} />
21+
<FooterVersion />
22+
</div>
1723
</div>
1824
);
1925
};

packages/web-awesome/src/components/Footer/styles.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@
1212
.version {
1313
color: var(--color-text-muted);
1414
}
15+
16+
.footer-controls {
17+
display: flex;
18+
align-items: center;
19+
gap: 8px;
20+
}

packages/web-awesome/src/components/Header/CiInfo/index.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface CiInfoProps {
1313
}
1414

1515
interface CiIconProps {
16-
type: CiDescriptor["type"];
16+
type?: CiDescriptor["type"];
1717
}
1818

1919
export const CiIcon = ({ type }: CiIconProps) => {
@@ -47,35 +47,39 @@ export const CiIcon = ({ type }: CiIconProps) => {
4747
export const CiInfo = ({ className }: CiInfoProps) => {
4848
const { ci } = getReportOptions<AwesomeReportOptions>();
4949

50-
if (!ci) {
51-
return null;
52-
}
50+
const ciLink = ci ? ci.pullRequestUrl || ci.jobRunUrl || ci.jobUrl : undefined;
51+
const ciLabel = getCiLabel(ci, ciLink);
52+
const safeLink = sanitizeExternalUrl(ciLink);
5353

54-
const link = ci.pullRequestUrl || ci.jobRunUrl || ci.jobUrl;
55-
const safeLink = sanitizeExternalUrl(link);
56-
const label = ci.pullRequestName || ci.jobRunName || ci.jobName || link;
57-
58-
if (!link) {
54+
if (!ciLabel) {
5955
return null;
6056
}
6157

6258
if (!safeLink) {
6359
return (
6460
<span className={clsx(styles["ci-info"], className)}>
65-
<CiIcon type={ci.type} />
61+
<CiIcon type={ci?.type} />
6662
<Text type="paragraph" size="m" bold>
67-
{label}
63+
{ciLabel}
6864
</Text>
6965
</span>
7066
);
7167
}
7268

7369
return (
7470
<a className={clsx(styles["ci-info"], className)} href={safeLink} target="_blank" rel="noopener noreferrer">
75-
<CiIcon type={ci.type} />
71+
<CiIcon type={ci?.type} />
7672
<Text type="paragraph" size="m" bold>
77-
{label}
73+
{ciLabel}
7874
</Text>
7975
</a>
8076
);
8177
};
78+
79+
const getCiLabel = (ci?: CiDescriptor, link?: string) => {
80+
if (!ci) {
81+
return undefined;
82+
}
83+
84+
return ci.pullRequestName || ci.jobRunName || ci.jobName || link;
85+
};

packages/web-awesome/src/components/HeaderControls/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { themeStore, toggleUserTheme } from "@allurereport/web-commons";
2-
import { LanguagePicker, ThemeButton } from "@allurereport/web-components";
2+
import { ThemeButton } from "@allurereport/web-components";
33
import { computed } from "@preact/signals";
44

55
import { EnvironmentPicker } from "@/components/EnvironmentPicker";
66
import ToggleLayout from "@/components/ToggleLayout";
7-
import { currentLocale, setLocale } from "@/stores/locale";
87

98
interface HeaderControlsProps {
109
className?: string;
@@ -16,7 +15,6 @@ export const HeaderControls = ({ className }: HeaderControlsProps) => {
1615
return (
1716
<div className={className}>
1817
<EnvironmentPicker />
19-
<LanguagePicker locale={currentLocale.value} setLocale={setLocale} />
2018
<ToggleLayout />
2119
<ThemeButton theme={selectedTheme.value} toggleTheme={toggleUserTheme} />
2220
</div>

0 commit comments

Comments
 (0)