Skip to content

Commit 6a057e2

Browse files
committed
PRO-12619 feat: full update of HTML report layout
1 parent b85c79c commit 6a057e2

33 files changed

Lines changed: 402 additions & 366 deletions

autotests/packs/allTests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const pack: Pack = {
8080
skipTests,
8181
takeFullPageScreenshotOnError: false,
8282
takeViewportScreenshotOnError: true,
83-
testFileGlobs: ['**/autotests/tests/**/*.ts'],
83+
testFileGlobs: ['**/autotests/tests/**/exists.ts'],
8484
testIdleTimeout: 8_000,
8585
testTimeout: 15_000,
8686
userAgent,

src/actions/asserts/assertUrlMatchRoute.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import {assertValueIsDefined, assertValueIsNotNull} from '../../utils/asserts';
44
import {log} from '../../utils/log';
55

66
import type {Route} from '../../Route';
7-
import type {Url} from '../../types/internal';
7+
import type {MaybePromise, Url} from '../../types/internal';
88

99
type MaybeUrlOrPath = Url | string | null | undefined;
1010

1111
/**
1212
* Asserts that url or url path (which can be wrapped in a promise) match route.
1313
*/
1414
export const assertUrlMatchRoute = async (
15-
maybeUrlOrPath: MaybeUrlOrPath | Promise<MaybeUrlOrPath>,
15+
maybeUrlOrPath: MaybePromise<MaybeUrlOrPath>,
1616
route: Route<unknown>,
1717
): Promise<void> => {
1818
const {routeParams} = route;

src/types/report.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import type {EndE2edReason, ExitCode, TestRunStatus} from '../constants/internal
55
import type {ApiStatistics} from './apiStatistics';
66
import type {FullPackConfig} from './config';
77
import type {UtcTimeInMs} from './date';
8-
import type {SafeHtml} from './html';
98
import type {TestFilePath} from './paths';
109
import type {StartInfo} from './startInfo';
1110
import type {FullTestRun, LiteTestRun, RunHash, RunId} from './testRun';
@@ -99,7 +98,7 @@ export type ReportClientState = {
9998
readonly fullTestRuns: readonly FullTestRun[];
10099
readonly internalDirectoryName: string;
101100
lengthOfReadedJsonReportDataParts: number;
102-
readonly locator: LocatorFunction<SafeHtml>;
101+
readonly locator: LocatorFunction;
103102
readonly pathToScreenshotsDirectoryForReport: string | null;
104103
readonly readJsonReportDataObservers: MutationObserver[];
105104
reportClientData?: ReportClientData;

src/utils/parse/parseValueAsJsonIfNeeded.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ type Return = Readonly<{hasParseError: boolean; value: unknown}>;
44

55
/**
66
* Parses `unknown` value as JSON, if needed.
7-
* If `isoValueInJsonFormat` is `true`, then parses value as JSON and saves parse error.
8-
* If `isoValueInJsonFormat` is `false`, then returns value as is.
9-
* If `isoValueInJsonFormat` is `undefined`, then safely tries to parse value as JSON.
7+
* If `isValueInJsonFormat` is `true`, then parses value as JSON and saves parse error.
8+
* If `isValueInJsonFormat` is `false`, then returns value as is.
9+
* If `isValueInJsonFormat` is `undefined`, then safely tries to parse value as JSON.
1010
*/
1111
export const parseValueAsJsonIfNeeded = (
1212
originalValue: unknown,
13-
isoValueInJsonFormat?: boolean,
13+
isValueInJsonFormat?: boolean,
1414
): Return => {
1515
let hasParseError = false;
1616
let value = originalValue;
1717

18-
if (isoValueInJsonFormat === true) {
18+
if (isValueInJsonFormat === true) {
1919
try {
2020
value = parseMaybeEmptyValueAsJson(originalValue);
2121
} catch {
2222
hasParseError = true;
2323
}
24-
} else if (isoValueInJsonFormat !== false) {
24+
} else if (isValueInJsonFormat !== false) {
2525
try {
2626
value = parseMaybeEmptyValueAsJson(originalValue);
2727
} catch {}
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import {assertValueIsDefined as clientAssertValueIsDefined} from './assertValueIsDefined';
2-
import {maybeRenderApiStatistics as clientMaybeRenderApiStatistics} from './maybeRenderApiStatistics';
3-
import {renderTestRunDetails as clientRenderTestRunDetails} from './render';
2+
import {
3+
MaybeApiStatistics as clientMaybeApiStatistics,
4+
TestRunDetails as clientTestRunDetails,
5+
} from './render';
46

5-
import type {ReportClientState, RunHash, SafeHtml} from '../../../types/internal';
7+
import type {ReportClientState, RunHash} from '../../../types/internal';
68

79
const assertValueIsDefined: typeof clientAssertValueIsDefined = clientAssertValueIsDefined;
8-
const maybeRenderApiStatistics = clientMaybeRenderApiStatistics;
9-
const renderTestRunDetails = clientRenderTestRunDetails;
10+
const MaybeApiStatistics = clientMaybeApiStatistics;
11+
const TestRunDetails = clientTestRunDetails;
1012

13+
declare const jsx: JSX.Runtime;
1114
declare const reportClientState: ReportClientState;
1215

1316
/**
@@ -62,9 +65,9 @@ export function chooseTestRun(runHash: RunHash): void {
6265
return;
6366
}
6467

65-
let rightColumnHtml: SafeHtml | undefined = maybeRenderApiStatistics(runHash);
68+
let rightColumnHtml = <MaybeApiStatistics runHash={runHash} />;
6669

67-
if (rightColumnHtml === undefined) {
70+
if (rightColumnHtml.length === 0) {
6871
const {fullTestRuns} = reportClientState;
6972
const fullTestRun = fullTestRuns.find((testRun) => testRun.runHash === runHash);
7073

@@ -77,7 +80,7 @@ export function chooseTestRun(runHash: RunHash): void {
7780
return;
7881
}
7982

80-
rightColumnHtml = renderTestRunDetails(fullTestRun);
83+
rightColumnHtml = <TestRunDetails fullTestRun={fullTestRun} />;
8184
}
8285

8386
e2edRightColumnContainer.innerHTML = String(rightColumnHtml);

src/utils/report/client/createJsxRuntime.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function createJsxRuntime(): JSX.Runtime {
3838
}
3939

4040
const attributesParts: readonly SafeHtml[] = Object.entries(properties).map(
41-
([key, value]) => sanitizeHtml`${key}="${value}"`,
41+
([key, value]) => sanitizeHtml`${key.toLowerCase()}="${value}"`,
4242
);
4343
const attributesHtml = createSafeHtmlWithoutSanitize`${attributesParts.join('')}`;
4444

src/utils/report/client/index.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ export {createJsxRuntime} from './createJsxRuntime';
1717
/** @internal */
1818
export {initialScript} from './initialScript';
1919
/** @internal */
20-
export {maybeRenderApiStatistics} from './maybeRenderApiStatistics';
21-
/** @internal */
2220
export {onDomContentLoad} from './onDomContentLoad';
2321
/** @internal */
2422
export {onFirstJsonReportDataLoad} from './onFirstJsonReportDataLoad';
@@ -30,17 +28,17 @@ export {readJsonReportData} from './readJsonReportData';
3028
export {readPartOfJsonReportData} from './readPartOfJsonReportData';
3129
/** @internal */
3230
export {
31+
ApiStatistics,
3332
ApiStatisticsItem,
34-
renderApiStatistics,
35-
renderAttributes,
36-
renderDatesInterval,
37-
renderDuration,
38-
renderStep,
39-
renderStepContent,
40-
renderSteps,
41-
renderTestRunDescription,
42-
renderTestRunDetails,
43-
renderTestRunError,
33+
DatesInterval,
34+
Duration,
35+
MaybeApiStatistics,
36+
Step,
37+
StepContent,
38+
Steps,
39+
TestRunDescription,
40+
TestRunDetails,
41+
TestRunError,
4442
} from './render';
4543
/** @internal */
4644
export {

src/utils/report/client/initialScript.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import {
2-
createSimpleLocator as clientCreateSimpleLocator,
3-
type LocatorFunction,
4-
} from 'create-locator';
1+
import {createSimpleLocator as clientCreateSimpleLocator} from 'create-locator';
52

63
import {addDomContentLoadedHandler as clientAddDomContentLoadedHandler} from './addDomContentLoadedHandler';
74
import {addOnClickOnClass as clientAddOnClickOnClass} from './addOnClickOnClass';
@@ -10,10 +7,9 @@ import {clickOnStep as clientClickOnStep} from './clickOnStep';
107
import {clickOnTestRun as clientClickOnTestRun} from './clickOnTestRun';
118
import {createJsxRuntime as clientCreateJsxRuntime} from './createJsxRuntime';
129
import {onDomContentLoad as clientOnDomContentLoad} from './onDomContentLoad';
13-
import {renderAttributes as clientRenderAttributes} from './render';
1410
import {setReadJsonReportDataObservers as clientSetReadJsonReportDataObservers} from './setReadJsonReportDataObservers';
1511

16-
import type {ReportClientState, SafeHtml} from '../../../types/internal';
12+
import type {ReportClientState} from '../../../types/internal';
1713

1814
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1915
declare let jsx: JSX.Runtime;
@@ -27,7 +23,6 @@ const clickOnTestRun = clientClickOnTestRun;
2723
const createJsxRuntime = clientCreateJsxRuntime;
2824
const createSimpleLocator = clientCreateSimpleLocator;
2925
const onDomContentLoad = clientOnDomContentLoad;
30-
const renderAttributes = clientRenderAttributes;
3126
const setReadJsonReportDataObservers = clientSetReadJsonReportDataObservers;
3227

3328
/**
@@ -38,9 +33,7 @@ const setReadJsonReportDataObservers = clientSetReadJsonReportDataObservers;
3833
export function initialScript(): void {
3934
jsx = createJsxRuntime();
4035

41-
const {locator: locatorAttributes} = createSimpleLocator(reportClientState.createLocatorOptions);
42-
const locator: LocatorFunction<SafeHtml> = (...args) =>
43-
renderAttributes(locatorAttributes(...(args as [string])));
36+
const {locator} = createSimpleLocator(reportClientState.createLocatorOptions);
4437

4538
Object.assign<ReportClientState, Partial<ReportClientState>>(reportClientState, {locator});
4639

src/utils/report/client/render/renderApiStatistics.ts renamed to src/utils/report/client/render/ApiStatistics.tsx

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {createSafeHtmlWithoutSanitize as clientCreateSafeHtmlWithoutSanitize} fr
33
import {ApiStatisticsItem as clientApiStatisticsItem} from './ApiStatisticsItem';
44

55
import type {
6-
ApiStatistics,
6+
ApiStatistics as ApiStatisticsType,
77
ApiStatisticsReportHash,
88
ObjectEntries,
99
SafeHtml,
@@ -12,8 +12,10 @@ import type {
1212
const createSafeHtmlWithoutSanitize = clientCreateSafeHtmlWithoutSanitize;
1313
const ApiStatisticsItem = clientApiStatisticsItem;
1414

15-
type Options = Readonly<{
16-
apiStatistics: ApiStatistics;
15+
declare const jsx: JSX.Runtime;
16+
17+
type Props = Readonly<{
18+
apiStatistics: ApiStatisticsType;
1719
hash: ApiStatisticsReportHash;
1820
}>;
1921

@@ -22,7 +24,7 @@ type Options = Readonly<{
2224
* This base client function should not use scope variables (except other base functions).
2325
* @internal
2426
*/
25-
export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
27+
export const ApiStatistics: JSX.Component<Props> = ({apiStatistics, hash}) => {
2628
let header: string | undefined;
2729
const items: SafeHtml[] = [];
2830

@@ -38,11 +40,13 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
3840
pageCount += count;
3941
pageDuration += duration;
4042

41-
pageItems.push(ApiStatisticsItem({count, duration, name: url, url}));
43+
pageItems.push(
44+
<ApiStatisticsItem count={count} duration={duration} name={url} url={url} />,
45+
);
4246
}
4347

4448
items.push(
45-
ApiStatisticsItem({count: pageCount, duration: pageDuration, isHeader: true, name}),
49+
<ApiStatisticsItem count={pageCount} duration={pageDuration} isHeader={true} name={name} />,
4650
);
4751
items.push(...pageItems);
4852
}
@@ -54,12 +58,12 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
5458
// eslint-disable-next-line max-depth
5559
for (const [statusCode, {count, duration, size}] of Object.entries(byStatusCode)) {
5660
items.push(
57-
ApiStatisticsItem({
58-
count,
59-
duration,
60-
name: `${method} ${url} ${statusCode}`,
61-
size,
62-
}),
61+
<ApiStatisticsItem
62+
count={count}
63+
duration={duration}
64+
name={`${method} ${url} ${statusCode}`}
65+
size={size}
66+
/>,
6367
);
6468
}
6569
}
@@ -72,23 +76,25 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
7276
>) {
7377
for (const [statusCode, {count, duration, size}] of Object.entries(byStatusCode)) {
7478
items.push(
75-
ApiStatisticsItem({
76-
count,
77-
duration,
78-
name: `${url} ${statusCode}`,
79-
size,
80-
url,
81-
}),
79+
<ApiStatisticsItem
80+
count={count}
81+
duration={duration}
82+
name={`${url} ${statusCode}`}
83+
size={size}
84+
url={url}
85+
/>,
8286
);
8387
}
8488
}
8589
}
8690

87-
return createSafeHtmlWithoutSanitize`<article class="test-details">
88-
<p class="test-details__path"></p>
89-
<h2 class="test-details__title">${header}</h2>
90-
<div role="tabpanel">
91-
<article class="overview">${items.join('')}</article>
92-
</div>
93-
</article>`;
94-
}
91+
return (
92+
<article class="test-details">
93+
<p class="test-details__path"></p>
94+
<h2 class="test-details__title">{header}</h2>
95+
<div role="tabpanel">
96+
<article class="overview">{createSafeHtmlWithoutSanitize`${items.join('')}`}</article>
97+
</div>
98+
</article>
99+
);
100+
};

src/utils/report/client/render/ApiStatisticsItem.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import {renderDuration as clientRenderDuration} from './renderDuration';
1+
import {Duration as clientDuration} from './Duration';
22

33
import type {SafeHtml, Url} from '../../../../types/internal';
44

5-
const renderDuration = clientRenderDuration;
5+
const Duration = clientDuration;
66

77
declare const jsx: JSX.Runtime;
88

@@ -29,7 +29,6 @@ export const ApiStatisticsItem: JSX.Component<Props> = ({
2929
url,
3030
}) => {
3131
const bytesInKiB = 1_024;
32-
const durationHtml = renderDuration(duration / count);
3332
const countHtml = `${count}x`;
3433
const sizeHtml = size === undefined ? '' : `${(size / count / bytesInKiB).toFixed(2)} KiB / `;
3534

@@ -52,7 +51,7 @@ export const ApiStatisticsItem: JSX.Component<Props> = ({
5251
<span class="step-expanded__name">{nameHtml}</span>
5352
<span class="step-expanded__time">
5453
{countHtml} / {sizeHtml}
55-
{durationHtml}
54+
<Duration durationInMs={duration / count} />
5655
</span>
5756
</span>
5857
);

0 commit comments

Comments
 (0)