api: add waitForElementState('disabled') (#3537)
Allows waiting for the element to be disabled.
This commit is contained in:
parent
0a22e2758a
commit
18292325b6
|
|
@ -3113,7 +3113,7 @@ If the element is detached from the DOM at any moment during the action, this me
|
|||
When all steps combined have not finished during the specified `timeout`, this method rejects with a [TimeoutError]. Passing zero timeout disables this.
|
||||
|
||||
#### elementHandle.waitForElementState(state[, options])
|
||||
- `state` <"visible"|"hidden"|"stable"|"enabled"> A state to wait for, see below for more details.
|
||||
- `state` <"visible"|"hidden"|"stable"|"enabled"|"disabled"> A state to wait for, see below for more details.
|
||||
- `options` <[Object]>
|
||||
- `timeout` <[number]> 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)](#browsercontextsetdefaulttimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
|
||||
- returns: <[Promise]> Promise that resolves when the element satisfies the `state`.
|
||||
|
|
@ -3123,6 +3123,7 @@ Depending on the `state` parameter, this method waits for one of the [actionabil
|
|||
- `"hidden"` Wait until the element is [not visible](./actionability.md#visible) or [not attached](./actionability.md#attached). Note that waiting for hidden does not throw when the element detaches.
|
||||
- `"stable"` Wait until the element is both [visible](./actionability.md#visible) and [stable](./actionability.md#stable).
|
||||
- `"enabled"` Wait until the element is [enabled](./actionability.md#enabled).
|
||||
- `"disabled"` Wait until the element is [not enabled](./actionability.md#enabled).
|
||||
|
||||
If the element does not satisfy the condition for the `timeout` milliseconds, this method will throw.
|
||||
|
||||
|
|
|
|||
11
src/dom.ts
11
src/dom.ts
|
|
@ -602,8 +602,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return result;
|
||||
}
|
||||
|
||||
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled', options: types.TimeoutOptions = {}): Promise<void> {
|
||||
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: types.TimeoutOptions = {}): Promise<void> {
|
||||
return this._page._runAbortableTask(async progress => {
|
||||
progress.log(` waiting for element to be ${state}`);
|
||||
if (state === 'visible') {
|
||||
const poll = await this._evaluateHandleInUtility(([injected, node]) => {
|
||||
return injected.waitForNodeVisible(node);
|
||||
|
|
@ -628,6 +629,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
assertDone(throwRetargetableDOMError(await pollHandler.finish()));
|
||||
return;
|
||||
}
|
||||
if (state === 'disabled') {
|
||||
const poll = await this._evaluateHandleInUtility(([injected, node]) => {
|
||||
return injected.waitForNodeDisabled(node);
|
||||
}, {});
|
||||
const pollHandler = new InjectedScriptPollHandler(progress, poll);
|
||||
assertDone(throwRetargetableDOMError(await pollHandler.finish()));
|
||||
return;
|
||||
}
|
||||
if (state === 'stable') {
|
||||
const rafCount = this._page._delegate.rafCountForStablePosition();
|
||||
const poll = await this._evaluateHandleInUtility(([injected, node, rafCount]) => {
|
||||
|
|
|
|||
|
|
@ -383,6 +383,19 @@ export default class InjectedScript {
|
|||
});
|
||||
}
|
||||
|
||||
waitForNodeDisabled(node: Node): types.InjectedScriptPoll<'error:notconnected' | 'done'> {
|
||||
return this.pollRaf((progress, continuePolling) => {
|
||||
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
||||
if (!node.isConnected || !element)
|
||||
return 'error:notconnected';
|
||||
if (!this._isElementDisabled(element)) {
|
||||
progress.logRepeating(' element is enabled - waiting...');
|
||||
return continuePolling;
|
||||
}
|
||||
return 'done';
|
||||
});
|
||||
}
|
||||
|
||||
focusNode(node: Node, resetSelectionIfNotFocused?: boolean): FatalDOMError | 'error:notconnected' | 'done' {
|
||||
if (!node.isConnected)
|
||||
return 'error:notconnected';
|
||||
|
|
|
|||
|
|
@ -1914,7 +1914,7 @@ export type ElementHandleUncheckOptions = {
|
|||
};
|
||||
export type ElementHandleUncheckResult = void;
|
||||
export type ElementHandleWaitForElementStateParams = {
|
||||
state: 'visible' | 'hidden' | 'stable' | 'enabled',
|
||||
state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled',
|
||||
timeout?: number,
|
||||
};
|
||||
export type ElementHandleWaitForElementStateOptions = {
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
|
|||
});
|
||||
}
|
||||
|
||||
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled', options: ElementHandleWaitForElementStateOptions = {}): Promise<void> {
|
||||
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled', options: ElementHandleWaitForElementStateOptions = {}): Promise<void> {
|
||||
return this._wrapApiCall('elementHandle.waitForElementState', async () => {
|
||||
return await this._elementChannel.waitForElementState({ state, ...options });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1585,6 +1585,7 @@ ElementHandle:
|
|||
- hidden
|
||||
- stable
|
||||
- enabled
|
||||
- disabled
|
||||
timeout: number?
|
||||
|
||||
waitForSelector:
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements Eleme
|
|||
return { value: serializeResult(await this._elementHandle._$$evalExpression(params.selector, params.expression, params.isFunction, parseArgument(params.arg))) };
|
||||
}
|
||||
|
||||
async waitForElementState(params: { state: 'visible' | 'hidden' | 'stable' | 'enabled' } & types.TimeoutOptions): Promise<void> {
|
||||
async waitForElementState(params: { state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' } & types.TimeoutOptions): Promise<void> {
|
||||
await this._elementHandle.waitForElementState(params.state, params);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -759,7 +759,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.ElementHandleWaitForElementStateParams = tObject({
|
||||
state: tEnum(['visible', 'hidden', 'stable', 'enabled']),
|
||||
state: tEnum(['visible', 'hidden', 'stable', 'enabled', 'disabled']),
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.ElementHandleWaitForSelectorParams = tObject({
|
||||
|
|
|
|||
|
|
@ -102,6 +102,17 @@ it('should throw waiting for enabled when detached', async ({ page }) => {
|
|||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
|
||||
it('should wait for disabled button', async({page}) => {
|
||||
await page.setContent('<button><span>Target</span></button>');
|
||||
const span = await page.$('text=Target');
|
||||
let done = false;
|
||||
const promise = span.waitForElementState('disabled').then(() => done = true);
|
||||
await giveItAChanceToResolve(page);
|
||||
expect(done).toBe(false);
|
||||
await span.evaluate(span => (span.parentElement as HTMLButtonElement).disabled = true);
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('should wait for stable position', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
|
|
|
|||
Loading…
Reference in a new issue