Skip to content

Commit

Permalink
FI-1015 feat: add locators to HTML report for testing locator utils
Browse files Browse the repository at this point in the history
  • Loading branch information
uid11 committed Nov 15, 2023
1 parent 6523859 commit 14e743a
Show file tree
Hide file tree
Showing 14 changed files with 182 additions and 37 deletions.
1 change: 1 addition & 0 deletions src/types/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {RunLabel} from './runLabel';
*/
export type E2edEnvironment = {
['E2ED_DEBUG']?: string;
['E2ED_ORIGIN']?: string;
['PWD']?: string;
[PATH_TO_PACK_VARIABLE_NAME]?: string;
[RUN_ENVIRONMENT_VARIABLE_NAME]?: RunEnvironment;
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type {ReportRootLocator} from '../utils/report';
export type {Brand, IsBrand} from './brand';
export type {Expect, IsEqual, IsReadonlyKey} from './checks';
export type {Class} from './class';
Expand Down
1 change: 1 addition & 0 deletions src/utils/report/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {getLiteReport} from './getLiteReport';
/** @internal */
// eslint-disable-next-line import/no-unused-modules
export {getTestsThatRunningAtGivenTime} from './getTestsThatRunningAtGivenTime';
export type {ReportRootLocator} from './render';
/** @internal */
export {writeHtmlReport} from './writeHtmlReport';
/** @internal */
Expand Down
1 change: 1 addition & 0 deletions src/utils/report/render/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type {RootLocator as ReportRootLocator} from './rootLocator';
/** @internal */
export {renderReportToHtml} from './renderReportToHtml';
19 changes: 19 additions & 0 deletions src/utils/report/render/renderAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {createSafeHtmlWithoutSanitize} from '../client';

import type {Attributes} from 'create-locator';

import type {SafeHtml} from '../../../types/internal';

/**
* Renders attributes object to safe HTML string.
* @internal
*/
export const renderAttributes = (attributes: object): SafeHtml => {
const parts: string[] = [];

for (const key of Object.keys(attributes)) {
parts.push(`${key}="${(attributes as Attributes)[key]}"`);
}

return createSafeHtmlWithoutSanitize`${parts.join(' ')}`;
};
23 changes: 18 additions & 5 deletions src/utils/report/render/renderNavigation.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {sanitizeHtml} from '../client';

import {renderAttributes} from './renderAttributes';
import {renderLogo} from './renderLogo';
import {renderRetriesButtons} from './renderRetriesButtons';
import {renderRetriesButtons, type RetriesButtonsLocator} from './renderRetriesButtons';

import type {RetryProps, SafeHtml} from '../../../types/internal';

type Props = Readonly<{retries: readonly RetryProps[]}> & Mark<NavigationLocator>;

export type NavigationLocator = Locator<{
retries: RetriesButtonsLocator;
}>;

/**
* Renders tag <nav>.
* Renders tag `<nav>`.
* @internal
*/
export const renderNavigation = (retries: readonly RetryProps[]): SafeHtml => sanitizeHtml`
<nav class="nav">
export const renderNavigation = ({retries, ...rest}: Props): SafeHtml => {
const locator = createLocator(rest);

return sanitizeHtml`
<nav class="nav" ${renderAttributes(locator())}>
<header class="header">
${renderLogo()}
</header>
${renderRetriesButtons(retries)}
${renderRetriesButtons({retries, ...locator.retries()})}
</nav>`;
};
11 changes: 8 additions & 3 deletions src/utils/report/render/renderReportToHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {getDurationWithUnits} from '../../getDurationWithUnits';
import {sanitizeHtml} from '../client';
import {getRetriesProps} from '../getRetriesProps';

import {renderAttributes} from './renderAttributes';
import {renderErrors} from './renderErrors';
import {renderHead} from './renderHead';
import {renderJsonData} from './renderJsonData';
import {renderNavigation} from './renderNavigation';
import {renderRetries} from './renderRetries';
import {rootLocator} from './rootLocator';

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

Expand All @@ -33,17 +35,20 @@ export const renderReportToHtml = (reportData: ReportData): SafeHtml => {
<html lang="en">
${renderHead(reportFileName)}
<body>
${renderNavigation(retries)}
${renderNavigation({retries, ...rootLocator.navigation()})}
<div class="main" role="tabpanel">
<section class="main__section _position_left" aria-label="Retry ${maxRetry}">
${renderRetries(retries)}
<section class="main__section _position_left" aria-label="Retry ${maxRetry}" ${renderAttributes(
rootLocator.column1(),
)}>
${renderRetries({retries, ...rootLocator.retries()})}
${renderErrors(reportData.errors)}
</section>
<div class="drag-container"></div>
<section
aria-label="Tests results"
class="main__section _position_right"
id="e2edTestRunDetailsContainer"
${renderAttributes(rootLocator.column2())}
><div class="test-details-empty"><p>No test selected</p></div></section>
</div>
${renderJsonData(reportData)}
Expand Down
13 changes: 10 additions & 3 deletions src/utils/report/render/renderRetries.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {createSafeHtmlWithoutSanitize} from '../client';

import {renderRetry} from './renderRetry';
import {renderRetry, type RetryLocator} from './renderRetry';

import type {RetryProps, SafeHtml} from '../../../types/internal';

type Props = Readonly<{retries: readonly RetryProps[]}> & Mark<RetriesLocator>;

export type RetriesLocator = Locator<{retry: RetryLocator}>;

/**
* Renders list of retries (with test runs).
* @internal
*/
export const renderRetries = (retries: readonly RetryProps[]): SafeHtml => {
const retriesHtml = retries.map(renderRetry);
export const renderRetries = ({retries, ...rest}: Props): SafeHtml => {
const locator = createLocator(rest);
const retriesHtml = retries.map((retry) => renderRetry({retry, ...locator.retry()}));

return createSafeHtmlWithoutSanitize`${retriesHtml.join('')}`;
};
19 changes: 16 additions & 3 deletions src/utils/report/render/renderRetriesButtons.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {createSafeHtmlWithoutSanitize} from '../client';

import {renderRetryButton} from './renderRetryButton';
import {renderAttributes} from './renderAttributes';
import {renderRetryButton, type RetryButtonLocator} from './renderRetryButton';

import type {RetryProps, SafeHtml} from '../../../types/internal';

type Props = Readonly<{retries: readonly RetryProps[]}> & Mark<RetriesButtonsLocator>;

export type RetriesButtonsLocator = Locator<{
button: RetryButtonLocator;
}>;

/**
* Renders retries navigation buttons.
* @internal
*/
export const renderRetriesButtons = (retries: readonly RetryProps[]): SafeHtml => {
export const renderRetriesButtons = ({retries, ...rest}: Props): SafeHtml => {
const locator = createLocator(rest);
const retryNumbers = retries.map(({retryIndex}) => retryIndex);
const maxRetry = Math.max(...retryNumbers);
const buttons: SafeHtml[] = [];
Expand All @@ -20,9 +30,12 @@ export const renderRetriesButtons = (retries: readonly RetryProps[]): SafeHtml =
disabled: !isRetry,
retry: index,
selected: index === maxRetry,
...locator.button(),
});
}

return createSafeHtmlWithoutSanitize`
<div role="tablist" aria-label="Retries" class="nav-tabs">${buttons.join('')}</div>`;
<div role="tablist" aria-label="Retries" class="nav-tabs" ${renderAttributes(
locator(),
)}>${buttons.join('')}</div>`;
};
27 changes: 21 additions & 6 deletions src/utils/report/render/renderRetry.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {createSafeHtmlWithoutSanitize} from '../client';

import {renderRetryHeader} from './renderRetryHeader';
import {renderTestRunButton} from './renderTestRunButton';
import {renderAttributes} from './renderAttributes';
import {renderRetryHeader, type RetryHeaderLocator} from './renderRetryHeader';
import {renderTestRunButton, type TestRunButtonLocator} from './renderTestRunButton';

import type {RetryProps, SafeHtml} from '../../../types/internal';

type Props = Readonly<{retry: RetryProps}> & Mark<RetryLocator>;

export type RetryLocator = Locator<
{button: TestRunButtonLocator; header: RetryHeaderLocator},
{index: string}
>;

/**
* Renders test runs list for one retry.
* @internal
*/
export const renderRetry = (retryProps: RetryProps): SafeHtml => {
const buttons = retryProps.testRunButtons.map(renderTestRunButton);
export const renderRetry = ({retry, ...rest}: Props): SafeHtml => {
const locator = createLocator(rest);
const buttons = retry.testRunButtons.map((props, index) =>
renderTestRunButton({...props, index, ...locator.button()}),
);

return createSafeHtmlWithoutSanitize`
<article class="retry" id="retry${retryProps.retryIndex}" ${retryProps.hidden ? 'hidden' : ''}>
${renderRetryHeader(retryProps)}
<article class="retry" id="retry${retry.retryIndex}" ${
retry.hidden ? 'hidden' : ''
} {${renderAttributes(locator({index: String(retry.retryIndex)}))}}>
${renderRetryHeader({...retry, ...locator.header()})}
${buttons.join('')}
</article>`;
};
24 changes: 19 additions & 5 deletions src/utils/report/render/renderRetryButton.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {sanitizeHtml} from '../client';

import {renderAttributes} from './renderAttributes';

import type {RetryButtonProps, SafeHtml} from '../../../types/internal';

type Props = RetryButtonProps & Mark<RetryButtonLocator>;

export type RetryButtonLocator = Locator<
void,
{disabled: `${boolean}`; retry: string; selected: `${boolean}`}
>;

/**
* Renders tag <button> with single retry button.
* @internal
*/
export const renderRetryButton = ({
disabled,
retry,
selected,
}: RetryButtonProps): SafeHtml => sanitizeHtml`<button
export const renderRetryButton = ({disabled, retry, selected, ...rest}: Props): SafeHtml => {
const locator = createLocator(rest);

return sanitizeHtml`<button
aria-controls="retry${retry}"
aria-selected="${String(selected)}"
class="nav-tabs__button"
id="retry${retry}-nav"
role="tab"
${renderAttributes(
locator({disabled: `${disabled}`, retry: String(retry), selected: `${selected}`}),
)}
${disabled ? 'disabled' : ''}>Retry ${retry}</button>`;
};
18 changes: 14 additions & 4 deletions src/utils/report/render/renderRetryHeader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {renderDatesInterval, renderDuration, sanitizeHtml} from '../client';

import type {RetryProps, SafeHtml} from '../../../types/internal';
import {renderAttributes} from './renderAttributes';

import type {RetryProps, SafeHtml, Void} from '../../../types/internal';

type Props = RetryProps & Mark<RetryHeaderLocator>;

export type RetryHeaderLocator = Locator<{date: Void; title: Void}>;

/**
* Renders retry header.
Expand All @@ -10,12 +18,14 @@ export const renderRetryHeader = ({
endTimeInMs,
retryIndex,
startTimeInMs,
}: RetryProps): SafeHtml => {
...rest
}: Props): SafeHtml => {
const locator = createLocator(rest);
const durationInMs = endTimeInMs - startTimeInMs;

return sanitizeHtml`
<h3 class="__title">Retry ${retryIndex}</h3>
<p class="__date">
<h3 class="__title" ${renderAttributes(locator.title())}>Retry ${retryIndex}</h3>
<p class="__date" ${renderAttributes(locator.date())}>
${renderDatesInterval({endTimeInMs, startTimeInMs})}
(${renderDuration(durationInMs)})
</p>`;
Expand Down
42 changes: 34 additions & 8 deletions src/utils/report/render/renderTestRunButton.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
import {createLocator, type Locator, type Mark} from 'create-locator';

import {renderDuration, sanitizeHtml} from '../client';

import type {SafeHtml, TestRunButtonProps} from '../../../types/internal';
import {renderAttributes} from './renderAttributes';

import type {TestRunStatus} from '../../../constants/internal';
import type {SafeHtml, TestRunButtonProps, Void} from '../../../types/internal';

type Props = TestRunButtonProps & Readonly<{index: number}> & Mark<TestRunButtonLocator>;

export type TestRunButtonLocator = Locator<
{name: Void; order: Void; parameters: Void; time: Void},
{status: TestRunStatus}
>;

/**
* Renders single test run button (in test runs list).
* @internal
*/
export const renderTestRunButton = (
{endTimeInMs, startTimeInMs, status, mainParams, name, runHash}: TestRunButtonProps,
index: number,
): SafeHtml => {
export const renderTestRunButton = ({
endTimeInMs,
index,
mainParams,
name,
runHash,
startTimeInMs,
status,
...rest
}: Props): SafeHtml => {
const locator = createLocator(rest);
const durationInMs = endTimeInMs - startTimeInMs;

return sanitizeHtml`<button
aria-selected="false"
class="test-button test-button_status_${status}"
data-runhash="${runHash}"
role="tab"
${renderAttributes(locator({status}))}
>
<span class="test-button__order">#${index + 1}</span>
<span class="test-button__name">${name}<span class="test-button__parameters">${mainParams}</span></span>
<span class="test-button__time">${renderDuration(durationInMs)}</span>
<span class="test-button__order" ${renderAttributes(locator.order())}>#${index + 1}</span>
<span class="test-button__name" ${renderAttributes(
locator.name(),
)}>${name}<span class="test-button__parameters" ${renderAttributes(
locator.parameters(),
)}>${mainParams}</span></span>
<span class="test-button__time" ${renderAttributes(locator.time())}>${renderDuration(
durationInMs,
)}</span>
</button>`;
};
19 changes: 19 additions & 0 deletions src/utils/report/render/rootLocator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {createLocator, type Locator} from 'create-locator';

import {e2edEnvironment} from '../../../constants/internal';

import type {Void} from '../../../types/internal';

import type {NavigationLocator} from './renderNavigation';
import type {RetriesLocator} from './renderRetries';

const isProduction = e2edEnvironment.E2ED_ORIGIN !== 'https://google.com';

export const rootLocator = createLocator<RootLocator>('app', {isProduction});

export type RootLocator = Locator<{
column1: Void;
column2: Void;
navigation: NavigationLocator;
retries: RetriesLocator;
}>;

0 comments on commit 14e743a

Please sign in to comment.