Skip to content

Commit 7849902

Browse files
committed
PRO-9847 fix: options of waitForRequest/waitForResponse actions
fix: skip `waitForRequest`/`waitForResponse` errors after end of test fix: handle `uncaughtException`/`unhandledRejection` global errors
1 parent 57865f1 commit 7849902

20 files changed

Lines changed: 153 additions & 29 deletions

src/README.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,17 @@ Modules in the dependency graph should only import the modules above them:
3939
32. `utils/promise`
4040
33. `utils/resourceUsage`
4141
34. `utils/fs`
42-
35. `utils/tests`
43-
36. `utils/end`
44-
37. `utils/pack`
45-
38. `useContext`
46-
39. `context`
47-
40. `utils/apiStatistics`
48-
41. `utils/selectors`
49-
42. `selectors`
50-
43. `utils/log`
51-
44. `utils/waitForEvents`
52-
45. `utils/expect`
53-
46. `expect`
54-
47. ...
42+
35. `utils/getGlobalErrorHandler`
43+
36. `utils/tests`
44+
37. `utils/end`
45+
38. `utils/pack`
46+
39. `useContext`
47+
40. `context`
48+
41. `utils/apiStatistics`
49+
42. `utils/selectors`
50+
43. `selectors`
51+
44. `utils/log`
52+
45. `utils/waitForEvents`
53+
46. `utils/expect`
54+
47. `expect`
55+
48. ...

src/actions/waitFor/waitForNewTab.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,22 @@ import type {InternalTab, Tab, Trigger, UtcTimeInMs} from '../../types/internal'
99

1010
type Options = Readonly<{skipLogs?: boolean; timeout?: number}>;
1111

12-
type WaitForNewTab = ((trigger: Trigger, options?: Options) => Promise<Tab>) &
12+
type WaitForNewTab = ((trigger: Trigger | undefined, options?: Options) => Promise<Tab>) &
1313
((options?: Options) => Promise<Tab>);
1414

1515
/**
1616
* Waits for opening of new tab and returns this tab.
1717
*/
1818
export const waitForNewTab = (async (
19-
triggerOrOptions?: Options | Trigger,
19+
triggerOrOptions?: Options | Trigger | undefined,
2020
options?: Options,
2121
): Promise<Tab> => {
2222
const startTimeInMs = Date.now() as UtcTimeInMs;
2323

2424
const context = getPlaywrightPage().context();
2525
const trigger = typeof triggerOrOptions === 'function' ? triggerOrOptions : undefined;
26-
const finalOptions = typeof triggerOrOptions === 'function' ? options : triggerOrOptions;
26+
const finalOptions =
27+
typeof triggerOrOptions === 'function' ? options : (triggerOrOptions ?? options);
2728

2829
const timeout = finalOptions?.timeout ?? getFullPackConfig().navigationTimeout;
2930
const timeoutWithUnits = getDurationWithUnits(timeout);

src/actions/waitFor/waitForRequest.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {LogEventType} from '../../constants/internal';
2+
import {getTestRunPromise} from '../../context/testRunPromise';
23
import {getPlaywrightPage} from '../../useContext';
34
import {getFullPackConfig} from '../../utils/config';
45
import {E2edError} from '../../utils/error';
@@ -33,15 +34,16 @@ type Options = Readonly<{skipLogs?: boolean; timeout?: number}>;
3334
*/
3435
export const waitForRequest = (async <SomeRequest extends Request>(
3536
predicate: RequestPredicate<SomeRequest>,
36-
triggerOrOptions?: Options | Trigger,
37+
triggerOrOptions?: Options | Trigger | undefined,
3738
options?: Options,
3839
): Promise<RequestWithUtcTimeInMs<SomeRequest>> => {
3940
const startTimeInMs = Date.now() as UtcTimeInMs;
4041

4142
setCustomInspectOnFunction(predicate);
4243

4344
const trigger = typeof triggerOrOptions === 'function' ? triggerOrOptions : undefined;
44-
const finalOptions = typeof triggerOrOptions === 'function' ? options : triggerOrOptions;
45+
const finalOptions =
46+
typeof triggerOrOptions === 'function' ? options : (triggerOrOptions ?? options);
4547

4648
const timeout = finalOptions?.timeout ?? getFullPackConfig().waitForRequestTimeout;
4749

@@ -50,6 +52,13 @@ export const waitForRequest = (async <SomeRequest extends Request>(
5052
}
5153

5254
const page = getPlaywrightPage();
55+
const testRunPromise = getTestRunPromise();
56+
57+
let isTestRunCompleted = false;
58+
59+
void testRunPromise.then(() => {
60+
isTestRunCompleted = true;
61+
});
5362

5463
const promise = page
5564
.waitForRequest(
@@ -73,7 +82,14 @@ export const waitForRequest = (async <SomeRequest extends Request>(
7382
.then(
7483
(playwrightRequest) =>
7584
getRequestFromPlaywrightRequest(playwrightRequest) as RequestWithUtcTimeInMs<SomeRequest>,
76-
);
85+
)
86+
.catch((error: unknown) => {
87+
if (isTestRunCompleted) {
88+
return new Promise<RequestWithUtcTimeInMs<SomeRequest>>(() => {});
89+
}
90+
91+
throw error;
92+
});
7793

7894
const timeoutWithUnits = getDurationWithUnits(timeout);
7995

src/actions/waitFor/waitForRequestToRoute.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ export const waitForRequestToRoute = (async <
5353
SomeResponse extends Response,
5454
>(
5555
Route: ApiRouteClassTypeWithGetParamsFromUrl<RouteParams, SomeRequest, SomeResponse>,
56-
triggerOrOptions?: Options<RouteParams, SomeRequest> | Trigger,
56+
triggerOrOptions?: Options<RouteParams, SomeRequest> | Trigger | undefined,
5757
options?: Options<RouteParams, SomeRequest>,
5858
): Return<RouteParams, SomeRequest> => {
5959
const startTimeInMs = Date.now() as UtcTimeInMs;
6060

6161
const trigger = typeof triggerOrOptions === 'function' ? triggerOrOptions : undefined;
62-
const finalOptions = typeof triggerOrOptions === 'function' ? options : triggerOrOptions;
62+
const finalOptions =
63+
typeof triggerOrOptions === 'function' ? options : (triggerOrOptions ?? options);
6364

6465
const {predicate = () => true} = finalOptions ?? {};
6566
const timeout = finalOptions?.timeout ?? getFullPackConfig().waitForRequestTimeout;

src/actions/waitFor/waitForResponse.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {AsyncLocalStorage} from 'node:async_hooks';
22

33
import {LogEventType} from '../../constants/internal';
4+
import {getTestRunPromise} from '../../context/testRunPromise';
45
import {getPlaywrightPage} from '../../useContext';
56
import {getFullPackConfig} from '../../utils/config';
67
import {setCustomInspectOnFunction} from '../../utils/fn';
@@ -39,15 +40,16 @@ export const waitForResponse = (async <
3940
SomeResponse extends Response = Response,
4041
>(
4142
predicate: ResponsePredicate<SomeRequest, SomeResponse>,
42-
triggerOrOptions?: Options | Trigger,
43+
triggerOrOptions?: Options | Trigger | undefined,
4344
options?: Options,
4445
): Promise<ResponseWithRequest<SomeRequest, SomeResponse>> => {
4546
const startTimeInMs = Date.now() as UtcTimeInMs;
4647

4748
setCustomInspectOnFunction(predicate);
4849

4950
const trigger = typeof triggerOrOptions === 'function' ? triggerOrOptions : undefined;
50-
const finalOptions = typeof triggerOrOptions === 'function' ? options : triggerOrOptions;
51+
const finalOptions =
52+
typeof triggerOrOptions === 'function' ? options : (triggerOrOptions ?? options);
5153

5254
const timeout = finalOptions?.timeout ?? getFullPackConfig().waitForResponseTimeout;
5355

@@ -56,6 +58,13 @@ export const waitForResponse = (async <
5658
}
5759

5860
const page = getPlaywrightPage();
61+
const testRunPromise = getTestRunPromise();
62+
63+
let isTestRunCompleted = false;
64+
65+
void testRunPromise.then(() => {
66+
isTestRunCompleted = true;
67+
});
5968

6069
const promise = page
6170
.waitForResponse(
@@ -73,7 +82,14 @@ export const waitForResponse = (async <
7382
getResponseFromPlaywrightResponse(playwrightResponse) as Promise<
7483
ResponseWithRequest<SomeRequest, SomeResponse>
7584
>,
76-
);
85+
)
86+
.catch((error: unknown) => {
87+
if (isTestRunCompleted) {
88+
return new Promise<ResponseWithRequest<SomeRequest, SomeResponse>>(() => {});
89+
}
90+
91+
throw error;
92+
});
7793

7894
const timeoutWithUnits = getDurationWithUnits(timeout);
7995

src/actions/waitFor/waitForResponseToRoute.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ export const waitForResponseToRoute = (async <
5353
SomeResponse extends Response,
5454
>(
5555
Route: ApiRouteClassTypeWithGetParamsFromUrl<RouteParams, SomeRequest, SomeResponse>,
56-
triggerOrOptions?: Options<RouteParams, SomeRequest, SomeResponse> | Trigger,
56+
triggerOrOptions?: Options<RouteParams, SomeRequest, SomeResponse> | Trigger | undefined,
5757
options?: Options<RouteParams, SomeRequest, SomeResponse>,
5858
): Return<RouteParams, SomeRequest, SomeResponse> => {
5959
const startTimeInMs = Date.now() as UtcTimeInMs;
6060

6161
const trigger = typeof triggerOrOptions === 'function' ? triggerOrOptions : undefined;
62-
const finalOptions = typeof triggerOrOptions === 'function' ? options : triggerOrOptions;
62+
const finalOptions =
63+
typeof triggerOrOptions === 'function' ? options : (triggerOrOptions ?? options);
6364

6465
const {predicate = () => true} = finalOptions ?? {};
6566
const timeout = finalOptions?.timeout ?? getFullPackConfig().waitForResponseTimeout;

src/bin/runE2edInDockerEnvironment.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import {RunEnvironment, setRunEnvironment} from '../configurator';
22
import {setProcessEndHandlers} from '../utils/end';
33
import {registerEndE2edRunEvent, registerStartE2edRunEvent} from '../utils/events';
44
import {logStartE2edError} from '../utils/generalLog';
5+
import {getGlobalErrorHandler} from '../utils/getGlobalErrorHandler';
56
import {runPackWithRetries} from '../utils/retry';
67

8+
process.on('uncaughtException', getGlobalErrorHandler('E2edUncaughtException'));
9+
process.on('unhandledRejection', getGlobalErrorHandler('E2edUnhandledRejection'));
10+
711
setProcessEndHandlers();
812
setRunEnvironment(RunEnvironment.Docker);
913

src/bin/runE2edInLocalEnvironment.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {setProcessEndHandlers} from '../utils/end';
44
import {setPathToPack} from '../utils/environment';
55
import {registerEndE2edRunEvent, registerStartE2edRunEvent} from '../utils/events';
66
import {logStartE2edError} from '../utils/generalLog';
7+
import {getGlobalErrorHandler} from '../utils/getGlobalErrorHandler';
78
import {runPackWithArgs} from '../utils/pack';
89
import {setUiMode} from '../utils/uiMode';
910

@@ -21,6 +22,9 @@ const [pathToPack] = process.argv.splice(2, 1);
2122

2223
assertValueIsDefined(pathToPack, 'pathToPack is defined', {argv: process.argv});
2324

25+
process.on('uncaughtException', getGlobalErrorHandler('E2edUncaughtException'));
26+
process.on('unhandledRejection', getGlobalErrorHandler('E2edUnhandledRejection'));
27+
2428
setPathToPack(pathToPack as FilePathFromRoot);
2529
setProcessEndHandlers();
2630
setRunEnvironment(RunEnvironment.Local);

src/bin/runTestsSubprocess.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {getFullPackConfig} from '../utils/config';
2+
import {getGlobalErrorHandler} from '../utils/getGlobalErrorHandler';
23
import {exitFromTestsSubprocess, runTests} from '../utils/tests';
34

45
import type {RunRetryOptions} from '../types/internal';
@@ -9,6 +10,9 @@ const testIdleTimeoutObject = setInterval(() => process.send?.(null), testIdleTi
910

1011
testIdleTimeoutObject.unref();
1112

13+
process.on('uncaughtException', getGlobalErrorHandler('SubprocessUncaughtException'));
14+
process.on('unhandledRejection', getGlobalErrorHandler('SubprocessUnhandledRejection'));
15+
1216
/**
1317
* Returns exit code `0`, if all tests passed, and `1` otherwise.
1418
*/

src/constants/internal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export {
5050
DOT_ENV_PATH,
5151
EVENTS_DIRECTORY_PATH,
5252
EXPECTED_SCREENSHOTS_DIRECTORY_PATH,
53+
GLOBAL_ERRORS_PATH,
5354
INSTALLED_E2ED_DIRECTORY_PATH,
5455
INTERNAL_DIRECTORY_NAME,
5556
INTERNAL_REPORTS_DIRECTORY_PATH,

0 commit comments

Comments
 (0)