mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: implement Locator.race (#10337)
This commit is contained in:
parent
dde569b97d
commit
9c35e9ab1f
@ -8,7 +8,7 @@ sidebar_label: Locator.click
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
click(
|
abstract click(
|
||||||
clickOptions?: ClickOptions & {
|
clickOptions?: ClickOptions & {
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ Fills out the input identified by the locator using the provided value. The type
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
fill(
|
abstract fill(
|
||||||
value: string,
|
value: string,
|
||||||
fillOptions?: {
|
fillOptions?: {
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.hover
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
hover(hoverOptions?: {signal?: AbortSignal}): Promise<void>;
|
abstract hover(hoverOptions?: {signal?: AbortSignal}): Promise<void>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ Locators describe a strategy of locating elements and performing an action on th
|
|||||||
#### Signature:
|
#### Signature:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export declare class Locator extends EventEmitter
|
export declare abstract class Locator extends EventEmitter
|
||||||
```
|
```
|
||||||
|
|
||||||
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
**Extends:** [EventEmitter](./puppeteer.eventemitter.md)
|
||||||
@ -17,13 +17,14 @@ export declare class Locator extends EventEmitter
|
|||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
| Method | Modifiers | Description |
|
| Method | Modifiers | Description |
|
||||||
| ------------------------------------------------------------------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [click(clickOptions)](./puppeteer.locator.click.md) | | |
|
| [click(clickOptions)](./puppeteer.locator.click.md) | | |
|
||||||
| [fill(value, fillOptions)](./puppeteer.locator.fill.md) | | Fills out the input identified by the locator using the provided value. The type of the input is determined at runtime and the appropriate fill-out method is chosen based on the type. contenteditable, selector, inputs are supported. |
|
| [fill(value, fillOptions)](./puppeteer.locator.fill.md) | | Fills out the input identified by the locator using the provided value. The type of the input is determined at runtime and the appropriate fill-out method is chosen based on the type. contenteditable, selector, inputs are supported. |
|
||||||
| [hover(hoverOptions)](./puppeteer.locator.hover.md) | | |
|
| [hover(hoverOptions)](./puppeteer.locator.hover.md) | | |
|
||||||
| [off(eventName, handler)](./puppeteer.locator.off.md) | | |
|
| [off(eventName, handler)](./puppeteer.locator.off.md) | | |
|
||||||
| [on(eventName, handler)](./puppeteer.locator.on.md) | | |
|
| [on(eventName, handler)](./puppeteer.locator.on.md) | | |
|
||||||
| [once(eventName, handler)](./puppeteer.locator.once.md) | | |
|
| [once(eventName, handler)](./puppeteer.locator.once.md) | | |
|
||||||
|
| [race(locators)](./puppeteer.locator.race.md) | <code>static</code> | Creates a race between multiple locators but ensures that only a single one acts. |
|
||||||
| [scroll(scrollOptions)](./puppeteer.locator.scroll.md) | | |
|
| [scroll(scrollOptions)](./puppeteer.locator.scroll.md) | | |
|
||||||
| [setEnsureElementIsInTheViewport(value)](./puppeteer.locator.setensureelementisintheviewport.md) | | |
|
| [setEnsureElementIsInTheViewport(value)](./puppeteer.locator.setensureelementisintheviewport.md) | | |
|
||||||
| [setTimeout(timeout)](./puppeteer.locator.settimeout.md) | | |
|
| [setTimeout(timeout)](./puppeteer.locator.settimeout.md) | | |
|
||||||
|
25
docs/api/puppeteer.locator.race.md
Normal file
25
docs/api/puppeteer.locator.race.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: Locator.race
|
||||||
|
---
|
||||||
|
|
||||||
|
# Locator.race() method
|
||||||
|
|
||||||
|
Creates a race between multiple locators but ensures that only a single one acts.
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class Locator {
|
||||||
|
static race(locators: Locator[]): Locator;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Description |
|
||||||
|
| --------- | ------------------------------------- | ----------- |
|
||||||
|
| locators | [Locator](./puppeteer.locator.md)\[\] | |
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
|
||||||
|
[Locator](./puppeteer.locator.md)
|
@ -8,7 +8,7 @@ sidebar_label: Locator.scroll
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
scroll(scrollOptions?: {
|
abstract scroll(scrollOptions?: {
|
||||||
scrollTop?: number;
|
scrollTop?: number;
|
||||||
scrollLeft?: number;
|
scrollLeft?: number;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setEnsureElementIsInTheViewport
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
setEnsureElementIsInTheViewport(value: boolean): this;
|
abstract setEnsureElementIsInTheViewport(value: boolean): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setTimeout
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
setTimeout(timeout: number): this;
|
abstract setTimeout(timeout: number): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setVisibility
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
setVisibility(visibility: VisibilityOption): this;
|
abstract setVisibility(visibility: VisibilityOption): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setWaitForEnabled
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
setWaitForEnabled(value: boolean): this;
|
abstract setWaitForEnabled(value: boolean): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setWaitForStableBoundingBox
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
setWaitForStableBoundingBox(value: boolean): this;
|
abstract setWaitForStableBoundingBox(value: boolean): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -116,30 +116,24 @@ export interface LocatorEventObject {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class Locator extends EventEmitter {
|
export abstract class Locator extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
static create(pageOrFrame: Page | Frame, selector: string): Locator {
|
static create(pageOrFrame: Page | Frame, selector: string): Locator {
|
||||||
return new Locator(pageOrFrame, selector).setTimeout(
|
return new LocatorImpl(pageOrFrame, selector).setTimeout(
|
||||||
'getDefaultTimeout' in pageOrFrame
|
'getDefaultTimeout' in pageOrFrame
|
||||||
? pageOrFrame.getDefaultTimeout()
|
? pageOrFrame.getDefaultTimeout()
|
||||||
: pageOrFrame.page().getDefaultTimeout()
|
: pageOrFrame.page().getDefaultTimeout()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pageOrFrame: Page | Frame;
|
/**
|
||||||
#selector: string;
|
* Creates a race between multiple locators but ensures that only a single one
|
||||||
#visibility: VisibilityOption = 'visible';
|
* acts.
|
||||||
#timeout = 30_000;
|
*/
|
||||||
#ensureElementIsInTheViewport = true;
|
static race(locators: Locator[]): Locator {
|
||||||
#waitForEnabled = true;
|
return new RaceLocatorImpl(locators);
|
||||||
#waitForStableBoundingBox = true;
|
|
||||||
|
|
||||||
private constructor(pageOrFrame: Page | Frame, selector: string) {
|
|
||||||
super();
|
|
||||||
this.#pageOrFrame = pageOrFrame;
|
|
||||||
this.#selector = selector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override on<K extends keyof LocatorEventObject>(
|
override on<K extends keyof LocatorEventObject>(
|
||||||
@ -163,6 +157,60 @@ export class Locator extends EventEmitter {
|
|||||||
return super.off(eventName, handler);
|
return super.off(eventName, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract setVisibility(visibility: VisibilityOption): this;
|
||||||
|
|
||||||
|
abstract setTimeout(timeout: number): this;
|
||||||
|
|
||||||
|
abstract setEnsureElementIsInTheViewport(value: boolean): this;
|
||||||
|
|
||||||
|
abstract setWaitForEnabled(value: boolean): this;
|
||||||
|
|
||||||
|
abstract setWaitForStableBoundingBox(value: boolean): this;
|
||||||
|
|
||||||
|
abstract click(
|
||||||
|
clickOptions?: ClickOptions & {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills out the input identified by the locator using the provided value. The
|
||||||
|
* type of the input is determined at runtime and the appropriate fill-out
|
||||||
|
* method is chosen based on the type. contenteditable, selector, inputs are
|
||||||
|
* supported.
|
||||||
|
*/
|
||||||
|
abstract fill(
|
||||||
|
value: string,
|
||||||
|
fillOptions?: {signal?: AbortSignal}
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
|
abstract hover(hoverOptions?: {signal?: AbortSignal}): Promise<void>;
|
||||||
|
|
||||||
|
abstract scroll(scrollOptions?: {
|
||||||
|
scrollTop?: number;
|
||||||
|
scrollLeft?: number;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class LocatorImpl extends Locator {
|
||||||
|
#pageOrFrame: Page | Frame;
|
||||||
|
#selector: string;
|
||||||
|
#visibility: VisibilityOption = 'visible';
|
||||||
|
#timeout = 30_000;
|
||||||
|
#ensureElementIsInTheViewport = true;
|
||||||
|
#waitForEnabled = true;
|
||||||
|
#waitForStableBoundingBox = true;
|
||||||
|
|
||||||
|
constructor(pageOrFrame: Page | Frame, selector: string) {
|
||||||
|
super();
|
||||||
|
this.#pageOrFrame = pageOrFrame;
|
||||||
|
this.#selector = selector;
|
||||||
|
}
|
||||||
|
|
||||||
setVisibility(visibility: VisibilityOption): this {
|
setVisibility(visibility: VisibilityOption): this {
|
||||||
this.#visibility = visibility;
|
this.#visibility = visibility;
|
||||||
return this;
|
return this;
|
||||||
@ -211,6 +259,7 @@ export class Locator extends EventEmitter {
|
|||||||
() => {
|
() => {
|
||||||
controller?.abort();
|
controller?.abort();
|
||||||
isActive = false;
|
isActive = false;
|
||||||
|
clearTimeout(timeoutId);
|
||||||
},
|
},
|
||||||
{once: true}
|
{once: true}
|
||||||
);
|
);
|
||||||
@ -593,3 +642,162 @@ export class Locator extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class RaceLocatorImpl extends Locator {
|
||||||
|
#locators: Locator[];
|
||||||
|
|
||||||
|
constructor(locators: Locator[]) {
|
||||||
|
super();
|
||||||
|
this.#locators = locators;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setVisibility(visibility: VisibilityOption): this {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
locator.setVisibility(visibility);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setTimeout(timeout: number): this {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
locator.setTimeout(timeout);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setEnsureElementIsInTheViewport(value: boolean): this {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
locator.setEnsureElementIsInTheViewport(value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setWaitForEnabled(value: boolean): this {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
locator.setWaitForEnabled(value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setWaitForStableBoundingBox(value: boolean): this {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
locator.setWaitForStableBoundingBox(value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #runRace(
|
||||||
|
action: (el: Locator, abortSignal: AbortSignal) => Promise<void>,
|
||||||
|
options: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const abortControllers = new WeakMap<Locator, AbortController>();
|
||||||
|
|
||||||
|
// Abort all locators if the user-provided signal aborts.
|
||||||
|
options.signal?.addEventListener('abort', () => {
|
||||||
|
for (const locator of this.#locators) {
|
||||||
|
abortControllers.get(locator)?.abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLocatorAction = (locator: Locator): (() => void) => {
|
||||||
|
return () => {
|
||||||
|
// When one locator is ready to act, we will abort other locators.
|
||||||
|
for (const other of this.#locators) {
|
||||||
|
if (other !== locator) {
|
||||||
|
abortControllers.get(other)?.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.emit(LocatorEmittedEvents.Action);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAbortController = (locator: Locator): AbortController => {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
abortControllers.set(locator, abortController);
|
||||||
|
return abortController;
|
||||||
|
};
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
this.#locators.map(locator => {
|
||||||
|
return action(
|
||||||
|
locator.on(LocatorEmittedEvents.Action, handleLocatorAction(locator)),
|
||||||
|
createAbortController(locator).signal
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
options.signal?.throwIfAborted();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async click(
|
||||||
|
clickOptions?: ClickOptions & {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.#runRace(
|
||||||
|
(locator, abortSignal) => {
|
||||||
|
return locator.click({
|
||||||
|
...clickOptions,
|
||||||
|
signal: abortSignal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
signal: clickOptions?.signal,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async fill(
|
||||||
|
value: string,
|
||||||
|
fillOptions?: {signal?: AbortSignal}
|
||||||
|
): Promise<void> {
|
||||||
|
return await this.#runRace(
|
||||||
|
(locator, abortSignal) => {
|
||||||
|
return locator.fill(value, {
|
||||||
|
...fillOptions,
|
||||||
|
signal: abortSignal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
signal: fillOptions?.signal,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async hover(hoverOptions?: {signal?: AbortSignal}): Promise<void> {
|
||||||
|
return await this.#runRace(
|
||||||
|
(locator, abortSignal) => {
|
||||||
|
return locator.hover({
|
||||||
|
...hoverOptions,
|
||||||
|
signal: abortSignal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
signal: hoverOptions?.signal,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async scroll(scrollOptions?: {
|
||||||
|
scrollTop?: number;
|
||||||
|
scrollLeft?: number;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
}): Promise<void> {
|
||||||
|
return await this.#runRace(
|
||||||
|
(locator, abortSignal) => {
|
||||||
|
return locator.hover({
|
||||||
|
...scrollOptions,
|
||||||
|
signal: abortSignal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
signal: scrollOptions?.signal,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -395,6 +395,12 @@
|
|||||||
"parameters": ["chrome"],
|
"parameters": ["chrome"],
|
||||||
"expectations": ["SKIP"]
|
"expectations": ["SKIP"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"testIdPattern": "[navigation.spec] navigation \"after each\" hook for \"should work with both domcontentloaded and load\"",
|
||||||
|
"platforms": ["darwin", "linux", "win32"],
|
||||||
|
"parameters": ["webDriverBiDi"],
|
||||||
|
"expectations": ["FAIL"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL",
|
"testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL",
|
||||||
"platforms": ["darwin", "linux", "win32"],
|
"platforms": ["darwin", "linux", "win32"],
|
||||||
@ -2482,11 +2488,5 @@
|
|||||||
"platforms": ["darwin", "linux", "win32"],
|
"platforms": ["darwin", "linux", "win32"],
|
||||||
"parameters": ["cdp", "chrome", "headless"],
|
"parameters": ["cdp", "chrome", "headless"],
|
||||||
"expectations": ["FAIL", "PASS"]
|
"expectations": ["FAIL", "PASS"]
|
||||||
},
|
|
||||||
{
|
|
||||||
"testIdPattern": "[navigation.spec] navigation \"after each\" hook for \"should work with both domcontentloaded and load\"",
|
|
||||||
"platforms": ["darwin", "linux", "win32"],
|
|
||||||
"parameters": ["webDriverBiDi"],
|
|
||||||
"expectations": ["FAIL"]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import {TimeoutError} from 'puppeteer-core';
|
import {TimeoutError} from 'puppeteer-core';
|
||||||
import {LocatorEmittedEvents} from 'puppeteer-core/internal/api/Locator.js';
|
import {
|
||||||
|
Locator,
|
||||||
|
LocatorEmittedEvents,
|
||||||
|
} from 'puppeteer-core/internal/api/Locator.js';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -444,4 +447,53 @@ describe('Locator', function () {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Locator.race', () => {
|
||||||
|
it('races multiple locators', async () => {
|
||||||
|
const {page} = getTestState();
|
||||||
|
|
||||||
|
await page.setViewport({width: 500, height: 500});
|
||||||
|
await page.setContent(`
|
||||||
|
<button onclick="window.count++;">test</button>
|
||||||
|
`);
|
||||||
|
await page.evaluate(() => {
|
||||||
|
// @ts-expect-error different context.
|
||||||
|
window.count = 0;
|
||||||
|
});
|
||||||
|
await Locator.race([
|
||||||
|
page.locator('button'),
|
||||||
|
page.locator('button'),
|
||||||
|
]).click();
|
||||||
|
const count = await page.evaluate(() => {
|
||||||
|
// @ts-expect-error different context.
|
||||||
|
return globalThis.count;
|
||||||
|
});
|
||||||
|
expect(count).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be aborted', async () => {
|
||||||
|
const {page} = getTestState();
|
||||||
|
const clock = sinon.useFakeTimers();
|
||||||
|
try {
|
||||||
|
await page.setViewport({width: 500, height: 500});
|
||||||
|
await page.setContent(`
|
||||||
|
<button style="display: none;" onclick="this.innerText = 'clicked';">test</button>
|
||||||
|
`);
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const result = Locator.race([
|
||||||
|
page.locator('button'),
|
||||||
|
page.locator('button'),
|
||||||
|
])
|
||||||
|
.setTimeout(5000)
|
||||||
|
.click({
|
||||||
|
signal: abortController.signal,
|
||||||
|
});
|
||||||
|
clock.tick(2000);
|
||||||
|
abortController.abort();
|
||||||
|
await expect(result).rejects.toThrow(/aborted/);
|
||||||
|
} finally {
|
||||||
|
clock.restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user