diff --git a/README.md b/README.md index 4331dc7..c78df77 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,11 @@ it throws the error.
Returns the BrowserWindow object that corresponds to the given Playwright page (with retries).
This is basically a wrapper around [app.browserWindow(page)](https://playwright.dev/docs/api/class-electronapplication#electron-application-browser-window)
that retries the operation.
Promise.<T>
Retries a given function until it returns a truthy value or the timeout is reached.
+This offers similar functionality to Playwright's page.waitForFunction()
+method – but with more flexibility and control over the retry attempts. It also defaults to ignoring common errors due to
+the way that Playwright handles browser contexts.
Promise.<void>
Stub a single dialog method. This is a convenience function that calls stubMultipleDialogs
for a single method.
Promise.<T>
Add a timeout to any helper function from this library which returns a Promise.
Promise.<T>
Retries a function until it returns without throwing an error containing a specific message.
Retries a function until it returns without throwing an error.
+Starting with Electron 27, Playwright can get very flakey when running code in Electron's main or renderer processes. +It will often throw errors like "context or browser has been closed" or "Promise was collected" for no apparent reason. +This function retries a given function until it returns without throwing one of these errors, or until the timeout is reached.
+You can simply wrap your Playwright calls in this function to make them more reliable:
+test('my test', async () => {
+ // instead of this:
+ const oldWayRenderer = await page.evaluate(() => document.body.classList.contains('active'))
+ const oldWayMain = await electronApp.evaluate(({}) => document.body.classList.contains('active'))
+ // use this:
+ const newWay = await retry(() =>
+ page.evaluate(() => document.body.classList.contains('active'))
+ )
+ // note the `() =>` in front of the original function call
+ // and the `await` keyword in front of `retry`,
+ // but NOT in front of `page.evaluate`
+})
+
Sets the default retry() options. These options will be used for all subsequent calls to retry() unless overridden. You can reset the defaults at any time by calling resetRetryOptions().
The number of retry attempts. Defaults to 5.
| | options.intervalMs |The interval between retries in milliseconds. Defaults to 200.
| + + +## retryUntilTruthy(fn, [timeoutMs], [intervalMs], [options]) ⇒Promise.<T>
+Retries a given function until it returns a truthy value or the timeout is reached.
+This offers similar functionality to Playwright's page.waitForFunction()
+method – but with more flexibility and control over the retry attempts. It also defaults to ignoring common errors due to
+the way that Playwright handles browser contexts.
Promise.<T>
- Error
function
| | The function to retry. It can return a promise or a value.
| +| [timeoutMs] |number
| 5000
| The maximum time in milliseconds to keep retrying the function. Defaults to 5000ms.
| +| [intervalMs] |number
| 100
| The delay between each retry attempt in milliseconds. Defaults to 100ms.
| +| [options] |RetryOptions
| {}
| Optional options for each retry attempt.
| + ## ElectronAppInfo @@ -759,7 +807,24 @@ For example, wait for a MenuItem to be enabled... or be visible.. etc ## retry(fn, [options]) ⇒Promise.<T>
-Retries a function until it returns without throwing an error containing a specific message.
+Retries a function until it returns without throwing an error.
+Starting with Electron 27, Playwright can get very flakey when running code in Electron's main or renderer processes. +It will often throw errors like "context or browser has been closed" or "Promise was collected" for no apparent reason. +This function retries a given function until it returns without throwing one of these errors, or until the timeout is reached.
+You can simply wrap your Playwright calls in this function to make them more reliable:
+test('my test', async () => {
+ // instead of this:
+ const oldWayRenderer = await page.evaluate(() => document.body.classList.contains('active'))
+ const oldWayMain = await electronApp.evaluate(({}) => document.body.classList.contains('active'))
+ // use this:
+ const newWay = await retry(() =>
+ page.evaluate(() => document.body.classList.contains('active'))
+ )
+ // note the `() =>` in front of the original function call
+ // and the `await` keyword in front of `retry`,
+ // but NOT in front of `page.evaluate`
+})
+
**Kind**: global function
**Returns**: Promise.<T>
- A promise that resolves with the result of the function or rejects with an error or timeout message.
@@ -772,7 +837,7 @@ For example, wait for a MenuItem to be enabled... or be visible.. etc | [options.retries] |number
| 5
| The number of retry attempts.
| | [options.intervalMs] |number
| 200
| The delay between each retry attempt in milliseconds.
| | [options.timeoutMs] |number
| 5000
| The maximum time to wait before giving up in milliseconds.
| -| [options.errorMatch] |string
\| RegExp
| "['context or browser has been closed', 'Promise was collected']"
| The error message or pattern to match against.
| +| [options.errorMatch] |string
\| Array.<string>
\| RegExp
| "['context or browser has been closed', 'Promise was collected', 'Execution context was destroyed']"
| String(s) or regex to match against error message. If the error does not match, it will throw immediately. If it does match, it will retry.
| diff --git a/src/utilities.ts b/src/utilities.ts index b414e57..8fe9553 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -93,7 +93,28 @@ export type RetryOptions = { } /** - * Retries a function until it returns without throwing an error containing a specific message. + * Retries a function until it returns without throwing an error. + * + * Starting with Electron 27, Playwright can get very flakey when running code in Electron's main or renderer processes. + * It will often throw errors like "context or browser has been closed" or "Promise was collected" for no apparent reason. + * This function retries a given function until it returns without throwing one of these errors, or until the timeout is reached. + * + * You can simply wrap your Playwright calls in this function to make them more reliable: + * + * ```javascript + * test('my test', async () => { + * // instead of this: + * const oldWayRenderer = await page.evaluate(() => document.body.classList.contains('active')) + * const oldWayMain = await electronApp.evaluate(({}) => document.body.classList.contains('active')) + * // use this: + * const newWay = await retry(() => + * page.evaluate(() => document.body.classList.contains('active')) + * ) + * // note the `() =>` in front of the original function call + * // and the `await` keyword in front of `retry`, + * // but NOT in front of `page.evaluate` + * }) + * ``` * * @category Utilities * @@ -103,7 +124,7 @@ export type RetryOptions = { * @param {number} [options.retries=5] The number of retry attempts. * @param {number} [options.intervalMs=200] The delay between each retry attempt in milliseconds. * @param {number} [options.timeoutMs=5000] The maximum time to wait before giving up in milliseconds. - * @param {string|RegExp} [options.errorMatch=['context or browser has been closed', 'Promise was collected']] The error message or pattern to match against. + * @param {string|string[]|RegExp} [options.errorMatch=['context or browser has been closed', 'Promise was collected', 'Execution context was destroyed']] String(s) or regex to match against error message. If the error does not match, it will throw immediately. If it does match, it will retry. * @returns {Promise