feat(locator handler): address api review feedback (#30554)
- Remove `handler` argument from `removeLocatorHandler`. - Rename `allowStayingVisible` into `noWaitAfter`. - Improve logging related to locator handler. - Remove experimental badges.
This commit is contained in:
parent
dc0665210f
commit
9bd2aea130
|
|
@ -3144,10 +3144,6 @@ return value resolves to `[]`.
|
||||||
## async method: Page.addLocatorHandler
|
## async method: Page.addLocatorHandler
|
||||||
* since: v1.42
|
* since: v1.42
|
||||||
|
|
||||||
:::warning[Experimental]
|
|
||||||
This method is experimental and its behavior may change in the upcoming releases.
|
|
||||||
:::
|
|
||||||
|
|
||||||
When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them tricky to handle in automated tests.
|
When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making them tricky to handle in automated tests.
|
||||||
|
|
||||||
This method lets you set up a special function, called a handler, that activates when it detects that overlay is visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.
|
This method lets you set up a special function, called a handler, that activates when it detects that overlay is visible. The handler's job is to remove the overlay, allowing your test to continue as if the overlay wasn't there.
|
||||||
|
|
@ -3155,7 +3151,7 @@ This method lets you set up a special function, called a handler, that activates
|
||||||
Things to keep in mind:
|
Things to keep in mind:
|
||||||
* When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of your normal test flow, instead of using [`method: Page.addLocatorHandler`].
|
* When an overlay is shown predictably, we recommend explicitly waiting for it in your test and dismissing it as a part of your normal test flow, instead of using [`method: Page.addLocatorHandler`].
|
||||||
* Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered.
|
* Playwright checks for the overlay every time before executing or retrying an action that requires an [actionability check](../actionability.md), or before performing an auto-waiting assertion check. When overlay is visible, Playwright calls the handler first, and then proceeds with the action/assertion. Note that the handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't perform any actions, the handler will not be triggered.
|
||||||
* After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with [`option: allowStayingVisible`].
|
* After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible anymore. You can opt-out of this behavior with [`option: noWaitAfter`].
|
||||||
* The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts.
|
* The execution time of the handler counts towards the timeout of the action/assertion that executed the handler. If your handler takes too long, it might cause timeouts.
|
||||||
* You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions within a handler don't depend on another handler.
|
* You can register multiple handlers. However, only a single handler will be running at a time. Make sure the actions within a handler don't depend on another handler.
|
||||||
|
|
||||||
|
|
@ -3285,13 +3281,13 @@ await page.GotoAsync("https://example.com");
|
||||||
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
|
await page.GetByRole("button", new() { Name = "Start here" }).ClickAsync();
|
||||||
```
|
```
|
||||||
|
|
||||||
An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible, so the handler is called before every actionability check. It is important to specify [`option: allowStayingVisible`], because the handler does not hide the `<body>` element.
|
An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible, so the handler is called before every actionability check. It is important to specify [`option: noWaitAfter`], because the handler does not hide the `<body>` element.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Setup the handler.
|
// Setup the handler.
|
||||||
await page.addLocatorHandler(page.locator('body'), async () => {
|
await page.addLocatorHandler(page.locator('body'), async () => {
|
||||||
await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
|
await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
|
||||||
}, { allowStayingVisible: true });
|
}, { noWaitAfter: true });
|
||||||
|
|
||||||
// Write the test as usual.
|
// Write the test as usual.
|
||||||
await page.goto('https://example.com');
|
await page.goto('https://example.com');
|
||||||
|
|
@ -3302,7 +3298,7 @@ await page.getByRole('button', { name: 'Start here' }).click();
|
||||||
// Setup the handler.
|
// Setup the handler.
|
||||||
page.addLocatorHandler(page.locator("body")), () => {
|
page.addLocatorHandler(page.locator("body")), () => {
|
||||||
page.evaluate("window.removeObstructionsForTestIfNeeded()");
|
page.evaluate("window.removeObstructionsForTestIfNeeded()");
|
||||||
}, new Page.AddLocatorHandlerOptions.setAllowStayingVisible(true));
|
}, new Page.AddLocatorHandlerOptions.setNoWaitAfter(true));
|
||||||
|
|
||||||
// Write the test as usual.
|
// Write the test as usual.
|
||||||
page.goto("https://example.com");
|
page.goto("https://example.com");
|
||||||
|
|
@ -3313,7 +3309,7 @@ page.getByRole("button", Page.GetByRoleOptions().setName("Start here")).click();
|
||||||
# Setup the handler.
|
# Setup the handler.
|
||||||
def handler():
|
def handler():
|
||||||
page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
||||||
page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True)
|
page.add_locator_handler(page.locator("body"), handler, no_wait_after=True)
|
||||||
|
|
||||||
# Write the test as usual.
|
# Write the test as usual.
|
||||||
page.goto("https://example.com")
|
page.goto("https://example.com")
|
||||||
|
|
@ -3324,7 +3320,7 @@ page.get_by_role("button", name="Start here").click()
|
||||||
# Setup the handler.
|
# Setup the handler.
|
||||||
def handler():
|
def handler():
|
||||||
await page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
await page.evaluate("window.removeObstructionsForTestIfNeeded()")
|
||||||
await page.add_locator_handler(page.locator("body"), handler, allow_staying_visible=True)
|
await page.add_locator_handler(page.locator("body"), handler, no_wait_after=True)
|
||||||
|
|
||||||
# Write the test as usual.
|
# Write the test as usual.
|
||||||
await page.goto("https://example.com")
|
await page.goto("https://example.com")
|
||||||
|
|
@ -3335,7 +3331,7 @@ await page.get_by_role("button", name="Start here").click()
|
||||||
// Setup the handler.
|
// Setup the handler.
|
||||||
await page.AddLocatorHandlerAsync(page.Locator("body"), async () => {
|
await page.AddLocatorHandlerAsync(page.Locator("body"), async () => {
|
||||||
await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()");
|
await page.EvaluateAsync("window.removeObstructionsForTestIfNeeded()");
|
||||||
}, new() { AllowStayingVisible = true });
|
}, new() { NoWaitAfter = true });
|
||||||
|
|
||||||
// Write the test as usual.
|
// Write the test as usual.
|
||||||
await page.GotoAsync("https://example.com");
|
await page.GotoAsync("https://example.com");
|
||||||
|
|
@ -3407,9 +3403,9 @@ Function that should be run once [`param: locator`] appears. This function shoul
|
||||||
|
|
||||||
Specifies the maximum number of times this handler should be called. Unlimited by default.
|
Specifies the maximum number of times this handler should be called. Unlimited by default.
|
||||||
|
|
||||||
### option: Page.addLocatorHandler.allowStayingVisible
|
### option: Page.addLocatorHandler.noWaitAfter
|
||||||
* since: v1.44
|
* since: v1.44
|
||||||
- `allowStayingVisible` <[boolean]>
|
- `noWaitAfter` <[boolean]>
|
||||||
|
|
||||||
By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run.
|
By default, after calling the handler Playwright will wait until the overlay becomes hidden, and only then Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of this behavior, so that overlay can stay visible after the handler has run.
|
||||||
|
|
||||||
|
|
@ -3417,11 +3413,7 @@ By default, after calling the handler Playwright will wait until the overlay bec
|
||||||
## async method: Page.removeLocatorHandler
|
## async method: Page.removeLocatorHandler
|
||||||
* since: v1.44
|
* since: v1.44
|
||||||
|
|
||||||
:::warning[Experimental]
|
Removes all locator handlers added by [`method: Page.addLocatorHandler`] for a specific locator.
|
||||||
This method is experimental and its behavior may change in the upcoming releases.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Removes locator handler added by [`method: Page.addLocatorHandler`].
|
|
||||||
|
|
||||||
### param: Page.removeLocatorHandler.locator
|
### param: Page.removeLocatorHandler.locator
|
||||||
* since: v1.44
|
* since: v1.44
|
||||||
|
|
@ -3429,20 +3421,6 @@ Removes locator handler added by [`method: Page.addLocatorHandler`].
|
||||||
|
|
||||||
Locator passed to [`method: Page.addLocatorHandler`].
|
Locator passed to [`method: Page.addLocatorHandler`].
|
||||||
|
|
||||||
### param: Page.removeLocatorHandler.handler
|
|
||||||
* langs: js, python
|
|
||||||
* since: v1.44
|
|
||||||
- `handler` <[function]\([Locator]\): [Promise<any>]>
|
|
||||||
|
|
||||||
Handler passed to [`method: Page.addLocatorHandler`].
|
|
||||||
|
|
||||||
### param: Page.addLocatorHandler.handler
|
|
||||||
* langs: csharp, java
|
|
||||||
* since: v1.44
|
|
||||||
- `handler` <[function]\([Locator]\)>
|
|
||||||
|
|
||||||
Handler passed to [`method: Page.addLocatorHandler`].
|
|
||||||
|
|
||||||
|
|
||||||
## async method: Page.reload
|
## async method: Page.reload
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
|
|
|
||||||
|
|
@ -362,12 +362,12 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response);
|
return Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addLocatorHandler(locator: Locator, handler: (locator: Locator) => any, options: { times?: number, allowStayingVisible?: boolean } = {}): Promise<void> {
|
async addLocatorHandler(locator: Locator, handler: (locator: Locator) => any, options: { times?: number, noWaitAfter?: boolean } = {}): Promise<void> {
|
||||||
if (locator._frame !== this._mainFrame)
|
if (locator._frame !== this._mainFrame)
|
||||||
throw new Error(`Locator must belong to the main frame of this page`);
|
throw new Error(`Locator must belong to the main frame of this page`);
|
||||||
if (options.times === 0)
|
if (options.times === 0)
|
||||||
return;
|
return;
|
||||||
const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, allowStayingVisible: options.allowStayingVisible });
|
const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, noWaitAfter: options.noWaitAfter });
|
||||||
this._locatorHandlers.set(uid, { locator, handler, times: options.times });
|
this._locatorHandlers.set(uid, { locator, handler, times: options.times });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,11 +386,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeLocatorHandler(locator: Locator, handler: (locator: Locator) => any): Promise<void> {
|
async removeLocatorHandler(locator: Locator): Promise<void> {
|
||||||
for (const [uid, data] of this._locatorHandlers) {
|
for (const [uid, data] of this._locatorHandlers) {
|
||||||
if (data.locator._equals(locator) && data.handler === handler) {
|
if (data.locator._equals(locator)) {
|
||||||
this._locatorHandlers.delete(uid);
|
this._locatorHandlers.delete(uid);
|
||||||
await this._channel.unregisterLocatorHandlerNoReply({ uid }).catch(() => {});
|
await this._channel.unregisterLocatorHandler({ uid }).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1046,7 +1046,7 @@ scheme.PageGoForwardResult = tObject({
|
||||||
});
|
});
|
||||||
scheme.PageRegisterLocatorHandlerParams = tObject({
|
scheme.PageRegisterLocatorHandlerParams = tObject({
|
||||||
selector: tString,
|
selector: tString,
|
||||||
allowStayingVisible: tOptional(tBoolean),
|
noWaitAfter: tOptional(tBoolean),
|
||||||
});
|
});
|
||||||
scheme.PageRegisterLocatorHandlerResult = tObject({
|
scheme.PageRegisterLocatorHandlerResult = tObject({
|
||||||
uid: tNumber,
|
uid: tNumber,
|
||||||
|
|
@ -1056,10 +1056,10 @@ scheme.PageResolveLocatorHandlerNoReplyParams = tObject({
|
||||||
remove: tOptional(tBoolean),
|
remove: tOptional(tBoolean),
|
||||||
});
|
});
|
||||||
scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({}));
|
scheme.PageResolveLocatorHandlerNoReplyResult = tOptional(tObject({}));
|
||||||
scheme.PageUnregisterLocatorHandlerNoReplyParams = tObject({
|
scheme.PageUnregisterLocatorHandlerParams = tObject({
|
||||||
uid: tNumber,
|
uid: tNumber,
|
||||||
});
|
});
|
||||||
scheme.PageUnregisterLocatorHandlerNoReplyResult = tOptional(tObject({}));
|
scheme.PageUnregisterLocatorHandlerResult = tOptional(tObject({}));
|
||||||
scheme.PageReloadParams = tObject({
|
scheme.PageReloadParams = tObject({
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
waitUntil: tOptional(tType('LifecycleEvent')),
|
waitUntil: tOptional(tType('LifecycleEvent')),
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> {
|
async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> {
|
||||||
const uid = this._page.registerLocatorHandler(params.selector, params.allowStayingVisible);
|
const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter);
|
||||||
return { uid };
|
return { uid };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
|
||||||
this._page.resolveLocatorHandler(params.uid, params.remove);
|
this._page.resolveLocatorHandler(params.uid, params.remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
async unregisterLocatorHandlerNoReply(params: channels.PageUnregisterLocatorHandlerNoReplyParams, metadata: CallMetadata): Promise<void> {
|
async unregisterLocatorHandler(params: channels.PageUnregisterLocatorHandlerParams, metadata: CallMetadata): Promise<void> {
|
||||||
this._page.unregisterLocatorHandler(params.uid);
|
this._page.unregisterLocatorHandler(params.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ export class Page extends SdkObject {
|
||||||
_video: Artifact | null = null;
|
_video: Artifact | null = null;
|
||||||
_opener: Page | undefined;
|
_opener: Page | undefined;
|
||||||
private _isServerSideOnly = false;
|
private _isServerSideOnly = false;
|
||||||
private _locatorHandlers = new Map<number, { selector: string, allowStayingVisible?: boolean, resolved?: ManualPromise<void> }>();
|
private _locatorHandlers = new Map<number, { selector: string, noWaitAfter?: boolean, resolved?: ManualPromise<void> }>();
|
||||||
private _lastLocatorHandlerUid = 0;
|
private _lastLocatorHandlerUid = 0;
|
||||||
private _locatorHandlerRunningCounter = 0;
|
private _locatorHandlerRunningCounter = 0;
|
||||||
|
|
||||||
|
|
@ -432,9 +432,9 @@ export class Page extends SdkObject {
|
||||||
}), this._timeoutSettings.navigationTimeout(options));
|
}), this._timeoutSettings.navigationTimeout(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
registerLocatorHandler(selector: string, allowStayingVisible: boolean | undefined) {
|
registerLocatorHandler(selector: string, noWaitAfter: boolean | undefined) {
|
||||||
const uid = ++this._lastLocatorHandlerUid;
|
const uid = ++this._lastLocatorHandlerUid;
|
||||||
this._locatorHandlers.set(uid, { selector, allowStayingVisible });
|
this._locatorHandlers.set(uid, { selector, noWaitAfter });
|
||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,8 +468,12 @@ export class Page extends SdkObject {
|
||||||
progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`);
|
progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`);
|
||||||
const promise = handler.resolved.then(async () => {
|
const promise = handler.resolved.then(async () => {
|
||||||
progress.throwIfAborted();
|
progress.throwIfAborted();
|
||||||
if (!handler.allowStayingVisible)
|
if (!handler.noWaitAfter) {
|
||||||
|
progress.log(` locator handler has finished, waiting for ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)} to be hidden`);
|
||||||
await this.mainFrame().waitForSelectorInternal(progress, handler.selector, { state: 'hidden' });
|
await this.mainFrame().waitForSelectorInternal(progress, handler.selector, { state: 'hidden' });
|
||||||
|
} else {
|
||||||
|
progress.log(` locator handler has finished`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
await this.openScope.race(promise).finally(() => --this._locatorHandlerRunningCounter);
|
await this.openScope.race(promise).finally(() => --this._locatorHandlerRunningCounter);
|
||||||
// Avoid side-effects after long-running operation.
|
// Avoid side-effects after long-running operation.
|
||||||
|
|
|
||||||
23
packages/playwright-core/types/types.d.ts
vendored
23
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -1790,8 +1790,6 @@ export interface Page {
|
||||||
prependListener(event: 'worker', listener: (worker: Worker) => any): this;
|
prependListener(event: 'worker', listener: (worker: Worker) => any): this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **NOTE** This method is experimental and its behavior may change in the upcoming releases.
|
|
||||||
*
|
|
||||||
* When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to
|
* When testing a web page, sometimes unexpected overlays like a "Sign up" dialog appear and block actions you want to
|
||||||
* automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making
|
* automate, e.g. clicking a button. These overlays don't always show up in the same way or at the same time, making
|
||||||
* them tricky to handle in automated tests.
|
* them tricky to handle in automated tests.
|
||||||
|
|
@ -1809,7 +1807,7 @@ export interface Page {
|
||||||
* handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't
|
* handler is only called when you perform an action/assertion - if the overlay becomes visible but you don't
|
||||||
* perform any actions, the handler will not be triggered.
|
* perform any actions, the handler will not be triggered.
|
||||||
* - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible
|
* - After executing the handler, Playwright will ensure that overlay that triggered the handler is not visible
|
||||||
* anymore. You can opt-out of this behavior with `allowStayingVisible`.
|
* anymore. You can opt-out of this behavior with `noWaitAfter`.
|
||||||
* - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler.
|
* - The execution time of the handler counts towards the timeout of the action/assertion that executed the handler.
|
||||||
* If your handler takes too long, it might cause timeouts.
|
* If your handler takes too long, it might cause timeouts.
|
||||||
* - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the
|
* - You can register multiple handlers. However, only a single handler will be running at a time. Make sure the
|
||||||
|
|
@ -1859,14 +1857,14 @@ export interface Page {
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible,
|
* An example with a custom callback on every actionability check. It uses a `<body>` locator that is always visible,
|
||||||
* so the handler is called before every actionability check. It is important to specify `allowStayingVisible`,
|
* so the handler is called before every actionability check. It is important to specify `noWaitAfter`, because the
|
||||||
* because the handler does not hide the `<body>` element.
|
* handler does not hide the `<body>` element.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* // Setup the handler.
|
* // Setup the handler.
|
||||||
* await page.addLocatorHandler(page.locator('body'), async () => {
|
* await page.addLocatorHandler(page.locator('body'), async () => {
|
||||||
* await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
|
* await page.evaluate(() => window.removeObstructionsForTestIfNeeded());
|
||||||
* }, { allowStayingVisible: true });
|
* }, { noWaitAfter: true });
|
||||||
*
|
*
|
||||||
* // Write the test as usual.
|
* // Write the test as usual.
|
||||||
* await page.goto('https://example.com');
|
* await page.goto('https://example.com');
|
||||||
|
|
@ -1893,7 +1891,7 @@ export interface Page {
|
||||||
* Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of
|
* Playwright will continue with the action/assertion that triggered the handler. This option allows to opt-out of
|
||||||
* this behavior, so that overlay can stay visible after the handler has run.
|
* this behavior, so that overlay can stay visible after the handler has run.
|
||||||
*/
|
*/
|
||||||
allowStayingVisible?: boolean;
|
noWaitAfter?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the maximum number of times this handler should be called. Unlimited by default.
|
* Specifies the maximum number of times this handler should be called. Unlimited by default.
|
||||||
|
|
@ -3680,16 +3678,13 @@ export interface Page {
|
||||||
}): Promise<null|Response>;
|
}): Promise<null|Response>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **NOTE** This method is experimental and its behavior may change in the upcoming releases.
|
* Removes all locator handlers added by
|
||||||
*
|
* [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler)
|
||||||
* Removes locator handler added by
|
* for a specific locator.
|
||||||
* [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler).
|
|
||||||
* @param locator Locator passed to
|
* @param locator Locator passed to
|
||||||
* [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler).
|
* [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler).
|
||||||
* @param handler Handler passed to
|
|
||||||
* [page.addLocatorHandler(locator, handler[, options])](https://playwright.dev/docs/api/class-page#page-add-locator-handler).
|
|
||||||
*/
|
*/
|
||||||
removeLocatorHandler(locator: Locator, handler: ((locator: Locator) => Promise<any>)): Promise<void>;
|
removeLocatorHandler(locator: Locator): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Routing provides the capability to modify network requests that are made by a page.
|
* Routing provides the capability to modify network requests that are made by a page.
|
||||||
|
|
|
||||||
|
|
@ -1790,7 +1790,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel {
|
||||||
goForward(params: PageGoForwardParams, metadata?: CallMetadata): Promise<PageGoForwardResult>;
|
goForward(params: PageGoForwardParams, metadata?: CallMetadata): Promise<PageGoForwardResult>;
|
||||||
registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>;
|
registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>;
|
||||||
resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>;
|
resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>;
|
||||||
unregisterLocatorHandlerNoReply(params: PageUnregisterLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageUnregisterLocatorHandlerNoReplyResult>;
|
unregisterLocatorHandler(params: PageUnregisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageUnregisterLocatorHandlerResult>;
|
||||||
reload(params: PageReloadParams, metadata?: CallMetadata): Promise<PageReloadResult>;
|
reload(params: PageReloadParams, metadata?: CallMetadata): Promise<PageReloadResult>;
|
||||||
expectScreenshot(params: PageExpectScreenshotParams, metadata?: CallMetadata): Promise<PageExpectScreenshotResult>;
|
expectScreenshot(params: PageExpectScreenshotParams, metadata?: CallMetadata): Promise<PageExpectScreenshotResult>;
|
||||||
screenshot(params: PageScreenshotParams, metadata?: CallMetadata): Promise<PageScreenshotResult>;
|
screenshot(params: PageScreenshotParams, metadata?: CallMetadata): Promise<PageScreenshotResult>;
|
||||||
|
|
@ -1927,10 +1927,10 @@ export type PageGoForwardResult = {
|
||||||
};
|
};
|
||||||
export type PageRegisterLocatorHandlerParams = {
|
export type PageRegisterLocatorHandlerParams = {
|
||||||
selector: string,
|
selector: string,
|
||||||
allowStayingVisible?: boolean,
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type PageRegisterLocatorHandlerOptions = {
|
export type PageRegisterLocatorHandlerOptions = {
|
||||||
allowStayingVisible?: boolean,
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type PageRegisterLocatorHandlerResult = {
|
export type PageRegisterLocatorHandlerResult = {
|
||||||
uid: number,
|
uid: number,
|
||||||
|
|
@ -1943,13 +1943,13 @@ export type PageResolveLocatorHandlerNoReplyOptions = {
|
||||||
remove?: boolean,
|
remove?: boolean,
|
||||||
};
|
};
|
||||||
export type PageResolveLocatorHandlerNoReplyResult = void;
|
export type PageResolveLocatorHandlerNoReplyResult = void;
|
||||||
export type PageUnregisterLocatorHandlerNoReplyParams = {
|
export type PageUnregisterLocatorHandlerParams = {
|
||||||
uid: number,
|
uid: number,
|
||||||
};
|
};
|
||||||
export type PageUnregisterLocatorHandlerNoReplyOptions = {
|
export type PageUnregisterLocatorHandlerOptions = {
|
||||||
|
|
||||||
};
|
};
|
||||||
export type PageUnregisterLocatorHandlerNoReplyResult = void;
|
export type PageUnregisterLocatorHandlerResult = void;
|
||||||
export type PageReloadParams = {
|
export type PageReloadParams = {
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
waitUntil?: LifecycleEvent,
|
waitUntil?: LifecycleEvent,
|
||||||
|
|
|
||||||
|
|
@ -1350,7 +1350,7 @@ Page:
|
||||||
registerLocatorHandler:
|
registerLocatorHandler:
|
||||||
parameters:
|
parameters:
|
||||||
selector: string
|
selector: string
|
||||||
allowStayingVisible: boolean?
|
noWaitAfter: boolean?
|
||||||
returns:
|
returns:
|
||||||
uid: number
|
uid: number
|
||||||
|
|
||||||
|
|
@ -1359,7 +1359,7 @@ Page:
|
||||||
uid: number
|
uid: number
|
||||||
remove: boolean?
|
remove: boolean?
|
||||||
|
|
||||||
unregisterLocatorHandlerNoReply:
|
unregisterLocatorHandler:
|
||||||
parameters:
|
parameters:
|
||||||
uid: number
|
uid: number
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ test('should work with a custom check', async ({ page, server }) => {
|
||||||
await page.addLocatorHandler(page.locator('body'), async () => {
|
await page.addLocatorHandler(page.locator('body'), async () => {
|
||||||
if (await page.getByText('This interstitial covers the button').isVisible())
|
if (await page.getByText('This interstitial covers the button').isVisible())
|
||||||
await page.locator('#close').click();
|
await page.locator('#close').click();
|
||||||
}, { allowStayingVisible: true });
|
}, { noWaitAfter: true });
|
||||||
|
|
||||||
for (const args of [
|
for (const args of [
|
||||||
['mouseover', 2],
|
['mouseover', 2],
|
||||||
|
|
@ -243,7 +243,7 @@ test('should work with times: option', async ({ page, server }) => {
|
||||||
let called = 0;
|
let called = 0;
|
||||||
await page.addLocatorHandler(page.locator('body'), async () => {
|
await page.addLocatorHandler(page.locator('body'), async () => {
|
||||||
++called;
|
++called;
|
||||||
}, { allowStayingVisible: true, times: 2 });
|
}, { noWaitAfter: true, times: 2 });
|
||||||
|
|
||||||
await page.locator('#aside').hover();
|
await page.locator('#aside').hover();
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
|
|
@ -278,7 +278,27 @@ test('should wait for hidden by default', async ({ page, server }) => {
|
||||||
expect(called).toBe(1);
|
expect(called).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should work with allowStayingVisible', async ({ page, server }) => {
|
test('should wait for hidden by default 2', async ({ page, server }) => {
|
||||||
|
await page.goto(server.PREFIX + '/input/handle-locator.html');
|
||||||
|
|
||||||
|
let called = 0;
|
||||||
|
await page.addLocatorHandler(page.getByRole('button', { name: 'close' }), async button => {
|
||||||
|
called++;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.locator('#aside').hover();
|
||||||
|
await page.evaluate(() => {
|
||||||
|
(window as any).clicked = 0;
|
||||||
|
(window as any).setupAnnoyingInterstitial('hide', 1);
|
||||||
|
});
|
||||||
|
const error = await page.locator('#target').click({ timeout: 3000 }).catch(e => e);
|
||||||
|
expect(await page.evaluate('window.clicked')).toBe(0);
|
||||||
|
await expect(page.locator('#interstitial')).toBeVisible();
|
||||||
|
expect(called).toBe(1);
|
||||||
|
expect(error.message).toContain(`locator handler has finished, waiting for getByRole('button', { name: 'close' }) to be hidden`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with noWaitAfter', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/handle-locator.html');
|
await page.goto(server.PREFIX + '/input/handle-locator.html');
|
||||||
|
|
||||||
let called = 0;
|
let called = 0;
|
||||||
|
|
@ -288,7 +308,7 @@ test('should work with allowStayingVisible', async ({ page, server }) => {
|
||||||
await button.click();
|
await button.click();
|
||||||
else
|
else
|
||||||
await page.locator('#interstitial').waitFor({ state: 'hidden' });
|
await page.locator('#interstitial').waitFor({ state: 'hidden' });
|
||||||
}, { allowStayingVisible: true });
|
}, { noWaitAfter: true });
|
||||||
|
|
||||||
await page.locator('#aside').hover();
|
await page.locator('#aside').hover();
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
|
|
@ -305,11 +325,10 @@ test('should removeLocatorHandler', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/handle-locator.html');
|
await page.goto(server.PREFIX + '/input/handle-locator.html');
|
||||||
|
|
||||||
let called = 0;
|
let called = 0;
|
||||||
const handler = async locator => {
|
await page.addLocatorHandler(page.getByRole('button', { name: 'close' }), async locator => {
|
||||||
++called;
|
++called;
|
||||||
await locator.click();
|
await locator.click();
|
||||||
};
|
});
|
||||||
await page.addLocatorHandler(page.getByRole('button', { name: 'close' }), handler);
|
|
||||||
|
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
(window as any).clicked = 0;
|
(window as any).clicked = 0;
|
||||||
|
|
@ -324,7 +343,7 @@ test('should removeLocatorHandler', async ({ page, server }) => {
|
||||||
(window as any).clicked = 0;
|
(window as any).clicked = 0;
|
||||||
(window as any).setupAnnoyingInterstitial('hide', 1);
|
(window as any).setupAnnoyingInterstitial('hide', 1);
|
||||||
});
|
});
|
||||||
await page.removeLocatorHandler(page.getByRole('button', { name: 'close' }), handler);
|
await page.removeLocatorHandler(page.getByRole('button', { name: 'close' }));
|
||||||
|
|
||||||
const error = await page.locator('#target').click({ timeout: 3000 }).catch(e => e);
|
const error = await page.locator('#target').click({ timeout: 3000 }).catch(e => e);
|
||||||
expect(called).toBe(1);
|
expect(called).toBe(1);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue