Skip to content

Commit

Permalink
FI-714 feat: add functions waitForRequestToRoute/`waitForResponseTo…
Browse files Browse the repository at this point in the history
…Route`

feat: add `RequestWithUtcTimeInMs` public type
fix: correct definition of `ResponseWithRequest` type
fix: use `ResponseWithRequest` for `waitForResponse`
fix: use `RequestWithUtcTimeInMs` for `waitForRequest`
fix: add `skipLogs` option to `waitForRequest`/`waitForResponse`
fix: tests for timeout in `expect` functions
fix: add check that methods `isMatchUrl` and `getParamsFromUrl` are consistent
fix: printing of `E2edError` after `replaceFields` applying
chore: add eslint plugin `typescript-sort-keys/recommended`
tests: add tests for `waitForResponseToRoute` function
  • Loading branch information
uid11 committed Dec 16, 2023
1 parent 3fe7406 commit 29d9c39
Show file tree
Hide file tree
Showing 87 changed files with 850 additions and 300 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extends:
- airbnb-base
- plugin:import/recommended
- plugin:import/typescript
- plugin:typescript-sort-keys/recommended
- plugin:@typescript-eslint/recommended
- plugin:@typescript-eslint/recommended-requiring-type-checking
- plugin:@typescript-eslint/strict
Expand All @@ -13,7 +14,7 @@ parser: '@typescript-eslint/parser'
parserOptions:
project: ./tsconfig.json
sourceType: module
plugins: [simple-import-sort]
plugins: [simple-import-sort, typescript-sort-keys]
root: true
ignorePatterns: [bin/forks/*/package, build, local]
rules:
Expand Down
6 changes: 1 addition & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

## [v0.15.17](https://github.com/joomcode/e2ed/tree/v0.15.17) (2023-12-13)

[Full Changelog](https://github.com/joomcode/e2ed/compare/v0.15.16...v0.15.17)

## [v0.15.16](https://github.com/joomcode/e2ed/tree/v0.15.16) (2023-12-13)

[Full Changelog](https://github.com/joomcode/e2ed/compare/v0.15.15...v0.15.16)
[Full Changelog](https://github.com/joomcode/e2ed/compare/v0.15.15...v0.15.17)

- [fix: update minimal version of TypeScript in `package-lock.json`](https://github.com/joomcode/e2ed/commit/e2e384132d95e577a6eafda727ebc8424e62d4ac) ([uid11](https://github.com/uid11))
- [Merge pull request #52 from joomcode/fix/respect-errors-from-doBeforePack-doAfterPack](https://github.com/joomcode/e2ed/commit/c3238464719f2ad50b25a606d427b0327bbb6d37) ([uid11](https://github.com/uid11))
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ After the run, a detailed HTML report and a summary lite report in JSON format a
## Adding e2ed to a project

Prerequisites: [node](https://nodejs.org/en/) >=16,
[TypeScript](https://www.typescriptlang.org/) >=4.5.
[TypeScript](https://www.typescriptlang.org/) >=4.8.

All commands below are run from the root directory of the project.

Expand Down
2 changes: 1 addition & 1 deletion autotests/configurator/logFieldReplacer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const logFieldReplacer: FieldReplacer = (path, value) => {
return getStringTrimmedToMaxLength(value, 500);
}

const key = path[path.length - 1];
const key = path.at(-1);

if (key === 'responseBody' && value && typeof value === 'object') {
const {payload, ...responseBodyWithoutPayload} = value as {payload?: unknown};
Expand Down
1 change: 1 addition & 0 deletions autotests/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export {createDevice} from './device';
export {createUser} from './user';
export {addUser, getUsers} from './worker';
32 changes: 32 additions & 0 deletions autotests/entities/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {createClientFunction} from 'e2ed';
import {log} from 'e2ed/utils';

import type {UserWorker} from 'autotests/types';

const clientGetUsers = createClientFunction(
(delay: number) =>
fetch(`https://reqres.in/api/users?delay=${delay}`, {method: 'GET'}).then((res) => res.json()),
{name: 'getUsers', timeout: 6_000},
);

/**
* Adds user-worker.
*/
export const addUser = createClientFunction(
(user: UserWorker, delay?: number) =>
fetch(`https://reqres.in/api/users${delay ? `?delay=${delay}` : ''}`, {
body: JSON.stringify(user),
headers: {'Content-Type': 'application/json; charset=UTF-8'},
method: 'POST',
}).then((res) => res.json()),
{name: 'addUser', timeout: 2_000},
);

/**
* Get list of user-workers.
*/
export const getUsers = (delay: number): Promise<unknown> => {
log(`Send API request with delay = ${delay}s`);

return clientGetUsers(delay);
};
4 changes: 2 additions & 2 deletions autotests/hooks/navigateTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {navigateToUrl} from 'e2ed/actions';
import type {NavigateTo} from 'autotests/types';

/**
* This hook is used inside the navigateToPage function to navigate to the page
* This hook is used inside the `navigateToPage` function to navigate to the page
* under the already computed url.
* Use context (e2ed/context) to get parameters inside a hook.
* Use context (`e2ed/context`) to get parameters inside a hook.
*/
export const navigateTo: NavigateTo = async (url) => {
const pageCookies = getPageCookies();
Expand Down
48 changes: 48 additions & 0 deletions autotests/routes/apiRoutes/AddUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {ApiRoute} from 'autotests/routes';
import {assertValueIsTrue} from 'e2ed/utils';

import type {ApiAddUserRequest, ApiAddUserResponse} from 'autotests/types';
import type {Url} from 'e2ed/types';

type Params = Readonly<{delay?: number}>;

const pathStart = '/api/users';

/**
* Client API route for adding user-worker.
*/
export class AddUser extends ApiRoute<Params, ApiAddUserRequest, ApiAddUserResponse> {
static override getParamsFromUrl(url: Url): Params {
const urlObject = new URL(url);

assertValueIsTrue(
urlObject.pathname.startsWith(pathStart),
'url pathname starts with correct path',
{urlObject},
);

const delay: number | undefined = Number(urlObject.searchParams.get('delay'));

if (!Number.isNaN(delay)) {
assertValueIsTrue(Number.isInteger(delay), 'url has correct delay', {delay, urlObject});

return {delay};
}

return {};
}

getMethod(): 'POST' {
return 'POST';
}

override getOrigin(): Url {
return 'https://reqres.in' as Url;
}

getPath(): string {
const {delay} = this.routeParams;

return delay === undefined ? pathStart : `${pathStart}?delay=${delay}`;
}
}
1 change: 1 addition & 0 deletions autotests/routes/apiRoutes/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {AddUser} from './AddUser';
export {CreateDevice} from './CreateDevice';
export {CreateProduct} from './CreateProduct';
export {GetUser} from './GetUser';
Expand Down
6 changes: 3 additions & 3 deletions autotests/tests/e2edReportExample/browserData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ test('correctly read data from browser', {meta: {testId: '14'}}, async () => {

await expect(
error[0] === 'error' && info[0] === 'info' && log[0] === 'log' && warn[0] === 'warn',
'`getBrowserConsoleMessages` read all of messages',
'getBrowserConsoleMessages read all of messages',
).eql(true);

await expect(log[1], '`setPageElementsIgnoredOnInterfaceStabilization` works correct').eql(
await expect(log[1], 'setPageElementsIgnoredOnInterfaceStabilization works correct').eql(
'.retry',
);

const jsErrors = await getBrowserJsErrors();

await expect(
jsErrors.length === 2 && jsErrors[0]?.message === 'foo' && jsErrors[1]?.message === 'bar',
'`getBrowserJsErrors` read JS errors',
'getBrowserJsErrors read JS errors',
).eql(true);
});
10 changes: 9 additions & 1 deletion autotests/tests/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ test('expect function works correctly', {meta: {testId: '16'}}, async () => {
await expect(1, 'should throws').eql(2);
}, 'throws an error when actual value do not fit expected value');

await expect(
getTimeoutPromise(5_900).then(() => true),
'should not failed by timeout',
).ok();

await assertFunctionThrows(async () => {
await expect(getTimeoutPromise(5_100), 'should failed by timeout').ok();
await expect(
getTimeoutPromise(6_100).then(() => true),
'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');
Expand Down
20 changes: 19 additions & 1 deletion autotests/tests/internalTypeTests/waitForEvents.skip.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {waitForRequest, waitForResponse} from 'e2ed/actions';
import {AddUser, GetUser} from 'autotests/routes/apiRoutes';
import {waitForRequest, waitForRequestToRoute, waitForResponse} from 'e2ed/actions';

// ok
void waitForRequest(() => false);
Expand Down Expand Up @@ -41,3 +42,20 @@ void waitForRequest(({method, query, requestBody, requestHeaders, url}) => true)

// eslint-disable-next-line @typescript-eslint/no-unused-vars
void waitForResponse(({responseBody, responseHeaders, statusCode}) => true);

// ok
void waitForRequestToRoute(AddUser);

// ok
void waitForRequestToRoute(AddUser, {
predicate: ({delay}, {requestBody, url}) => {
if (delay !== undefined && delay > 0 && requestBody.job !== 'foo') {
return url.startsWith('https');
}

return false;
},
});

// @ts-expect-error: waitForRequestToRoute does not accept routes without `getParamsFromUrl` method
void waitForRequestToRoute(GetUser);
2 changes: 1 addition & 1 deletion autotests/tests/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ test(

await assertFunctionThrows(async () => {
await request(GetUsers, {maxRetriesCount: 1, timeout: 2_000});
}, '`request` function throws an error on timeout');
}, 'request function throws an error on timeout');
},
);
18 changes: 2 additions & 16 deletions autotests/tests/waitForAllRequestsComplete.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {test} from 'autotests';
import {createClientFunction} from 'e2ed';
import {getUsers} from 'autotests/entities';
import {waitForAllRequestsComplete, waitForTimeout} from 'e2ed/actions';
import {assertFunctionThrows, E2edError, log} from 'e2ed/utils';
import {assertFunctionThrows, E2edError} from 'e2ed/utils';

test(
'waitForAllRequestsComplete works correct with timeout and predicate in base cases',
Expand Down Expand Up @@ -31,20 +31,6 @@ test(

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

const clientGetUsers = createClientFunction(
(delay: number) =>
fetch(`https://reqres.in/api/users?delay=${delay}`, {method: 'GET'}).then((res) =>
res.json(),
),
{name: 'getUsers', timeout: 6_000},
);

const getUsers = (delay: number): Promise<unknown> => {
log(`Send API request with delay = ${delay}000ms`);

return clientGetUsers(delay);
};

void getUsers(2);

await assertFunctionThrows(
Expand Down
39 changes: 21 additions & 18 deletions autotests/tests/waitForRequest.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
import {test} from 'autotests';
import {createClientFunction, expect} from 'e2ed';
import {waitForRequest} from 'e2ed/actions';
import {addUser} from 'autotests/entities';
import {AddUser} from 'autotests/routes/apiRoutes';
import {expect} from 'e2ed';
import {waitForRequest, waitForRequestToRoute} from 'e2ed/actions';
import {assertFunctionThrows} from 'e2ed/utils';

import type {UserWorker} from 'autotests/types';
import type {Request} from 'e2ed/types';

type Body = Readonly<{job: string; name: string}> | undefined;
type Body = UserWorker | undefined;

const worker: UserWorker = {job: 'leader', name: 'John'};

test(
'waitForRequest gets correct request body and rejects on timeout',
{meta: {testId: '2'}, testIdleTimeout: 3_000},
async () => {
const addUser = createClientFunction(
() =>
fetch('https://reqres.in/api/users', {
body: JSON.stringify({job: 'leader', name: 'John'}),
headers: {'Content-Type': 'application/json; charset=UTF-8'},
method: 'POST',
}).then((res) => res.json()),
{name: 'addUser', timeout: 2_000},
);

void addUser();
void addUser(worker);

const request = await waitForRequest(
({requestBody}: Request<Body>) => requestBody?.name === 'John',
);

await expect(request.requestBody, 'request has correct body').eql({
job: 'leader',
name: 'John',
});
await expect(request.requestBody, 'request has correct body').eql(worker);

await assertFunctionThrows(async () => {
await waitForRequest(() => false, {timeout: 100});
}, '`waitForRequest` throws an error on timeout');

void addUser(worker, 1);

const {request: secondRequest, routeParams} = await waitForRequestToRoute(AddUser);

await expect(
secondRequest.requestBody,
'request from waitForRequestToRoute has correct body',
).eql(worker);

await expect(routeParams, 'routeParams from waitForRequestToRoute is correct').eql({delay: 1});
},
);
40 changes: 14 additions & 26 deletions autotests/tests/waitForResponse.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
import {test} from 'autotests';
import {createClientFunction, expect} from 'e2ed';
import {addUser} from 'autotests/entities';
import {expect} from 'e2ed';
import {waitForResponse} from 'e2ed/actions';
import {assertFunctionThrows} from 'e2ed/utils';

import type {UserWorker} from 'autotests/types';
import type {Response} from 'e2ed/types';

type Body = Readonly<{job: string; name: string}> | undefined;
type Body = UserWorker | undefined;

const worker: UserWorker = {job: 'leader', name: 'John'};

test(
'waitForResponse gets correct response body and rejects on timeout',
{meta: {testId: '3'}, testIdleTimeout: 3_000},
async () => {
const addUser = createClientFunction(
() =>
fetch('https://reqres.in/api/users', {
body: JSON.stringify({job: 'leader', name: 'John'}),
headers: {'Content-Type': 'application/json; charset=UTF-8'},
method: 'POST',
}).then((res) => res.json()),
{name: 'addUser'},
);

void addUser();
void addUser(worker);

let response = await waitForResponse(
({responseBody}: Response<Body>) => responseBody?.name === 'John',
let response = await waitForResponse<Response<Body>>(
({responseBody}) => responseBody?.name === 'John',
);

await expect(response.responseBody, 'response has correct body').contains({
job: 'leader',
name: 'John',
});
await expect(response.responseBody, 'response has correct body').contains(worker);

await assertFunctionThrows(async () => {
await waitForResponse(() => false, {timeout: 100});
}, '`waitForResponse` throws an error on timeout');

void addUser();
void addUser(worker);

response = await waitForResponse(
({request}: Response<Body>) => request?.url === 'https://reqres.in/api/users',
response = await waitForResponse<Response<Body>>(
({request}) => request.url === 'https://reqres.in/api/users',
);

await expect(response.responseBody, 'second response has correct body').contains({
job: 'leader',
name: 'John',
});
await expect(response.responseBody, 'second response has correct body').contains(worker);
},
);
12 changes: 12 additions & 0 deletions autotests/types/api/AddUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type {ApiGetUsersResponse, UserWorker} from 'autotests/types';
import type {Request} from 'e2ed/types';

/**
* API request for adding user-worker endpoint.
*/
export type ApiAddUserRequest = Request<UserWorker>;

/**
* API response for adding user-worker endpoint.
*/
export type ApiAddUserResponse = ApiGetUsersResponse;
Loading

0 comments on commit 29d9c39

Please sign in to comment.