Skip to content

Commit

Permalink
FI-705 fix: stricter type of expect function
Browse files Browse the repository at this point in the history
fix: support usual promises in `expect` function
feat: add field `maxIntervalBetweenRequestsInMs` ot abstract class `Page`
feat: add examples of `mapBackendResponseErrorToLog`/`mapBackendResponseToLog`
tests: more tests of types of selectors methods
chore: update nodejs to current LTS (20.10.0)
chore: update alpine to 3.18.5
fix: support new contributor in `updateChangelog` script
refactor: move selectors code to `utils/selectors`
refactor: remove `utils/locators`
fix: use default cursor for empty expanded steps
feat: add duration to backend response logs
refactor: rename function `it` to `test` in initial autotests examples
fix: print `message` and `cause` fields of `E2edError` in `replaceFields`
fix: reject stuck assertion of `expect` by timeout
tests: add separate tests for `expect` function
  • Loading branch information
uid11 committed Dec 6, 2023
1 parent dc29643 commit d34eeb0
Show file tree
Hide file tree
Showing 78 changed files with 648 additions and 336 deletions.
11 changes: 9 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
FROM alpine:3.18.4
FROM node:20.10.0-alpine AS node

FROM alpine:3.18.5

COPY --from=node /usr/lib /usr/lib
COPY --from=node /usr/local/lib /usr/local/lib
COPY --from=node /usr/local/include /usr/local/include
COPY --from=node /usr/local/bin /usr/local/bin

RUN apk --no-cache upgrade && \
apk --no-cache add \
bash libevent nodejs npm chromium firefox xwininfo xvfb dbus eudev ttf-freefont fluxbox procps tzdata icu-data-full
bash libevent npm chromium firefox xwininfo xvfb dbus eudev ttf-freefont fluxbox procps tzdata icu-data-full

COPY ./build/node_modules/e2ed /node_modules/e2ed

Expand Down
2 changes: 2 additions & 0 deletions autotests/configurator/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export {doAfterPack} from './doAfterPack';
export {doBeforePack} from './doBeforePack';
export {mapBackendResponseErrorToLog} from './mapBackendResponseErrorToLog';
export {mapBackendResponseToLog} from './mapBackendResponseToLog';
export {mapLogPayloadInConsole} from './mapLogPayloadInConsole';
export {mapLogPayloadInLogFile} from './mapLogPayloadInLogFile';
export {mapLogPayloadInReport} from './mapLogPayloadInReport';
Expand Down
42 changes: 42 additions & 0 deletions autotests/configurator/mapBackendResponseErrorToLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {getDurationWithUnits} from 'e2ed/utils';

import type {MapBackendResponseErrorToLog} from 'autotests/types/packSpecific';

/**
* Maps responses with errors from the backend to "red" logs (as errors) during the test.
* It is assumed that the function will select responses with
* statuse codes of 400 and higher (client and server errors).
* Backend responses with errors are accumulated in separate "red" log step
* (with `logEventStatus: 'failed'`).
* Log the `responseBody` field carefully, as the body of backend response can be very large.
* If the function returns `undefined`, the response is not logged (skipped).
*/
export const mapBackendResponseErrorToLog: MapBackendResponseErrorToLog = ({
completionTimeInMs,
request,
responseBody,
responseHeaders,
statusCode,
}) => {
if (statusCode < 400) {
return undefined;
}

const {requestBody, utcTimeInMs, ...requestWithoutBody} = request ?? {};

const maybeWithDuration: {duration?: string} = {};

if (completionTimeInMs !== undefined && utcTimeInMs !== undefined) {
maybeWithDuration.duration = getDurationWithUnits(completionTimeInMs - utcTimeInMs);
}

return {
...maybeWithDuration,
request: {
requestBody: requestBody instanceof Buffer ? String(requestBody) : requestBody,
...requestWithoutBody,
},
responseBody: responseBody instanceof Buffer ? String(responseBody) : responseBody,
responseHeaders,
};
};
38 changes: 38 additions & 0 deletions autotests/configurator/mapBackendResponseToLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {getDurationWithUnits} from 'e2ed/utils';

import type {MapBackendResponseToLog} from 'autotests/types/packSpecific';

/**
* Maps responses from the backend to logs during the test.
* Backend responses received during a certain test step are accumulated
* in an array in the `backendResponses` field of the log of this step.
* Log the `responseBody` field carefully, as the body of backend response can be very large.
* If the function returns `undefined`, the response is not logged (skipped).
*/
export const mapBackendResponseToLog: MapBackendResponseToLog = ({
completionTimeInMs,
request,
responseBody,
responseHeaders,
statusCode,
}) => {
if (statusCode >= 400) {
return undefined;
}

if (request) {
const maybeWithDuration: {duration?: string} = {};

if (completionTimeInMs !== undefined && request.utcTimeInMs !== undefined) {
maybeWithDuration.duration = getDurationWithUnits(completionTimeInMs - request.utcTimeInMs);
}

return {...maybeWithDuration, statusCode, url: request?.url};
}

return {
responseBody: responseBody instanceof Buffer ? String(responseBody) : responseBody,
responseHeaders,
statusCode,
};
};
2 changes: 1 addition & 1 deletion autotests/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export {it} from './it';
export {test} from './test';
10 changes: 5 additions & 5 deletions autotests/packs/allTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {RunEnvironment, runEnvironment} from 'e2ed/configurator';
import {
doAfterPack,
doBeforePack,
mapBackendResponseErrorToLog,
mapBackendResponseToLog,
mapLogPayloadInConsole,
mapLogPayloadInLogFile,
mapLogPayloadInReport,
Expand All @@ -30,7 +32,7 @@ const filterTestsIntoPack: FilterTestsIntoPack = ({options}) => options.meta.tes
*/
export const pack: Pack = {
ajaxRequestTimeout: 40_000,
assertionTimeout: 10_000,
assertionTimeout: 5_000,
browser: [browser, ...browserFlags].join(' '),
browserInitTimeout: 60_000,
concurrency: isLocalRun ? 1 : 2,
Expand All @@ -42,10 +44,8 @@ export const pack: Pack = {
filterTestsIntoPack,
liteReportFileName: 'lite-report.json',
logFileName: 'pack-logs.log',
mapBackendResponseErrorToLog: ({request, responseHeaders, statusCode}) =>
statusCode >= 400 ? {request, responseHeaders, statusCode} : undefined,
mapBackendResponseToLog: ({request, statusCode}) =>
statusCode < 400 ? {statusCode, url: request?.url} : undefined,
mapBackendResponseErrorToLog,
mapBackendResponseToLog,
mapLogPayloadInConsole,
mapLogPayloadInLogFile,
mapLogPayloadInReport,
Expand Down
20 changes: 13 additions & 7 deletions autotests/pageObjects/pages/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,19 @@ export class Main extends Page<CustomPageParams> {
}

override async waitForPageLoaded(): Promise<void> {
await waitForAllRequestsComplete(({url}) => {
if (url.startsWith('https://adservice.google.com/')) {
return false;
}

return true;
});
await waitForAllRequestsComplete(
({url}) => {
if (
url.startsWith('https://adservice.google.com/') ||
url.startsWith('https://play.google.com/')
) {
return false;
}

return true;
},
{maxIntervalBetweenRequestsInMs: this.maxIntervalBetweenRequestsInMs},
);

await waitForInterfaceStabilization(this.pageStabilizationInterval);
}
Expand Down
27 changes: 15 additions & 12 deletions autotests/pageObjects/pages/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,21 @@ export class Search extends MobilePage<CustomPageParams> {
}

override async waitForPageLoaded(): Promise<void> {
await waitForAllRequestsComplete(({url}) => {
if (
url.startsWith('https://adservice.google.com/') ||
url.startsWith('https://googleads.g.doubleclick.net/') ||
url.startsWith('https://play.google.com/') ||
url.startsWith('https://static.doubleclick.net/')
) {
return false;
}

return true;
});
await waitForAllRequestsComplete(
({url}) => {
if (
url.startsWith('https://adservice.google.com/') ||
url.startsWith('https://googleads.g.doubleclick.net/') ||
url.startsWith('https://play.google.com/') ||
url.startsWith('https://static.doubleclick.net/')
) {
return false;
}

return true;
},
{maxIntervalBetweenRequestsInMs: this.maxIntervalBetweenRequestsInMs},
);

await waitForInterfaceStabilization(this.pageStabilizationInterval);
}
Expand Down
2 changes: 1 addition & 1 deletion autotests/it.ts → autotests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ import type {Pack} from 'autotests/types/packSpecific';
* Test function that describes the test or the task
* (test does not necessarily contain checks).
*/
export const it = createTestFunction<Pack>(hooks);
export const test = createTestFunction<Pack>(hooks);
4 changes: 2 additions & 2 deletions autotests/tests/e2edReportExample/browserData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */

import {it} from 'autotests';
import {test} from 'autotests';
import {E2edReportExample} from 'autotests/pageObjects/pages';
import {createClientFunction, expect} from 'e2ed';
import {
Expand All @@ -11,7 +11,7 @@ import {
waitForInterfaceStabilization,
} from 'e2ed/actions';

it('correctly read data from browser', {meta: {testId: '14'}}, async () => {
test('correctly read data from browser', {meta: {testId: '14'}}, async () => {
await navigateToPage(E2edReportExample);

await setPageElementsIgnoredOnInterfaceStabilization(['.retry']);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {it} from 'autotests';
import {test} from 'autotests';
import {E2edReportExample} from 'autotests/pageObjects/pages';
import {expect} from 'e2ed';
import {click, navigateToPage} from 'e2ed/actions';

it('custom selector methods', {meta: {testId: '15'}}, async () => {
test('selector custom methods', {meta: {testId: '15'}}, async () => {
const reportPage = await navigateToPage(E2edReportExample);

await expect(reportPage.navigationRetries.exists, 'navigation retries exists').ok();
Expand Down
4 changes: 2 additions & 2 deletions autotests/tests/e2edReportExample/setPageCookies.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {it} from 'autotests';
import {test} from 'autotests';
import {E2edReportExample} from 'autotests/pageObjects/pages';
import {expect} from 'e2ed';
import {navigateToPage} from 'e2ed/actions';
Expand All @@ -14,7 +14,7 @@ const cookie = {
value: 'bar',
} as const;

it('set page cookies', {meta: {testId: '5'}, testIdleTimeout: 35_000}, async () => {
test('set page cookies correctly', {meta: {testId: '5'}, testIdleTimeout: 35_000}, async () => {
await navigateToPage(E2edReportExample, {pageCookies: [cookie]});

const documentCookie = await getDocumentCookie();
Expand Down
23 changes: 23 additions & 0 deletions autotests/tests/expect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {test} from 'autotests';
import {expect} from 'e2ed';
import {assertFunctionThrows, getTimeoutPromise} from 'e2ed/utils';

test('expect function works correctly', {meta: {testId: '16'}}, async () => {
await assertFunctionThrows(async () => {
await expect(1, 'should throws').eql(2);
}, 'throws an error when actual value do not fit expected value');

await assertFunctionThrows(async () => {
await expect(getTimeoutPromise(5_100), 'should failed by timeout').ok();
}, 'throws an timeout error when actual value is a pending promise');

await expect(Promise.resolve('foo'), 'awaits usual promises').eql('foo');

await assertFunctionThrows(async () => {
await expect(
Promise.resolve('foo'),
'throws an error when comparing for equality of values of different types',
// @ts-expect-error: actual value and expected value has different types
).eql(3);
}, 'throws an error when actual value and expected value has different types');
});
57 changes: 31 additions & 26 deletions autotests/tests/internalTypeTests/selectors.skip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,30 @@ import {
locatorIdSelector,
} from 'autotests/selectors';

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

// @ts-expect-error: wrong number of arguments
htmlElementSelector.findByLocatorId();
// @ts-expect-error: wrong type of arguments
htmlElementSelector.findByLocatorId(0);
// ok
htmlElementSelector.findByLocatorId('id');
htmlElementSelector.findByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.findByLocatorId('id').findByLocatorId('id2');
htmlElementSelector.findByLocatorId('id').findByLocatorId('id2') satisfies Selector;
// ok
htmlElementSelector.findByLocatorId('id').find('.test-children');
htmlElementSelector.findByLocatorId('id').find('.test-children') satisfies Selector;
// ok
htmlElementSelector.find('body').findByLocatorId('id');
htmlElementSelector.find('body').findByLocatorId('id') satisfies Selector;

// ok
createSelector('id').findByLocatorId('id').find('body').findByLocatorId('id');
createSelector('id').findByLocatorId('id').find('body').findByLocatorId('id') satisfies Selector;
// ok
createSelectorByCss('id').findByLocatorId('id').find('body').findByLocatorId('id');
createSelectorByCss('id')
.findByLocatorId('id')
.find('body')
.findByLocatorId('id') satisfies Selector;
// ok
locatorIdSelector('id').findByLocatorId('id').find('body').findByLocatorId('id');
locatorIdSelector('id').findByLocatorId('id').find('body').findByLocatorId('id') satisfies Selector;

// @ts-expect-error: wrong number of arguments
locatorIdSelector();
Expand All @@ -32,41 +37,41 @@ locatorIdSelector();
locatorIdSelector(3);

// ok
htmlElementSelector.filterByLocatorId('id');
htmlElementSelector.filterByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.parentByLocatorId('id');
htmlElementSelector.parentByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.childByLocatorId('id');
htmlElementSelector.childByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.siblingByLocatorId('id');
htmlElementSelector.siblingByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.nextSiblingByLocatorId('id');
htmlElementSelector.nextSiblingByLocatorId('id') satisfies Selector;
// ok
htmlElementSelector.prevSiblingByLocatorId('id');
htmlElementSelector.prevSiblingByLocatorId('id') satisfies Selector;

// ok
htmlElementSelector.filterByLocatorParameter('prop', 'value');
htmlElementSelector.filterByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.findByLocatorParameter('prop', 'value');
htmlElementSelector.findByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.parentByLocatorParameter('prop', 'value');
htmlElementSelector.parentByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.childByLocatorParameter('prop', 'value');
htmlElementSelector.childByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.siblingByLocatorParameter('prop', 'value');
htmlElementSelector.siblingByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.nextSiblingByLocatorParameter('prop', 'value');
htmlElementSelector.nextSiblingByLocatorParameter('prop', 'value') satisfies Selector;
// ok
htmlElementSelector.prevSiblingByLocatorParameter('prop', 'value');
htmlElementSelector.prevSiblingByLocatorParameter('prop', 'value') satisfies Selector;

// ok
void htmlElementSelector.getLocatorId();

// TODO: should be an error "wrong number of arguments"
void htmlElementSelector.getLocatorId('foo');

// ok
void htmlElementSelector.hasLocatorId();
// @ts-expect-error: TODO: should be ok
void htmlElementSelector.hasLocatorId() satisfies Promise<boolean>;

// TODO: should be an error "wrong number of arguments"
void htmlElementSelector.hasLocatorId('foo');
Expand All @@ -77,10 +82,10 @@ void htmlElementSelector.hasLocatorParameter();
// @ts-expect-error: wrong number of arguments
void htmlElementSelector.getLocatorParameter();

// ok
void htmlElementSelector.getLocatorParameter('prop');
// ok
void htmlElementSelector.hasLocatorParameter('prop');
// @ts-expect-error: TODO: should be ok
void htmlElementSelector.getLocatorParameter('prop') satisfies Promise<string | null>;
// @ts-expect-error: TODO: should be ok
void htmlElementSelector.hasLocatorParameter('prop') satisfies Promise<boolean>;

// ok
htmlElementSelector.getDescription() satisfies string | undefined;
Loading

0 comments on commit d34eeb0

Please sign in to comment.