feat(force): add fill, selectOption, selectText ({force}) (#7286)

This commit is contained in:
Pavel Feldman 2021-06-24 08:18:09 -07:00 committed by GitHub
parent ba3f0ff6f5
commit e6bf0a07fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 41 deletions

View File

@ -448,8 +448,8 @@ To send fine-grained keyboard events, use [`method: ElementHandle.type`].
Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element. Value to set for the `<input>`, `<textarea>` or `[contenteditable]` element.
### option: ElementHandle.fill.force = %%-input-force-%%
### option: ElementHandle.fill.noWaitAfter = %%-input-no-wait-after-%% ### option: ElementHandle.fill.noWaitAfter = %%-input-no-wait-after-%%
### option: ElementHandle.fill.timeout = %%-input-timeout-%% ### option: ElementHandle.fill.timeout = %%-input-timeout-%%
## async method: ElementHandle.focus ## async method: ElementHandle.focus
@ -717,9 +717,8 @@ await handle.SelectOptionAsync(new[] {
``` ```
### param: ElementHandle.selectOption.values = %%-select-options-values-%% ### param: ElementHandle.selectOption.values = %%-select-options-values-%%
### option: ElementHandle.selectOption.force = %%-input-force-%%
### option: ElementHandle.selectOption.noWaitAfter = %%-input-no-wait-after-%% ### option: ElementHandle.selectOption.noWaitAfter = %%-input-no-wait-after-%%
### option: ElementHandle.selectOption.timeout = %%-input-timeout-%% ### option: ElementHandle.selectOption.timeout = %%-input-timeout-%%
## async method: ElementHandle.selectText ## async method: ElementHandle.selectText
@ -727,6 +726,7 @@ await handle.SelectOptionAsync(new[] {
This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text This method waits for [actionability](./actionability.md) checks, then focuses the element and selects all its text
content. content.
### option: ElementHandle.selectText.force = %%-input-force-%%
### option: ElementHandle.selectText.timeout = %%-input-timeout-%% ### option: ElementHandle.selectText.timeout = %%-input-timeout-%%
## async method: ElementHandle.setInputFiles ## async method: ElementHandle.setInputFiles

View File

@ -699,8 +699,8 @@ To send fine-grained keyboard events, use [`method: Frame.type`].
Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element. Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.
### option: Frame.fill.force = %%-input-force-%%
### option: Frame.fill.noWaitAfter = %%-input-no-wait-after-%% ### option: Frame.fill.noWaitAfter = %%-input-no-wait-after-%%
### option: Frame.fill.timeout = %%-input-timeout-%% ### option: Frame.fill.timeout = %%-input-timeout-%%
## async method: Frame.focus ## async method: Frame.focus
@ -1065,11 +1065,9 @@ await frame.SelectOptionAsync("select#colors", new[] { "red", "green", "blue" })
``` ```
### param: Frame.selectOption.selector = %%-query-selector-%% ### param: Frame.selectOption.selector = %%-query-selector-%%
### param: Frame.selectOption.values = %%-select-options-values-%% ### param: Frame.selectOption.values = %%-select-options-values-%%
### option: Frame.selectOption.force = %%-input-force-%%
### option: Frame.selectOption.noWaitAfter = %%-input-no-wait-after-%% ### option: Frame.selectOption.noWaitAfter = %%-input-no-wait-after-%%
### option: Frame.selectOption.timeout = %%-input-timeout-%% ### option: Frame.selectOption.timeout = %%-input-timeout-%%
## async method: Frame.setContent ## async method: Frame.setContent

View File

@ -1746,8 +1746,8 @@ Shortcut for main frame's [`method: Frame.fill`].
Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element. Value to fill for the `<input>`, `<textarea>` or `[contenteditable]` element.
### option: Page.fill.force = %%-input-force-%%
### option: Page.fill.noWaitAfter = %%-input-no-wait-after-%% ### option: Page.fill.noWaitAfter = %%-input-no-wait-after-%%
### option: Page.fill.timeout = %%-input-timeout-%% ### option: Page.fill.timeout = %%-input-timeout-%%
## async method: Page.focus ## async method: Page.focus
@ -2631,11 +2631,9 @@ await page.SelectOptionAsync("select#colors", new[] { "red", "green", "blue" });
Shortcut for main frame's [`method: Frame.selectOption`]. Shortcut for main frame's [`method: Frame.selectOption`].
### param: Page.selectOption.selector = %%-input-selector-%% ### param: Page.selectOption.selector = %%-input-selector-%%
### param: Page.selectOption.values = %%-select-options-values-%% ### param: Page.selectOption.values = %%-select-options-values-%%
### option: Page.selectOption.force = %%-input-force-%%
### option: Page.selectOption.noWaitAfter = %%-input-no-wait-after-%% ### option: Page.selectOption.noWaitAfter = %%-input-no-wait-after-%%
### option: Page.selectOption.timeout = %%-input-timeout-%% ### option: Page.selectOption.timeout = %%-input-timeout-%%
## async method: Page.setContent ## async method: Page.setContent

View File

@ -33,7 +33,7 @@ export type WaitForEventOptions = Function | { predicate?: Function, timeout?: n
export type WaitForFunctionOptions = { timeout?: number, polling?: 'raf' | number }; export type WaitForFunctionOptions = { timeout?: number, polling?: 'raf' | number };
export type SelectOption = { value?: string, label?: string, index?: number }; export type SelectOption = { value?: string, label?: string, index?: number };
export type SelectOptionOptions = { timeout?: number, noWaitAfter?: boolean }; export type SelectOptionOptions = { force?: boolean, timeout?: number, noWaitAfter?: boolean };
export type FilePayload = { name: string, mimeType: string, buffer: Buffer }; export type FilePayload = { name: string, mimeType: string, buffer: Buffer };
export type StorageState = { export type StorageState = {
cookies: channels.NetworkCookie[], cookies: channels.NetworkCookie[],

View File

@ -1490,10 +1490,12 @@ export type FrameEvaluateExpressionHandleResult = {
export type FrameFillParams = { export type FrameFillParams = {
selector: string, selector: string,
value: string, value: string,
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
export type FrameFillOptions = { export type FrameFillOptions = {
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -1681,6 +1683,7 @@ export type FrameSelectOptionParams = {
label?: string, label?: string,
index?: number, index?: number,
}[], }[],
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -1691,6 +1694,7 @@ export type FrameSelectOptionOptions = {
label?: string, label?: string,
index?: number, index?: number,
}[], }[],
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -2052,10 +2056,12 @@ export type ElementHandleDispatchEventOptions = {
export type ElementHandleDispatchEventResult = void; export type ElementHandleDispatchEventResult = void;
export type ElementHandleFillParams = { export type ElementHandleFillParams = {
value: string, value: string,
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
export type ElementHandleFillOptions = { export type ElementHandleFillOptions = {
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -2196,6 +2202,7 @@ export type ElementHandleSelectOptionParams = {
label?: string, label?: string,
index?: number, index?: number,
}[], }[],
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -2206,6 +2213,7 @@ export type ElementHandleSelectOptionOptions = {
label?: string, label?: string,
index?: number, index?: number,
}[], }[],
force?: boolean,
timeout?: number, timeout?: number,
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
@ -2213,9 +2221,11 @@ export type ElementHandleSelectOptionResult = {
values: string[], values: string[],
}; };
export type ElementHandleSelectTextParams = { export type ElementHandleSelectTextParams = {
force?: boolean,
timeout?: number, timeout?: number,
}; };
export type ElementHandleSelectTextOptions = { export type ElementHandleSelectTextOptions = {
force?: boolean,
timeout?: number, timeout?: number,
}; };
export type ElementHandleSelectTextResult = void; export type ElementHandleSelectTextResult = void;

View File

@ -1178,6 +1178,7 @@ Frame:
parameters: parameters:
selector: string selector: string
value: string value: string
force: boolean?
timeout: number? timeout: number?
noWaitAfter: boolean? noWaitAfter: boolean?
@ -1328,6 +1329,7 @@ Frame:
value: string? value: string?
label: string? label: string?
index: number? index: number?
force: boolean?
timeout: number? timeout: number?
noWaitAfter: boolean? noWaitAfter: boolean?
returns: returns:
@ -1641,6 +1643,7 @@ ElementHandle:
fill: fill:
parameters: parameters:
value: string value: string
force: boolean?
timeout: number? timeout: number?
noWaitAfter: boolean? noWaitAfter: boolean?
@ -1759,6 +1762,7 @@ ElementHandle:
value: string? value: string?
label: string? label: string?
index: number? index: number?
force: boolean?
timeout: number? timeout: number?
noWaitAfter: boolean? noWaitAfter: boolean?
returns: returns:
@ -1768,6 +1772,7 @@ ElementHandle:
selectText: selectText:
parameters: parameters:
force: boolean?
timeout: number? timeout: number?
setInputFiles: setInputFiles:

View File

@ -608,6 +608,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
scheme.FrameFillParams = tObject({ scheme.FrameFillParams = tObject({
selector: tString, selector: tString,
value: tString, value: tString,
force: tOptional(tBoolean),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean),
}); });
@ -692,6 +693,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
label: tOptional(tString), label: tOptional(tString),
index: tOptional(tNumber), index: tOptional(tNumber),
}))), }))),
force: tOptional(tBoolean),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean),
}); });
@ -831,6 +833,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
}); });
scheme.ElementHandleFillParams = tObject({ scheme.ElementHandleFillParams = tObject({
value: tString, value: tString,
force: tOptional(tBoolean),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean),
}); });
@ -883,10 +886,12 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
label: tOptional(tString), label: tOptional(tString),
index: tOptional(tNumber), index: tOptional(tNumber),
}))), }))),
force: tOptional(tBoolean),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
noWaitAfter: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean),
}); });
scheme.ElementHandleSelectTextParams = tObject({ scheme.ElementHandleSelectTextParams = tObject({
force: tOptional(tBoolean),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
}); });
scheme.ElementHandleSetInputFilesParams = tObject({ scheme.ElementHandleSetInputFilesParams = tObject({

View File

@ -225,7 +225,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async _waitAndScrollIntoViewIfNeeded(progress: Progress): Promise<void> { async _waitAndScrollIntoViewIfNeeded(progress: Progress): Promise<void> {
while (progress.isRunning()) { while (progress.isRunning()) {
assertDone(throwRetargetableDOMError(await this._waitForDisplayedAtStablePosition(progress, false /* waitForEnabled */))); assertDone(throwRetargetableDOMError(await this._waitForDisplayedAtStablePosition(progress, false /* force */, false /* waitForEnabled */)));
progress.throwIfAborted(); // Avoid action that has side-effects. progress.throwIfAborted(); // Avoid action that has side-effects.
const result = throwRetargetableDOMError(await this._scrollRectIntoViewIfNeeded()); const result = throwRetargetableDOMError(await this._scrollRectIntoViewIfNeeded());
@ -356,11 +356,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
const { force = false, position } = options; const { force = false, position } = options;
if ((options as any).__testHookBeforeStable) if ((options as any).__testHookBeforeStable)
await (options as any).__testHookBeforeStable(); await (options as any).__testHookBeforeStable();
if (!force) { const result = await this._waitForDisplayedAtStablePosition(progress, force, waitForEnabled);
const result = await this._waitForDisplayedAtStablePosition(progress, waitForEnabled); if (result !== 'done')
if (result !== 'done') return result;
return result;
}
if ((options as any).__testHookAfterStable) if ((options as any).__testHookAfterStable)
await (options as any).__testHookAfterStable(); await (options as any).__testHookAfterStable();
@ -469,7 +467,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this._retryPointerAction(progress, 'tap', true /* waitForEnabled */, point => this._page.touchscreen.tap(point.x, point.y), options); return this._retryPointerAction(progress, 'tap', true /* waitForEnabled */, point => this._page.touchscreen.tap(point.x, point.y), options);
} }
async selectOption(metadata: CallMetadata, elements: ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions): Promise<string[]> { async selectOption(metadata: CallMetadata, elements: ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions & types.ForceOptions): Promise<string[]> {
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
return controller.run(async progress => { return controller.run(async progress => {
const result = await this._selectOption(progress, elements, values, options); const result = await this._selectOption(progress, elements, values, options);
@ -477,15 +475,15 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, this._page._timeoutSettings.timeout(options)); }, this._page._timeoutSettings.timeout(options));
} }
async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions): Promise<string[] | 'error:notconnected'> { async _selectOption(progress: Progress, elements: ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions & types.ForceOptions): Promise<string[] | 'error:notconnected'> {
const optionsToSelect = [...elements, ...values]; const optionsToSelect = [...elements, ...values];
await progress.beforeInputAction(this); await progress.beforeInputAction(this);
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
progress.throwIfAborted(); // Avoid action that has side-effects. progress.throwIfAborted(); // Avoid action that has side-effects.
progress.log(' selecting specified option(s)'); progress.log(' selecting specified option(s)');
const poll = await this.evaluateHandleInUtility(([injected, node, optionsToSelect]) => { const poll = await this.evaluateHandleInUtility(([injected, node, { optionsToSelect, force }]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled'], injected.selectOptions.bind(injected, optionsToSelect)); return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled'], force, injected.selectOptions.bind(injected, optionsToSelect));
}, optionsToSelect); }, { optionsToSelect, force: options.force });
const pollHandler = new InjectedScriptPollHandler(progress, poll); const pollHandler = new InjectedScriptPollHandler(progress, poll);
const result = throwFatalDOMError(await pollHandler.finish()); const result = throwFatalDOMError(await pollHandler.finish());
await this._page._doSlowMo(); await this._page._doSlowMo();
@ -493,7 +491,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}); });
} }
async fill(metadata: CallMetadata, value: string, options: types.NavigatingActionWaitOptions = {}): Promise<void> { async fill(metadata: CallMetadata, value: string, options: types.NavigatingActionWaitOptions & types.ForceOptions = {}): Promise<void> {
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
return controller.run(async progress => { return controller.run(async progress => {
const result = await this._fill(progress, value, options); const result = await this._fill(progress, value, options);
@ -501,14 +499,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, this._page._timeoutSettings.timeout(options)); }, this._page._timeoutSettings.timeout(options));
} }
async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { async _fill(progress: Progress, value: string, options: types.NavigatingActionWaitOptions & types.ForceOptions): Promise<'error:notconnected' | 'done'> {
progress.log(`elementHandle.fill("${value}")`); progress.log(`elementHandle.fill("${value}")`);
await progress.beforeInputAction(this); await progress.beforeInputAction(this);
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
progress.log(' waiting for element to be visible, enabled and editable'); progress.log(' waiting for element to be visible, enabled and editable');
const poll = await this.evaluateHandleInUtility(([injected, node, value]) => { const poll = await this.evaluateHandleInUtility(([injected, node, { value, force }]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled', 'editable'], injected.fill.bind(injected, value)); return injected.waitForElementStatesAndPerformAction(node, ['visible', 'enabled', 'editable'], force, injected.fill.bind(injected, value));
}, value); }, { value, force: options.force });
const pollHandler = new InjectedScriptPollHandler(progress, poll); const pollHandler = new InjectedScriptPollHandler(progress, poll);
const filled = throwFatalDOMError(await pollHandler.finish()); const filled = throwFatalDOMError(await pollHandler.finish());
progress.throwIfAborted(); // Avoid action that has side-effects. progress.throwIfAborted(); // Avoid action that has side-effects.
@ -528,13 +526,13 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}, 'input'); }, 'input');
} }
async selectText(metadata: CallMetadata, options: types.TimeoutOptions = {}): Promise<void> { async selectText(metadata: CallMetadata, options: types.TimeoutOptions & types.ForceOptions = {}): Promise<void> {
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
return controller.run(async progress => { return controller.run(async progress => {
progress.throwIfAborted(); // Avoid action that has side-effects. progress.throwIfAborted(); // Avoid action that has side-effects.
const poll = await this.evaluateHandleInUtility(([injected, node]) => { const poll = await this.evaluateHandleInUtility(([injected, node, force]) => {
return injected.waitForElementStatesAndPerformAction(node, ['visible'], injected.selectText.bind(injected)); return injected.waitForElementStatesAndPerformAction(node, ['visible'], force, injected.selectText.bind(injected));
}, {}); }, options.force);
const pollHandler = new InjectedScriptPollHandler(progress, poll); const pollHandler = new InjectedScriptPollHandler(progress, poll);
const result = throwFatalDOMError(await pollHandler.finish()); const result = throwFatalDOMError(await pollHandler.finish());
assertDone(throwRetargetableDOMError(result)); assertDone(throwRetargetableDOMError(result));
@ -732,7 +730,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return controller.run(async progress => { return controller.run(async progress => {
progress.log(` waiting for element to be ${state}`); progress.log(` waiting for element to be ${state}`);
const poll = await this.evaluateHandleInUtility(([injected, node, state]) => { const poll = await this.evaluateHandleInUtility(([injected, node, state]) => {
return injected.waitForElementStatesAndPerformAction(node, [state], () => 'done' as const); return injected.waitForElementStatesAndPerformAction(node, [state], false, () => 'done' as const);
}, state); }, state);
const pollHandler = new InjectedScriptPollHandler(progress, poll); const pollHandler = new InjectedScriptPollHandler(progress, poll);
assertDone(throwRetargetableDOMError(throwFatalDOMError(await pollHandler.finish()))); assertDone(throwRetargetableDOMError(throwFatalDOMError(await pollHandler.finish())));
@ -770,15 +768,15 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
return this; return this;
} }
async _waitForDisplayedAtStablePosition(progress: Progress, waitForEnabled: boolean): Promise<'error:notconnected' | 'done'> { async _waitForDisplayedAtStablePosition(progress: Progress, force: boolean, waitForEnabled: boolean): Promise<'error:notconnected' | 'done'> {
if (waitForEnabled) if (waitForEnabled)
progress.log(` waiting for element to be visible, enabled and stable`); progress.log(` waiting for element to be visible, enabled and stable`);
else else
progress.log(` waiting for element to be visible and stable`); progress.log(` waiting for element to be visible and stable`);
const poll = this.evaluateHandleInUtility(([injected, node, waitForEnabled]) => { const poll = this.evaluateHandleInUtility(([injected, node, { waitForEnabled, force }]) => {
return injected.waitForElementStatesAndPerformAction(node, return injected.waitForElementStatesAndPerformAction(node,
waitForEnabled ? ['visible', 'stable', 'enabled'] : ['visible', 'stable'], () => 'done' as const); waitForEnabled ? ['visible', 'stable', 'enabled'] : ['visible', 'stable'], force, () => 'done' as const);
}, waitForEnabled); }, { waitForEnabled, force });
const pollHandler = new InjectedScriptPollHandler(progress, await poll); const pollHandler = new InjectedScriptPollHandler(progress, await poll);
const result = await pollHandler.finish(); const result = await pollHandler.finish();
if (waitForEnabled) if (waitForEnabled)

View File

@ -986,7 +986,7 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout(options)); }, this._page._timeoutSettings.timeout(options));
} }
async fill(metadata: CallMetadata, selector: string, value: string, options: types.NavigatingActionWaitOptions) { async fill(metadata: CallMetadata, selector: string, value: string, options: types.NavigatingActionWaitOptions & { force?: boolean }) {
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
return controller.run(async progress => { return controller.run(async progress => {
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._fill(progress, value, options))); return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._fill(progress, value, options)));
@ -1097,7 +1097,7 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout(options)); }, this._page._timeoutSettings.timeout(options));
} }
async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions = {}): Promise<string[]> { async selectOption(metadata: CallMetadata, selector: string, elements: dom.ElementHandle[], values: types.SelectOption[], options: types.NavigatingActionWaitOptions & types.ForceOptions = {}): Promise<string[]> {
const controller = new ProgressController(metadata, this); const controller = new ProgressController(metadata, this);
return controller.run(async progress => { return controller.run(async progress => {
return await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._selectOption(progress, elements, values, options)); return await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._selectOption(progress, elements, values, options));

View File

@ -344,7 +344,7 @@ export class InjectedScript {
return element; return element;
} }
waitForElementStatesAndPerformAction<T>(node: Node, states: ElementState[], waitForElementStatesAndPerformAction<T>(node: Node, states: ElementState[], force: boolean | undefined,
callback: (node: Node, progress: InjectedScriptProgress, continuePolling: symbol) => T | symbol): InjectedScriptPoll<T | 'error:notconnected' | FatalDOMError> { callback: (node: Node, progress: InjectedScriptProgress, continuePolling: symbol) => T | symbol): InjectedScriptPoll<T | 'error:notconnected' | FatalDOMError> {
let lastRect: { x: number, y: number, width: number, height: number } | undefined; let lastRect: { x: number, y: number, width: number, height: number } | undefined;
let counter = 0; let counter = 0;
@ -352,6 +352,11 @@ export class InjectedScript {
let lastTime = 0; let lastTime = 0;
const predicate = (progress: InjectedScriptProgress, continuePolling: symbol) => { const predicate = (progress: InjectedScriptProgress, continuePolling: symbol) => {
if (force) {
progress.log(` forcing action`);
return callback(node, progress, continuePolling);
}
for (const state of states) { for (const state of states) {
if (state !== 'stable') { if (state !== 'stable') {
const result = this.checkElementState(node, state); const result = this.checkElementState(node, state);

View File

@ -33,8 +33,11 @@ export type NavigatingActionWaitOptions = TimeoutOptions & {
noWaitAfter?: boolean, noWaitAfter?: boolean,
}; };
export type PointerActionWaitOptions = TimeoutOptions & { export type ForceOptions = {
force?: boolean, force?: boolean,
};
export type PointerActionWaitOptions = TimeoutOptions & ForceOptions & {
trial?: boolean; trial?: boolean;
}; };

35
types/types.d.ts vendored
View File

@ -1756,6 +1756,11 @@ export interface Page {
* @param options * @param options
*/ */
fill(selector: string, value: string, options?: { fill(selector: string, value: string, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -2508,6 +2513,11 @@ export interface Page {
*/ */
index?: number; index?: number;
}>, options?: { }>, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -3870,6 +3880,11 @@ export interface Frame {
* @param options * @param options
*/ */
fill(selector: string, value: string, options?: { fill(selector: string, value: string, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -4299,6 +4314,11 @@ export interface Frame {
*/ */
index?: number; index?: number;
}>, options?: { }>, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -6313,6 +6333,11 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* @param options * @param options
*/ */
fill(value: string, options?: { fill(value: string, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -6612,6 +6637,11 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
*/ */
index?: number; index?: number;
}>, options?: { }>, options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
* opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
@ -6634,6 +6664,11 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
* @param options * @param options
*/ */
selectText(options?: { selectText(options?: {
/**
* Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`.
*/
force?: boolean;
/** /**
* Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
* using the * using the