mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
feat: locator.blur() (#18303)
Note this is only available on Locator. Fixes #10724.
This commit is contained in:
parent
6c3f3068b6
commit
329b3eadb4
@ -18,6 +18,14 @@ Returns an array of `node.innerText` values for all matching nodes.
|
||||
|
||||
Returns an array of `node.textContent` values for all matching nodes.
|
||||
|
||||
## async method: Locator.blur
|
||||
* since: v1.28
|
||||
|
||||
Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) on the element.
|
||||
|
||||
### option: Locator.blur.timeout = %%-input-timeout-%%
|
||||
* since: v1.28
|
||||
|
||||
## async method: Locator.boundingBox
|
||||
* since: v1.14
|
||||
- returns: <[null]|[Object]>
|
||||
|
@ -198,6 +198,10 @@ export class Locator implements api.Locator {
|
||||
return this._frame.focus(this._selector, { strict: true, ...options });
|
||||
}
|
||||
|
||||
async blur(options?: TimeoutOptions): Promise<void> {
|
||||
await this._frame._channel.blur({ selector: this._selector, strict: true, ...options });
|
||||
}
|
||||
|
||||
async count(): Promise<number> {
|
||||
return this._frame._queryCount(this._selector);
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ export const commandsWithTracingSnapshots = new Set([
|
||||
'Frame.evalOnSelectorAll',
|
||||
'Frame.addScriptTag',
|
||||
'Frame.addStyleTag',
|
||||
'Frame.blur',
|
||||
'Frame.check',
|
||||
'Frame.click',
|
||||
'Frame.dragAndDrop',
|
||||
|
@ -1203,6 +1203,12 @@ scheme.FrameAddStyleTagParams = tObject({
|
||||
scheme.FrameAddStyleTagResult = tObject({
|
||||
element: tChannel(['ElementHandle']),
|
||||
});
|
||||
scheme.FrameBlurParams = tObject({
|
||||
selector: tString,
|
||||
strict: tOptional(tBoolean),
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameBlurResult = tOptional(tObject({}));
|
||||
scheme.FrameCheckParams = tObject({
|
||||
selector: tString,
|
||||
strict: tOptional(tBoolean),
|
||||
|
@ -152,6 +152,10 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
|
||||
await this._frame.focus(metadata, params.selector, params);
|
||||
}
|
||||
|
||||
async blur(params: channels.FrameBlurParams, metadata: CallMetadata): Promise<void> {
|
||||
await this._frame.blur(metadata, params.selector, params);
|
||||
}
|
||||
|
||||
async textContent(params: channels.FrameTextContentParams, metadata: CallMetadata): Promise<channels.FrameTextContentResult> {
|
||||
const value = await this._frame.textContent(metadata, params.selector, params);
|
||||
return { value: value === null ? undefined : value };
|
||||
|
@ -668,6 +668,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
return await this.evaluateInUtility(([injected, node, resetSelectionIfNotFocused]) => injected.focusNode(node, resetSelectionIfNotFocused), resetSelectionIfNotFocused);
|
||||
}
|
||||
|
||||
async _blur(progress: Progress): Promise<'error:notconnected' | 'done'> {
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
return await this.evaluateInUtility(([injected, node]) => injected.blurNode(node), {});
|
||||
}
|
||||
|
||||
async type(metadata: CallMetadata, text: string, options: { delay?: number } & types.NavigatingActionWaitOptions): Promise<void> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
|
@ -1191,6 +1191,14 @@ export class Frame extends SdkObject {
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async blur(metadata: CallMetadata, selector: string, options: types.TimeoutOptions & types.StrictOptions = {}) {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
await controller.run(async progress => {
|
||||
dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._blur(progress)));
|
||||
await this._page._doSlowMo();
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async textContent(metadata: CallMetadata, selector: string, options: types.QueryOnSelectorOptions = {}): Promise<string | null> {
|
||||
return this._scheduleRerunnableTask(metadata, selector, (progress, element) => element.textContent, undefined, options);
|
||||
}
|
||||
|
@ -766,6 +766,15 @@ export class InjectedScript {
|
||||
return 'done';
|
||||
}
|
||||
|
||||
blurNode(node: Node): 'error:notconnected' | 'done' {
|
||||
if (!node.isConnected)
|
||||
return 'error:notconnected';
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
throw this.createStacklessError('Node is not an element');
|
||||
(node as HTMLElement | SVGElement).blur();
|
||||
return 'done';
|
||||
}
|
||||
|
||||
setInputFiles(node: Node, payloads: { name: string, mimeType: string, buffer: string }[]) {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
return 'Node is not of type HTMLElement';
|
||||
|
14
packages/playwright-core/types/types.d.ts
vendored
14
packages/playwright-core/types/types.d.ts
vendored
@ -9487,6 +9487,20 @@ export interface Locator {
|
||||
*/
|
||||
allTextContents(): Promise<Array<string>>;
|
||||
|
||||
/**
|
||||
* Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) on the element.
|
||||
* @param options
|
||||
*/
|
||||
blur(options?: {
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
|
||||
* using the
|
||||
* [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout)
|
||||
* or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is
|
||||
* calculated relative to the main frame viewport - which is usually the same as the browser window.
|
||||
|
@ -2131,6 +2131,7 @@ export interface FrameChannel extends FrameEventTarget, Channel {
|
||||
evalOnSelectorAll(params: FrameEvalOnSelectorAllParams, metadata?: Metadata): Promise<FrameEvalOnSelectorAllResult>;
|
||||
addScriptTag(params: FrameAddScriptTagParams, metadata?: Metadata): Promise<FrameAddScriptTagResult>;
|
||||
addStyleTag(params: FrameAddStyleTagParams, metadata?: Metadata): Promise<FrameAddStyleTagResult>;
|
||||
blur(params: FrameBlurParams, metadata?: Metadata): Promise<FrameBlurResult>;
|
||||
check(params: FrameCheckParams, metadata?: Metadata): Promise<FrameCheckResult>;
|
||||
click(params: FrameClickParams, metadata?: Metadata): Promise<FrameClickResult>;
|
||||
content(params?: FrameContentParams, metadata?: Metadata): Promise<FrameContentResult>;
|
||||
@ -2235,6 +2236,16 @@ export type FrameAddStyleTagOptions = {
|
||||
export type FrameAddStyleTagResult = {
|
||||
element: ElementHandleChannel,
|
||||
};
|
||||
export type FrameBlurParams = {
|
||||
selector: string,
|
||||
strict?: boolean,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameBlurOptions = {
|
||||
strict?: boolean,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameBlurResult = void;
|
||||
export type FrameCheckParams = {
|
||||
selector: string,
|
||||
strict?: boolean,
|
||||
|
@ -1586,6 +1586,14 @@ Frame:
|
||||
tracing:
|
||||
snapshot: true
|
||||
|
||||
blur:
|
||||
parameters:
|
||||
selector: string
|
||||
strict: boolean?
|
||||
timeout: number?
|
||||
tracing:
|
||||
snapshot: true
|
||||
|
||||
check:
|
||||
parameters:
|
||||
selector: string
|
||||
|
@ -89,12 +89,29 @@ it('should select single option', async ({ page, server }) => {
|
||||
expect(await page.evaluate(() => window['result'].onChange)).toEqual(['blue']);
|
||||
});
|
||||
|
||||
it('should focus a button', async ({ page, server }) => {
|
||||
it('should focus and blur a button', async ({ page, server }) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = page.locator('button');
|
||||
expect(await button.evaluate(button => document.activeElement === button)).toBe(false);
|
||||
|
||||
let focused = false;
|
||||
let blurred = false;
|
||||
await page.exposeFunction('focusEvent', () => focused = true);
|
||||
await page.exposeFunction('blurEvent', () => blurred = true);
|
||||
await button.evaluate(button => {
|
||||
button.addEventListener('focus', window['focusEvent']);
|
||||
button.addEventListener('blur', window['blurEvent']);
|
||||
});
|
||||
|
||||
await button.focus();
|
||||
expect(focused).toBe(true);
|
||||
expect(blurred).toBe(false);
|
||||
expect(await button.evaluate(button => document.activeElement === button)).toBe(true);
|
||||
|
||||
await button.blur();
|
||||
expect(focused).toBe(true);
|
||||
expect(blurred).toBe(true);
|
||||
expect(await button.evaluate(button => document.activeElement === button)).toBe(false);
|
||||
});
|
||||
|
||||
it('focus should respect strictness', async ({ page, server }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user