Skip to content
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: ci
permissions:
contents: read

on: push

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@ should be copied to this directory after the pack is completed,
and then screenshots from this directory will be displayed in the HTML report.
If `null`, screenshots will not be displayed in the HTML report.

`regroupSteps: (steps: readonly LogEvent[]) => readonly LogEvent[]`: a function that regroups the tree of test steps
in the HTML report.
This way, you can leave only the important test steps (actions, checks) at the top level,
while hiding minor technical steps at deeper levels of the tree
(they will be visible in the report if you explicitly expand them).

`reportFileName: string | null`: the name of the file under which, after running the tests,
the HTML report will be saved in the `autotests/reports` directory, for example, `report.html`.
Also this name is used as the title of the report page.
Expand Down
1 change: 1 addition & 0 deletions autotests/configurator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {mapLogPayloadInConsole} from './mapLogPayloadInConsole';
export {mapLogPayloadInLogFile} from './mapLogPayloadInLogFile';
export {mapLogPayloadInReport} from './mapLogPayloadInReport';
export {matchScreenshot} from './matchScreenshot';
export {regroupSteps} from './regroupSteps';
export {skipTests} from './skipTests';
export type {
DoAfterPack,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {LogEventStatus, LogEventType} from '../../../constants/internal';
import {LogEventStatus, LogEventType} from 'e2ed/constants';
import {setReadonlyProperty} from 'e2ed/utils';

import type {LogEvent, LogEventWithChildren} from '../../../types/internal';
import type {LogEvent, Mutable} from 'e2ed/types';

/**
* Group log events to log events with children (for groupping of `TestRun` steps).
* Regroup log events (for groupping of `TestRun` steps).
* This base client function should not use scope variables (except other base functions).
* @internal
*/
export const groupLogEvents = (logEvents: readonly LogEvent[]): readonly LogEventWithChildren[] => {
export const regroupSteps = (logEvents: readonly LogEvent[]): readonly LogEvent[] => {
const topLevelTypes: readonly LogEventType[] = [
LogEventType.Action,
LogEventType.Assert,
Expand All @@ -20,16 +21,17 @@ export const groupLogEvents = (logEvents: readonly LogEvent[]): readonly LogEven
topLevelTypes.includes(logEvent.type) ||
logEvent.payload?.logEventStatus === LogEventStatus.Failed;

const result: LogEventWithChildren[] = [];
const result: LogEvent[] = [];

for (const logEvent of logEvents) {
const last = result.at(-1);
const newEvent: LogEventWithChildren = {children: [], ...logEvent};
const newEvent: LogEvent = {...logEvent};

if (isTopLevelEvent(logEvent)) {
if (last && !isTopLevelEvent(last)) {
const firstTopLevel: LogEventWithChildren = {
const firstTopLevel: LogEvent = {
children: [...result],
endTime: undefined,
message: 'Initialization',
payload: undefined,
time: last.time,
Expand All @@ -43,7 +45,11 @@ export const groupLogEvents = (logEvents: readonly LogEvent[]): readonly LogEven

result.push(newEvent);
} else if (last && isTopLevelEvent(last)) {
(last.children as LogEventWithChildren[]).push(newEvent);
if (last.children === undefined) {
setReadonlyProperty(last, 'children', [newEvent]);
} else {
(last.children as Mutable<typeof last.children>).push(newEvent);
}
} else {
result.push(newEvent);
}
Expand Down
2 changes: 2 additions & 0 deletions autotests/packs/allTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
mapLogPayloadInLogFile,
mapLogPayloadInReport,
matchScreenshot,
regroupSteps,
skipTests,
} from '../configurator';

Expand Down Expand Up @@ -74,6 +75,7 @@ export const pack: Pack = {
pathToScreenshotsDirectoryForReport: './screenshots',
port1: 1337,
port2: 1338,
regroupSteps,
reportFileName: 'report.html',
resourceUsageReadingInternal: 5_000,
selectorTimeout: 10_000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export class E2edReportExample extends Page<CustomPageParams> {
*/
readonly header: Selector = locator('header');

/**
* Logo of `e2ed` in page header.
*/
readonly logo: Selector = locator('logo');

/**
* Navigation bar with test retries.
*/
Expand Down Expand Up @@ -83,7 +88,7 @@ export class E2edReportExample extends Page<CustomPageParams> {
}

async clickLogo(): Promise<void> {
await click(this.header, {position: {x: 30, y: 30}});
await click(this.logo);
}

getRoute(): E2edReportExampleRoute {
Expand Down
3 changes: 3 additions & 0 deletions autotests/pageObjects/pages/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export class Main extends Page<CustomPageParams> {
await waitForAllRequestsComplete(
({url}) => {
if (
url.startsWith('https://assets.msn.com/') ||
url.startsWith('https://www.bing.com/ipv6test/') ||
url.startsWith('https://www2.bing.com/ipv6test/') ||
url.startsWith('https://browser.events.data.msn.com/') ||
url.startsWith('https://img-s-msn-com.akamaized.net/') ||
url.startsWith('https://rewards.bing.com/widget/') ||
Expand Down
2 changes: 1 addition & 1 deletion autotests/tests/e2edReportExample/toMatchScreenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {navigateToPage} from 'e2ed/actions';
test('correctly check screenshots via toMatchScreenshot', {meta: {testId: '20'}}, async () => {
const reportPage = await navigateToPage(E2edReportExample);

await expect(reportPage.header.find('a'), 'toMatchScreenshot check screenshot').toMatchScreenshot(
await expect(reportPage.logo, 'toMatchScreenshot check screenshot').toMatchScreenshot(
'pwoZRA8i7O',
{mask: []},
);
Expand Down
2 changes: 1 addition & 1 deletion autotests/tests/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {getFullPackConfig} from 'autotests/utils';
import {expect} from 'e2ed';
import {assertFunctionThrows, getTimeoutPromise} from 'e2ed/utils';

test('expect function works correctly', {meta: {testId: '16'}}, async () => {
test('expect(...) function works correctly', {meta: {testId: '16'}}, async () => {
const {assertionTimeout} = getFullPackConfig();

await assertFunctionThrows(async () => {
Expand Down
12 changes: 5 additions & 7 deletions autotests/tests/main/exists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {assertFunctionThrows, getDocumentUrl} from 'e2ed/utils';

import type {Url} from 'e2ed/types';

const testScrollValue = 200;
const language = 'en';
const searchQuery = 'foo';

// eslint-disable-next-line max-statements
test('exists', {meta: {testId: '1'}, testIdleTimeout: 10_000, testTimeout: 15_000}, async () => {
const language = 'en';
const searchQuery = 'foo';
const testScrollValue = 200;

await scroll(0, testScrollValue);

assertFunctionThrows(() => {
Expand All @@ -40,8 +40,6 @@ test('exists', {meta: {testId: '1'}, testIdleTimeout: 10_000, testTimeout: 15_00
'dynamic custom pack properties is correct',
).gt(0);

await step('Some step');

const urlObjectPromise = waitForStartOfPageLoad();

const mainPage = await navigateToPage(Main, {language});
Expand All @@ -60,7 +58,7 @@ test('exists', {meta: {testId: '1'}, testIdleTimeout: 10_000, testTimeout: 15_00

await expect(mainPage.searchQuery, 'search query on page is empty').eql('');

await step('Another step', () => {
await step('Some step', () => {
getPageCookies();
});

Expand Down
57 changes: 57 additions & 0 deletions autotests/tests/step.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {test} from 'autotests';
import {step} from 'e2ed';
import {waitForTimeout} from 'e2ed/actions';
import {LogEventType} from 'e2ed/constants';
import {assertFunctionThrows, log} from 'e2ed/utils';

test('step(...) function works correctly', {meta: {testId: '23'}}, async () => {
const timeout = 30;
const timeoutAddition = 10;

await step('First step', (): undefined => {});

await step('Step level 1', async () => {
await step('Step level 2');
await step('Skipped step level 2', () => {}, {skipLogs: true});

log('Some log on level 2', {payload: 18});

await step('Step level 2 with children', async () => {
await step('Step level 3 with action type', () => {}, {
payload: {initialPayload: 10},
timeout: 10,
type: LogEventType.Action,
});

await step('Step level 3', async () => {
await step('Step level 4');

await assertFunctionThrows(async () => {
await step(
'Failed step with timeout',
async () => {
await waitForTimeout(timeout + timeoutAddition);

await step('Also failed step', () => {
throw new Error('This step should be torn out of the tree');
});
},
{runPlaywrightStep: true, timeout},
);
}, 'step body throws an error on timeout end');

await step(
'Step level 4 with running playwright step',
() => ({finalPayload: 40, initialPayload: 30}),
{payload: {initialPayload: 20}, runPlaywrightStep: true},
);

log('Some log on level 4');
});

log('Some log on level 3', {level: 3});
});

log('Also some log on level 2', {level: 2});
});
});
9 changes: 6 additions & 3 deletions autotests/tests/switchingPagesForRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ test(

await waitForTimeout(maxNumberOfRequests * 333);

const npmPageTab = await waitForNewTab(async () => {
await reportPage.clickLogo();
});
const npmPageTab = await waitForNewTab(
async () => {
await reportPage.clickLogo();
},
{timeout: 10_000},
);

await switchToTab(npmPageTab);

Expand Down
9 changes: 6 additions & 3 deletions autotests/tests/switchingPagesForResponses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ test(

await waitForTimeout(maxNumberOfRequests * 333);

const npmPageTab = await waitForNewTab(async () => {
await reportPage.clickLogo();
});
const npmPageTab = await waitForNewTab(
async () => {
await reportPage.clickLogo();
},
{timeout: 10_000},
);

await switchToTab(npmPageTab);

Expand Down
4 changes: 2 additions & 2 deletions autotests/tests/waitForAllRequestsComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test(

await assertFunctionThrows(async () => {
await waitForAllRequestsComplete(() => true, {timeout: 100});
}, 'Catch error from waitForAllRequestsComplete for {timeout: 100}');
}, 'Caught an error from waitForAllRequestsComplete for {timeout: 100}');

await waitForAllRequestsComplete(() => true, {timeout: 1000});

Expand All @@ -37,7 +37,7 @@ test(

await assertFunctionThrows(
() => promise,
'Catch error from waitForAllRequestsComplete for {timeout: 1000}',
'Caught an error from waitForAllRequestsComplete for {timeout: 1000}',
);

waitedInMs = Date.now() - startRequestInMs;
Expand Down
27 changes: 27 additions & 0 deletions src/context/stepsStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {useContext} from '../useContext';

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

/**
* Raw get and set (maybe `undefined`) of test steps stack.
* @internal
*/
const [getRawStepsStack, setRawStepsStack] = useContext<LogEvent[]>();

/**
* Get always defined test steps stack.
* @internal
*/
export const getStepsStack = (): readonly LogEvent[] => {
const maybeStepsStack = getRawStepsStack();

if (maybeStepsStack !== undefined) {
return maybeStepsStack;
}

const stepsStack: LogEvent[] = [];

setRawStepsStack(stepsStack);

return stepsStack;
};
Loading