mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-27 13:50:25 +03:00
api: add isChecked method (#4953)
This adds `{Page,Frame}.isChecked(selector)` and `ElementHandle.isChecked()` methods. Useful to do assertions in tests: ```js await page.click('text="Add TODO"'); expect(await page.isChecked('.item-done')).toBe(false); ```
This commit is contained in:
parent
3b617b3709
commit
5a2cfdbd47
@ -327,6 +327,11 @@ Returns the `element.innerHTML`.
|
||||
|
||||
Returns the `element.innerText`.
|
||||
|
||||
## async method: ElementHandle.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
|
||||
## async method: ElementHandle.isDisabled
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -536,6 +536,15 @@ Returns `element.innerText`.
|
||||
|
||||
### option: Frame.innerText.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Frame.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
|
||||
### param: Frame.isChecked.selector = %%-input-selector-%%
|
||||
|
||||
### option: Frame.isChecked.timeout = %%-input-timeout-%%
|
||||
|
||||
## method: Frame.isDetached
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -1002,6 +1002,15 @@ Returns `element.innerText`.
|
||||
|
||||
### option: Page.innerText.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Page.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
|
||||
### param: Page.isChecked.selector = %%-input-selector-%%
|
||||
|
||||
### option: Page.isChecked.timeout = %%-input-timeout-%%
|
||||
|
||||
## method: Page.isClosed
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -87,6 +87,12 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
||||
});
|
||||
}
|
||||
|
||||
async isChecked(): Promise<boolean> {
|
||||
return this._wrapApiCall('elementHandle.isChecked', async () => {
|
||||
return (await this._elementChannel.isChecked()).value;
|
||||
});
|
||||
}
|
||||
|
||||
async isDisabled(): Promise<boolean> {
|
||||
return this._wrapApiCall('elementHandle.isDisabled', async () => {
|
||||
return (await this._elementChannel.isDisabled()).value;
|
||||
|
@ -362,6 +362,12 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
|
||||
});
|
||||
}
|
||||
|
||||
async isChecked(selector: string, options: channels.FrameIsCheckedOptions = {}): Promise<boolean> {
|
||||
return this._wrapApiCall(this._apiName('isChecked'), async () => {
|
||||
return (await this._channel.isChecked({ selector, ...options })).value;
|
||||
});
|
||||
}
|
||||
|
||||
async isDisabled(selector: string, options: channels.FrameIsDisabledOptions = {}): Promise<boolean> {
|
||||
return this._wrapApiCall(this._apiName('isDisabled'), async () => {
|
||||
return (await this._channel.isDisabled({ selector, ...options })).value;
|
||||
|
@ -530,6 +530,10 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||
return this._attributeToPage(() => this._mainFrame.getAttribute(selector, name, options));
|
||||
}
|
||||
|
||||
async isChecked(selector: string, options?: channels.FrameIsCheckedOptions): Promise<boolean> {
|
||||
return this._attributeToPage(() => this._mainFrame.isChecked(selector, options));
|
||||
}
|
||||
|
||||
async isDisabled(selector: string, options?: channels.FrameIsDisabledOptions): Promise<boolean> {
|
||||
return this._attributeToPage(() => this._mainFrame.isDisabled(selector, options));
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
|
||||
return { value: await this._elementHandle.innerHTML() };
|
||||
}
|
||||
|
||||
async isChecked(): Promise<channels.ElementHandleIsCheckedResult> {
|
||||
return { value: await this._elementHandle.isChecked() };
|
||||
}
|
||||
|
||||
async isDisabled(): Promise<channels.ElementHandleIsDisabledResult> {
|
||||
return { value: await this._elementHandle.isDisabled() };
|
||||
}
|
||||
|
@ -159,6 +159,10 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
return { value: value === null ? undefined : value };
|
||||
}
|
||||
|
||||
async isChecked(params: channels.FrameIsCheckedParams): Promise<channels.FrameIsCheckedResult> {
|
||||
return { value: await this._frame.isChecked(params.selector, params) };
|
||||
}
|
||||
|
||||
async isDisabled(params: channels.FrameIsDisabledParams): Promise<channels.FrameIsDisabledResult> {
|
||||
return { value: await this._frame.isDisabled(params.selector, params) };
|
||||
}
|
||||
|
@ -1190,6 +1190,7 @@ export interface FrameChannel extends Channel {
|
||||
hover(params: FrameHoverParams, metadata?: Metadata): Promise<FrameHoverResult>;
|
||||
innerHTML(params: FrameInnerHTMLParams, metadata?: Metadata): Promise<FrameInnerHTMLResult>;
|
||||
innerText(params: FrameInnerTextParams, metadata?: Metadata): Promise<FrameInnerTextResult>;
|
||||
isChecked(params: FrameIsCheckedParams, metadata?: Metadata): Promise<FrameIsCheckedResult>;
|
||||
isDisabled(params: FrameIsDisabledParams, metadata?: Metadata): Promise<FrameIsDisabledResult>;
|
||||
isEnabled(params: FrameIsEnabledParams, metadata?: Metadata): Promise<FrameIsEnabledResult>;
|
||||
isHidden(params: FrameIsHiddenParams, metadata?: Metadata): Promise<FrameIsHiddenResult>;
|
||||
@ -1446,6 +1447,16 @@ export type FrameInnerTextOptions = {
|
||||
export type FrameInnerTextResult = {
|
||||
value: string,
|
||||
};
|
||||
export type FrameIsCheckedParams = {
|
||||
selector: string,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameIsCheckedOptions = {
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameIsCheckedResult = {
|
||||
value: boolean,
|
||||
};
|
||||
export type FrameIsDisabledParams = {
|
||||
selector: string,
|
||||
timeout?: number,
|
||||
@ -1783,6 +1794,7 @@ export interface ElementHandleChannel extends JSHandleChannel {
|
||||
hover(params: ElementHandleHoverParams, metadata?: Metadata): Promise<ElementHandleHoverResult>;
|
||||
innerHTML(params?: ElementHandleInnerHTMLParams, metadata?: Metadata): Promise<ElementHandleInnerHTMLResult>;
|
||||
innerText(params?: ElementHandleInnerTextParams, metadata?: Metadata): Promise<ElementHandleInnerTextResult>;
|
||||
isChecked(params?: ElementHandleIsCheckedParams, metadata?: Metadata): Promise<ElementHandleIsCheckedResult>;
|
||||
isDisabled(params?: ElementHandleIsDisabledParams, metadata?: Metadata): Promise<ElementHandleIsDisabledResult>;
|
||||
isEditable(params?: ElementHandleIsEditableParams, metadata?: Metadata): Promise<ElementHandleIsEditableResult>;
|
||||
isEnabled(params?: ElementHandleIsEnabledParams, metadata?: Metadata): Promise<ElementHandleIsEnabledResult>;
|
||||
@ -1942,6 +1954,11 @@ export type ElementHandleInnerTextOptions = {};
|
||||
export type ElementHandleInnerTextResult = {
|
||||
value: string,
|
||||
};
|
||||
export type ElementHandleIsCheckedParams = {};
|
||||
export type ElementHandleIsCheckedOptions = {};
|
||||
export type ElementHandleIsCheckedResult = {
|
||||
value: boolean,
|
||||
};
|
||||
export type ElementHandleIsDisabledParams = {};
|
||||
export type ElementHandleIsDisabledOptions = {};
|
||||
export type ElementHandleIsDisabledResult = {
|
||||
|
@ -1218,6 +1218,13 @@ Frame:
|
||||
returns:
|
||||
value: string
|
||||
|
||||
isChecked:
|
||||
parameters:
|
||||
selector: string
|
||||
timeout: number?
|
||||
returns:
|
||||
value: boolean
|
||||
|
||||
isDisabled:
|
||||
parameters:
|
||||
selector: string
|
||||
@ -1637,6 +1644,10 @@ ElementHandle:
|
||||
returns:
|
||||
value: string
|
||||
|
||||
isChecked:
|
||||
returns:
|
||||
value: boolean
|
||||
|
||||
isDisabled:
|
||||
returns:
|
||||
value: boolean
|
||||
|
@ -578,6 +578,10 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameIsCheckedParams = tObject({
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameIsDisabledParams = tObject({
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
@ -770,6 +774,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.ElementHandleInnerHTMLParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleInnerTextParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsCheckedParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsDisabledParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsEditableParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsEnabledParams = tOptional(tObject({}));
|
||||
|
@ -664,6 +664,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}, {});
|
||||
}
|
||||
|
||||
async isChecked(): Promise<boolean> {
|
||||
return this._evaluateInUtility(([injected, node]) => {
|
||||
return injected.isCheckboxChecked(node);
|
||||
}, {});
|
||||
}
|
||||
|
||||
async waitForElementState(state: 'visible' | 'hidden' | 'stable' | 'enabled' | 'disabled' | 'editable', options: types.TimeoutOptions = {}): Promise<void> {
|
||||
return runAbortableTask(async progress => {
|
||||
progress.log(` waiting for element to be ${state}`);
|
||||
@ -1034,3 +1040,15 @@ export function editableTask(selector: SelectorInfo): SchedulableTask<boolean> {
|
||||
});
|
||||
}, selector.parsed);
|
||||
}
|
||||
|
||||
export function checkedTask(selector: SelectorInfo): SchedulableTask<boolean> {
|
||||
return injectedScript => injectedScript.evaluateHandle((injected, parsed) => {
|
||||
return injected.pollRaf((progress, continuePolling) => {
|
||||
const element = injected.querySelector(parsed, document);
|
||||
if (!element)
|
||||
return continuePolling;
|
||||
progress.log(` selector resolved to ${injected.previewNode(element)}`);
|
||||
return injected.isCheckboxChecked(element);
|
||||
});
|
||||
}, selector.parsed);
|
||||
}
|
||||
|
@ -963,6 +963,15 @@ export class Frame extends EventEmitter {
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async isChecked(selector: string, options: types.TimeoutOptions = {}): Promise<boolean> {
|
||||
const info = this._page.selectors._parseSelector(selector);
|
||||
const task = dom.checkedTask(info);
|
||||
return runAbortableTask(async progress => {
|
||||
progress.log(` checking checked state of "${selector}"`);
|
||||
return this._scheduleRerunnableTask(progress, info.world, task);
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async hover(controller: ProgressController, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions = {}) {
|
||||
return controller.run(async progress => {
|
||||
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._hover(progress, options)));
|
||||
|
@ -207,3 +207,15 @@ it('isEditable should work', async ({ page }) => {
|
||||
expect(await textarea.isEditable()).toBe(false);
|
||||
expect(await page.isEditable('textarea')).toBe(false);
|
||||
});
|
||||
|
||||
it('isChecked should work', async ({page}) => {
|
||||
await page.setContent(`<input type='checkbox' checked><div>Not a checkbox</div>`);
|
||||
const handle = await page.$('input');
|
||||
expect(await handle.isChecked()).toBe(true);
|
||||
expect(await page.isChecked('input')).toBe(true);
|
||||
await handle.evaluate(input => input.checked = false);
|
||||
expect(await handle.isChecked()).toBe(false);
|
||||
expect(await page.isChecked('input')).toBe(false);
|
||||
const error = await page.isChecked('div').catch(e => e);
|
||||
expect(error.message).toContain('Not a checkbox or radio button');
|
||||
});
|
||||
|
@ -92,3 +92,9 @@ it('should check the box by aria role', async ({page}) => {
|
||||
await page.check('div');
|
||||
expect(await page.evaluate(() => window['checkbox'].getAttribute('aria-checked'))).toBe('true');
|
||||
});
|
||||
|
||||
it('should throw when not a checkbox', async ({page}) => {
|
||||
await page.setContent(`<div>Check me</div>`);
|
||||
const error = await page.check('div').catch(e => e);
|
||||
expect(error.message).toContain('Not a checkbox or radio button');
|
||||
});
|
||||
|
37
types/types.d.ts
vendored
37
types/types.d.ts
vendored
@ -2033,6 +2033,22 @@ export interface Page {
|
||||
timeout?: number;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
* @param selector A selector to search for element. If there are multiple elements satisfying the selector, the first will be used. See [working with selectors](https://github.com/microsoft/playwright/blob/master/docs/selectors.md#working-with-selectors) for more details.
|
||||
* @param options
|
||||
*/
|
||||
isChecked(selector: string, 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(…)](https://github.com/microsoft/playwright/blob/master/docs/api.md#browsercontextsetdefaulttimeout)
|
||||
* or [page.setDefaultTimeout(…)](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagesetdefaulttimeout)
|
||||
* methods.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Indicates that the page has been closed.
|
||||
*/
|
||||
@ -3991,6 +4007,22 @@ export interface Frame {
|
||||
timeout?: number;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
* @param selector A selector to search for element. If there are multiple elements satisfying the selector, the first will be used. See [working with selectors](https://github.com/microsoft/playwright/blob/master/docs/selectors.md#working-with-selectors) for more details.
|
||||
* @param options
|
||||
*/
|
||||
isChecked(selector: string, 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(…)](https://github.com/microsoft/playwright/blob/master/docs/api.md#browsercontextsetdefaulttimeout)
|
||||
* or [page.setDefaultTimeout(…)](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagesetdefaulttimeout)
|
||||
* methods.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Returns `true` if the frame has been detached, or `false` otherwise.
|
||||
*/
|
||||
@ -5857,6 +5889,11 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
|
||||
*/
|
||||
innerText(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
*/
|
||||
isChecked(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is disabled, the opposite of [enabled](https://github.com/microsoft/playwright/blob/master/docs/actionability.md#enabled).
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user