Skip to content

Commit cf16e9a

Browse files
committed
feat: use aka JSX components for rendering HTML report
1 parent fe5e103 commit cf16e9a

16 files changed

Lines changed: 94 additions & 80 deletions

src/types/global.d.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// eslint-disable-next-line import/no-unused-modules
22
import type {SafeHtml} from './html';
3+
import type {Any, ZeroOrOneArg} from './utils';
34

45
/**
56
* Extends global namespaces.
@@ -19,9 +20,9 @@ declare global {
1920
* JSX functional component.
2021
* @internal
2122
*/
22-
type Component<Props extends Properties = Properties> = (
23+
type Component<Props extends object | undefined = Properties | undefined> = (
2324
this: void,
24-
properties?: Props,
25+
...args: ZeroOrOneArg<Props>
2526
) => Element;
2627

2728
/**
@@ -38,7 +39,7 @@ declare global {
3839

3940
type ElementChildrenAttribute = {children: {}};
4041

41-
type ElementType = Component | HtmlTag;
42+
type ElementType = Component<Any> | HtmlTag;
4243

4344
/**
4445
* Creates fragment (`<>...</>`).

src/utils/report/client/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export {readJsonReportData} from './readJsonReportData';
3030
export {readPartOfJsonReportData} from './readPartOfJsonReportData';
3131
/** @internal */
3232
export {
33+
ApiStatisticsItem,
3334
renderApiStatistics,
34-
renderApiStatisticsItem,
3535
renderAttributes,
3636
renderDatesInterval,
3737
renderDuration,

src/utils/report/client/render/renderApiStatisticsItem.tsx renamed to src/utils/report/client/render/ApiStatisticsItem.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const renderDuration = clientRenderDuration;
66

77
declare const jsx: JSX.Runtime;
88

9-
type Options = Readonly<{
9+
type Props = Readonly<{
1010
count: number;
1111
duration: number;
1212
isHeader?: boolean;
@@ -20,14 +20,14 @@ type Options = Readonly<{
2020
* This base client function should not use scope variables (except other base functions).
2121
* @internal
2222
*/
23-
export function renderApiStatisticsItem({
23+
export const ApiStatisticsItem: JSX.Component<Props> = ({
2424
count,
2525
duration,
2626
isHeader,
2727
name,
2828
size,
2929
url,
30-
}: Options): SafeHtml {
30+
}) => {
3131
const bytesInKiB = 1_024;
3232
const durationHtml = renderDuration(duration / count);
3333
const countHtml = `${count}x`;
@@ -56,4 +56,4 @@ export function renderApiStatisticsItem({
5656
</span>
5757
</span>
5858
);
59-
}
59+
};

src/utils/report/client/render/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @internal */
2-
export {renderApiStatistics} from './renderApiStatistics';
2+
export {ApiStatisticsItem} from './ApiStatisticsItem';
33
/** @internal */
4-
export {renderApiStatisticsItem} from './renderApiStatisticsItem';
4+
export {renderApiStatistics} from './renderApiStatistics';
55
/** @internal */
66
export {renderAttributes} from './renderAttributes';
77
/** @internal */

src/utils/report/client/render/renderApiStatistics.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {createSafeHtmlWithoutSanitize as clientCreateSafeHtmlWithoutSanitize} from '../sanitizeHtml';
22

3-
import {renderApiStatisticsItem as clientRenderApiStatisticsItem} from './renderApiStatisticsItem';
3+
import {ApiStatisticsItem as clientApiStatisticsItem} from './ApiStatisticsItem';
44

55
import type {
66
ApiStatistics,
@@ -10,7 +10,7 @@ import type {
1010
} from '../../../../types/internal';
1111

1212
const createSafeHtmlWithoutSanitize = clientCreateSafeHtmlWithoutSanitize;
13-
const renderApiStatisticsItem = clientRenderApiStatisticsItem;
13+
const ApiStatisticsItem = clientApiStatisticsItem;
1414

1515
type Options = Readonly<{
1616
apiStatistics: ApiStatistics;
@@ -38,11 +38,11 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
3838
pageCount += count;
3939
pageDuration += duration;
4040

41-
pageItems.push(renderApiStatisticsItem({count, duration, name: url, url}));
41+
pageItems.push(ApiStatisticsItem({count, duration, name: url, url}));
4242
}
4343

4444
items.push(
45-
renderApiStatisticsItem({count: pageCount, duration: pageDuration, isHeader: true, name}),
45+
ApiStatisticsItem({count: pageCount, duration: pageDuration, isHeader: true, name}),
4646
);
4747
items.push(...pageItems);
4848
}
@@ -54,7 +54,7 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
5454
// eslint-disable-next-line max-depth
5555
for (const [statusCode, {count, duration, size}] of Object.entries(byStatusCode)) {
5656
items.push(
57-
renderApiStatisticsItem({
57+
ApiStatisticsItem({
5858
count,
5959
duration,
6060
name: `${method} ${url} ${statusCode}`,
@@ -72,7 +72,7 @@ export function renderApiStatistics({apiStatistics, hash}: Options): SafeHtml {
7272
>) {
7373
for (const [statusCode, {count, duration, size}] of Object.entries(byStatusCode)) {
7474
items.push(
75-
renderApiStatisticsItem({
75+
ApiStatisticsItem({
7676
count,
7777
duration,
7878
name: `${url} ${statusCode}`,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
declare const jsx: JSX.Runtime;
2+
3+
type Props = Readonly<{warnings: readonly string[]}>;
4+
5+
/**
6+
* Renders report warnings.
7+
* @internal
8+
*/
9+
export const Warnings: JSX.Component<Props> = ({warnings}) => {
10+
if (warnings.length === 0) {
11+
return <></>;
12+
}
13+
14+
const renderedWarnings = warnings.map((warning) => <div class="__error">{warning}</div>);
15+
16+
return <div class="warnings">{renderedWarnings.join('')}</div>;
17+
};

src/utils/report/render/locator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ const isProduction = e2edEnvironment.E2ED_ORIGIN !== 'https://bing.com';
1010

1111
const createLocatorOptions: CreateLocatorOptions = {attributesOptions, isProduction};
1212

13-
const {locator: locatorAttributes} = createSimpleLocator(createLocatorOptions);
13+
export const locator: LocatorFunction = createSimpleLocator(createLocatorOptions).locator;
1414

1515
export {createLocatorOptions};
1616

1717
/**
1818
* `locator` function.
1919
* @internal
2020
*/
21-
export const locator: LocatorFunction<SafeHtml> = (...args): SafeHtml =>
22-
renderAttributes(locatorAttributes(...(args as [string])));
21+
export const locatorAttributes: LocatorFunction<SafeHtml> = (...args): SafeHtml =>
22+
renderAttributes(locator(...(args as [string])));

src/utils/report/render/renderNavigation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {sanitizeHtml} from '../client';
22

3-
import {locator} from './locator';
3+
import {locatorAttributes} from './locator';
44
import {renderLogo} from './renderLogo';
55
import {renderRetriesButtons} from './renderRetriesButtons';
66

@@ -14,8 +14,8 @@ type Props = Readonly<{retries: readonly RetryProps[]}>;
1414
*/
1515
export const renderNavigation = ({
1616
retries,
17-
}: Props): SafeHtml => sanitizeHtml`<nav class="nav" ${locator('Navigation')}>
18-
<header class="header" ${locator('header')}>
17+
}: Props): SafeHtml => sanitizeHtml`<nav class="nav" ${locatorAttributes('Navigation')}>
18+
<header class="header" ${locatorAttributes('header')}>
1919
${renderLogo()}
2020
</header>
2121
${renderRetriesButtons({retries})}

src/utils/report/render/renderReportToHtml.ts renamed to src/utils/report/render/renderReportToHtml.tsx

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {assertValueIsNotNull} from '../../asserts';
22
import {generalLog} from '../../generalLog';
33
import {getDurationWithUnits} from '../../getDurationWithUnits';
44

5-
import {sanitizeHtml} from '../client';
5+
import {createJsxRuntime, sanitizeHtml} from '../client';
66
import {getImgCspHosts} from '../getImgCspHosts';
77
import {getRetriesProps} from '../getRetriesProps';
88

@@ -12,10 +12,14 @@ import {renderHead} from './renderHead';
1212
import {renderJsonData} from './renderJsonData';
1313
import {renderNavigation} from './renderNavigation';
1414
import {renderRetries} from './renderRetries';
15-
import {renderWarnings} from './renderWarnings';
15+
import {Warnings} from './Warnings';
1616

1717
import type {ReportData, SafeHtml, UtcTimeInMs} from '../../../types/internal';
1818

19+
declare const jsx: JSX.Runtime;
20+
21+
Object.assign(globalThis, {jsx: createJsxRuntime()});
22+
1923
/**
2024
* Renders report data to HTML report page.
2125
* @internal
@@ -33,28 +37,39 @@ export const renderReportToHtml = (reportData: ReportData): SafeHtml => {
3337
const retryNumbers = retries.map(({retryIndex}) => retryIndex);
3438
const maxRetry = Math.max(...retryNumbers);
3539

36-
const safeHtml = sanitizeHtml`<!DOCTYPE html>
37-
<html lang="en">
38-
${renderHead(reportFileName, imgCspHosts)}
39-
<body>
40-
${renderNavigation({retries})}
41-
<div class="main" role="tabpanel">
42-
<section class="main__section _position_left" aria-label="Retry ${maxRetry}" ${locator('column1')}>
43-
${renderRetries({retries})}
44-
${renderErrors(reportData.errors)}
45-
${renderWarnings(reportData.warnings)}
46-
</section>
47-
<div class="drag-container"></div>
48-
<section
49-
aria-label="Tests results"
50-
class="main__section _position_right"
51-
id="e2edRightColumnContainer"
52-
${locator('column2')}
53-
><div class="test-details-empty"><p>No test selected</p></div></section>
54-
</div>
55-
${renderJsonData(reportData)}
56-
</body>
57-
</html>`;
40+
const page = (
41+
<html lang="en">
42+
{renderHead(reportFileName, imgCspHosts)}
43+
<body>
44+
{renderNavigation({retries})}
45+
<div class="main" role="tabpanel">
46+
<section
47+
class="main__section _position_left"
48+
aria-label={`Retry ${maxRetry}`}
49+
{...locator('column1')}
50+
>
51+
{renderRetries({retries})}
52+
{renderErrors(reportData.errors)}
53+
<Warnings warnings={reportData.warnings} />
54+
</section>
55+
<div class="drag-container"></div>
56+
<section
57+
aria-label="Tests results"
58+
class="main__section _position_right"
59+
id="e2edRightColumnContainer"
60+
{...locator('column2')}
61+
>
62+
<div class="test-details-empty">
63+
<p>No test selected</p>
64+
</div>
65+
</section>
66+
</div>
67+
{renderJsonData(reportData)}
68+
</body>
69+
</html>
70+
);
71+
72+
const safeHtml = sanitizeHtml`<!DOCTYPE html>${page}`;
5873

5974
const durationWithUnits = getDurationWithUnits(Date.now() - startTimeInMs);
6075

src/utils/report/render/renderRetriesButtons.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {createSafeHtmlWithoutSanitize} from '../client';
22

3-
import {locator} from './locator';
3+
import {locatorAttributes} from './locator';
44
import {renderRetryButton} from './renderRetryButton';
55

66
import type {RetryProps, SafeHtml} from '../../../types/internal';
@@ -29,5 +29,5 @@ export const renderRetriesButtons = ({retries}: Props): SafeHtml => {
2929
}
3030

3131
return createSafeHtmlWithoutSanitize`
32-
<div role="tablist" aria-label="Retries" class="nav-tabs" ${locator('RetriesButtons')}>${buttons.join('')}</div>`;
32+
<div role="tablist" aria-label="Retries" class="nav-tabs" ${locatorAttributes('RetriesButtons')}>${buttons.join('')}</div>`;
3333
};

0 commit comments

Comments
 (0)