Skip to content

Commit

Permalink
FI-1512 feat: restore waitForAllRequestsComplete action
Browse files Browse the repository at this point in the history
  • Loading branch information
uid11 committed Nov 15, 2024
1 parent 5b1c35b commit 6cac69f
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 12 deletions.
5 changes: 2 additions & 3 deletions src/utils/promise/getTimeoutPromise.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {e2edEnvironment} from '../../constants/internal';
import {isDebug} from '../../constants/internal';

const maxTimeoutInMs = 3600_000;

Expand All @@ -7,6 +7,5 @@ const maxTimeoutInMs = 3600_000;
*/
export const getTimeoutPromise = (delayInMs: number): Promise<void> =>
new Promise((resolve) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
setTimeout(resolve, e2edEnvironment.E2ED_DEBUG ? maxTimeoutInMs : delayInMs);
setTimeout(resolve, isDebug ? maxTimeoutInMs : delayInMs);
});
16 changes: 16 additions & 0 deletions src/utils/requestHooks/getRequestHookContextId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type {Request as PlaywrightRequest} from '@playwright/test';

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

/**
* Get `RequestHookContextId` from Playwright request.
* @internal
*/
export const getRequestHookContextId = (
playwrightRequest: PlaywrightRequest,
): RequestHookContextId => {
const request = playwrightRequest as {_guid?: string; url: () => string};

// eslint-disable-next-line no-underscore-dangle
return String(request._guid) as RequestHookContextId;
};
2 changes: 2 additions & 0 deletions src/utils/requestHooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export {getMainRequestOptions} from './getMainRequestOptions';
/** @internal */
export {getRequestFromPlaywrightRequest} from './getRequestFromPlaywrightRequest';
/** @internal */
export {getRequestHookContextId} from './getRequestHookContextId';
/** @internal */
export {getResponseFromPlaywrightResponse} from './getResponseFromPlaywrightResponse';
5 changes: 2 additions & 3 deletions src/utils/retry/getTestsSubprocessForkOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {e2edEnvironment} from '../../constants/internal';
import {e2edEnvironment, isDebug} from '../../constants/internal';

import {assertNumberIsPositiveInteger} from '../asserts';

Expand All @@ -9,8 +9,7 @@ import type {ForkOptions} from 'node:child_process';
* @internal
*/
export const getTestsSubprocessForkOptions = (): ForkOptions | undefined => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!e2edEnvironment.E2ED_DEBUG) {
if (!isDebug) {
return undefined;
}

Expand Down
20 changes: 18 additions & 2 deletions src/utils/test/preparePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import {getIsPageNavigatingNow, setIsPageNavigatingNow} from '../../context/isPa
import {getJsErrorsFromContext} from '../../context/jsError';
import {getNavigationDelay} from '../../context/navigationDelay';
import {getOnResponseCallbacks} from '../../context/onResponseCallbacks';
import {getWaitForEventsState} from '../../context/waitForEventsState';

import {getResponseFromPlaywrightResponse} from '../requestHooks';
import {
getRequestFromPlaywrightRequest,
getRequestHookContextId,
getResponseFromPlaywrightResponse,
} from '../requestHooks';
import {addNotCompleteRequest, completeRequest} from '../waitForEvents';

import type {
ConsoleMessage as PlaywrightConsoleMessage,
Expand All @@ -27,6 +33,7 @@ export const preparePage = async (page: Page): Promise<() => Promise<void>> => {
const consoleMessages = getConsoleMessagesFromContext() as ConsoleMessage[];
const jsErrors = getJsErrorsFromContext() as JsError[];
const navigationDelay = getNavigationDelay();
const waitForEventsState = getWaitForEventsState();

await page.route(
() => navigationDelay.promise !== undefined,
Expand Down Expand Up @@ -64,7 +71,7 @@ export const preparePage = async (page: Page): Promise<() => Promise<void>> => {
jsErrors.push({dateTimeInIso, error});
});

const requestListener = AsyncLocalStorage.bind((newRequest: PlaywrightRequest) => {
const requestListener = AsyncLocalStorage.bind(async (newRequest: PlaywrightRequest) => {
const isNavigationRequest = newRequest.isNavigationRequest();
const isPageNavigatingNow = getIsPageNavigatingNow();

Expand All @@ -77,6 +84,11 @@ export const preparePage = async (page: Page): Promise<() => Promise<void>> => {

setIsPageNavigatingNow(navigationRequestsCount > 0);
}

const request = getRequestFromPlaywrightRequest(newRequest);
const requestHookContextId = getRequestHookContextId(newRequest);

await addNotCompleteRequest(request, requestHookContextId, waitForEventsState);
});

const responseListener = AsyncLocalStorage.bind((newResponse: PlaywrightResponse) => {
Expand All @@ -92,6 +104,10 @@ export const preparePage = async (page: Page): Promise<() => Promise<void>> => {
});

const requestfinishedListener = AsyncLocalStorage.bind(async (request: PlaywrightRequest) => {
const requestHookContextId = getRequestHookContextId(request);

completeRequest(requestHookContextId, waitForEventsState);

const onResponseCallbacks = getOnResponseCallbacks();

if (onResponseCallbacks.length === 0) {
Expand Down
5 changes: 2 additions & 3 deletions src/utils/tests/runTests.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {fork} from 'node:child_process';

import {CONFIG_PATH, e2edEnvironment} from '../../constants/internal';
import {CONFIG_PATH, e2edEnvironment, isDebug} from '../../constants/internal';

import {getFullPackConfig} from '../config';
import {getRunLabel, setRunLabel} from '../environment';
Expand Down Expand Up @@ -43,8 +43,7 @@ export const runTests = async ({runLabel}: RunRetryOptions): Promise<void> => {
await new Promise<void>((resolve, reject) => {
const playwrightArgs = ['test', `--config=${CONFIG_PATH}`];

// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (e2edEnvironment.E2ED_DEBUG) {
if (isDebug) {
// playwrightArgs.unshift('--node-options=--inspect-brk');
e2edEnvironment.PWDEBUG = 'console';
playwrightArgs.push('--debug');
Expand Down
23 changes: 23 additions & 0 deletions src/utils/waitForEvents/addNotCompleteRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {processAllRequestsCompletePredicates} from './processAllRequestsCompletePredicates';

import type {
RequestHookContextId,
RequestWithUtcTimeInMs,
WaitForEventsState,
} from '../../types/internal';

/**
* Adds new request to hash of not complete requests.
* @internal
*/
export const addNotCompleteRequest = async (
request: RequestWithUtcTimeInMs,
requestHookContextId: RequestHookContextId,
waitForEventsState: WaitForEventsState,
): Promise<void> => {
const {hashOfNotCompleteRequests} = waitForEventsState;

hashOfNotCompleteRequests[requestHookContextId] = request;

await processAllRequestsCompletePredicates(requestHookContextId, waitForEventsState);
};
26 changes: 26 additions & 0 deletions src/utils/waitForEvents/completeRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {RequestHookContextId, WaitForEventsState} from '../../types/internal';

/**
* Completes request on getting of its response.
* @internal
*/
export const completeRequest = (
requestHookContextId: RequestHookContextId,
waitForEventsState: WaitForEventsState,
): void => {
const {allRequestsCompletePredicates, hashOfNotCompleteRequests} = waitForEventsState;

// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete hashOfNotCompleteRequests[requestHookContextId];

for (const allRequestsCompletePredicateWithPromise of allRequestsCompletePredicates) {
const {requestHookContextIds, setResolveTimeout} = allRequestsCompletePredicateWithPromise;
const requestWasWaited = requestHookContextIds.has(requestHookContextId);

requestHookContextIds.delete(requestHookContextId);

if (requestHookContextIds.size === 0 && requestWasWaited) {
setResolveTimeout();
}
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
type HashOfNotCompleteRequests = WaitForEventsState['hashOfNotCompleteRequests'];

/**
* Get initial array of requestHookContextId for predicate of waitForAllRequestsComplete function.
* Get initial set of `requestHookContextId` for predicate of `waitForAllRequestsComplete` function.
* @internal
*/
export const getInitialIdsForAllRequestsCompletePredicate = async (
Expand Down
4 changes: 4 additions & 0 deletions src/utils/waitForEvents/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
/** @internal */
export {addNotCompleteRequest} from './addNotCompleteRequest';
/** @internal */
export {completeRequest} from './completeRequest';
/** @internal */
export {getInitialIdsForAllRequestsCompletePredicate} from './getInitialIdsForAllRequestsCompletePredicate';
/** @internal */
export {getWaitForResponsePredicate} from './getWaitForResponsePredicate';
Expand Down
46 changes: 46 additions & 0 deletions src/utils/waitForEvents/processAllRequestsCompletePredicate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {E2edError} from '../error';

import type {
AllRequestsCompletePredicateWithPromise,
RequestHookContextId,
RequestWithUtcTimeInMs,
} from '../../types/internal';

/**
* Processes one `waitForAllRequestsComplete` predicate for new request.
* Returns `true` if the promise was fulfilled, and `false` otherwise.
* @internal
*/
export const processAllRequestsCompletePredicate = async (
allRequestsCompletePredicateWithPromise: AllRequestsCompletePredicateWithPromise,
request: RequestWithUtcTimeInMs,
requestHookContextId: RequestHookContextId,
): Promise<boolean> => {
const {clearResolveTimeout, predicate, reject, requestHookContextIds} =
allRequestsCompletePredicateWithPromise;

try {
const isRequestMatched = await predicate(request);

if (isRequestMatched !== true) {
return false;
}

clearResolveTimeout();

requestHookContextIds.add(requestHookContextId);
} catch (cause) {
clearResolveTimeout();

const error = new E2edError(
'waitForAllRequestsComplete promise rejected due to error in predicate function',
{cause, predicate, request},
);

reject(error);

return true;
}

return false;
};
35 changes: 35 additions & 0 deletions src/utils/waitForEvents/processAllRequestsCompletePredicates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {processAllRequestsCompletePredicate} from './processAllRequestsCompletePredicate';

import type {RequestHookContextId, WaitForEventsState} from '../../types/internal';

/**
* Processes `waitForAllRequestsComplete` predicates for new request.
* @internal
*/
export const processAllRequestsCompletePredicates = async (
requestHookContextId: RequestHookContextId,
waitForEventsState: WaitForEventsState,
): Promise<void> => {
const {allRequestsCompletePredicates, hashOfNotCompleteRequests} = waitForEventsState;
const request = hashOfNotCompleteRequests[requestHookContextId];

if (request === undefined) {
return;
}

const promises = [...allRequestsCompletePredicates].map(
async (allRequestsCompletePredicateWithPromise) => {
const isFulfilled = await processAllRequestsCompletePredicate(
allRequestsCompletePredicateWithPromise,
request,
requestHookContextId,
);

if (isFulfilled) {
allRequestsCompletePredicates.delete(allRequestsCompletePredicateWithPromise);
}
},
);

await Promise.all(promises);
};

0 comments on commit 6cac69f

Please sign in to comment.