Skip to content

Commit

Permalink
api: add isVisible, isHidden, isEnabled, isDisabled and isEditable (#…
Browse files Browse the repository at this point in the history
…4915)

These methods are useful for verification in tests, e.g.
```js
expect(await page.isEnabled(':text("Remove All")')).toBe(false);
await page.click(':text("Add Item")');
expect(await page.isVisible('.item:text("new item")')).toBe(true);
expect(await page.isEnabled(':text("Remove All")')).toBe(true);
```
  • Loading branch information
dgozman committed Jan 8, 2021
1 parent 498f9a5 commit 3f90405
Show file tree
Hide file tree
Showing 18 changed files with 781 additions and 14 deletions.
4 changes: 2 additions & 2 deletions docs/src/actionability.md
Expand Up @@ -44,11 +44,11 @@ Element is considered stable when it has maintained the same bounding box for at

### Enabled

Element is considered enabled when it is not a `<button>`, `<select>` or `<input>` with a `disabled` property set.
Element is considered enabled when it is not a `<button>`, `<select>`, `<input>` or `<textarea>` with a `disabled` property set.

### Editable

Element is considered editable when it does not have `readonly` property set.
Element is considered editable when it is [enabled] and does not have `readonly` property set.

### Receiving events

Expand Down
28 changes: 27 additions & 1 deletion docs/src/api/class-elementhandle.md
Expand Up @@ -317,6 +317,31 @@ Returns the `element.innerHTML`.

Returns the `element.innerText`.

## async method: ElementHandle.isDisabled
- returns: <[boolean]>

Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).

## async method: ElementHandle.isEditable
- returns: <[boolean]>

Returns whether the element is [editable](./actionability.md#editable).

## async method: ElementHandle.isEnabled
- returns: <[boolean]>

Returns whether the element is [enabled](./actionability.md#enabled).

## async method: ElementHandle.isHidden
- returns: <[boolean]>

Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).

## async method: ElementHandle.isVisible
- returns: <[boolean]>

Returns whether the element is [visible](./actionability.md#visible).

## async method: ElementHandle.ownerFrame
- returns: <[null]|[Frame]>

Expand Down Expand Up @@ -544,11 +569,12 @@ checks to pass. This method throws when the element is detached while waiting, u
* `"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).
* `"editable"` Wait until the element is [editable](./actionability.md#editable).

If the element does not satisfy the condition for the [`option: timeout`] milliseconds, this method will throw.

### param: ElementHandle.waitForElementState.state
- `state` <"visible"|"hidden"|"stable"|"enabled"|"disabled">
- `state` <"visible"|"hidden"|"stable"|"enabled"|"disabled"|"editable">

A state to wait for, see below for more details.

Expand Down
45 changes: 45 additions & 0 deletions docs/src/api/class-frame.md
Expand Up @@ -507,6 +507,51 @@ Returns `element.innerText`.

Returns `true` if the frame has been detached, or `false` otherwise.

## async method: Frame.isDisabled
- returns: <[boolean]>

Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).

### param: Frame.isDisabled.selector = %%-input-selector-%%

### option: Frame.isDisabled.timeout = %%-input-timeout-%%

## async method: Frame.isEditable
- returns: <[boolean]>

Returns whether the element is [editable](./actionability.md#editable).

### param: Frame.isEditable.selector = %%-input-selector-%%

### option: Frame.isEditable.timeout = %%-input-timeout-%%

## async method: Frame.isEnabled
- returns: <[boolean]>

Returns whether the element is [enabled](./actionability.md#enabled).

### param: Frame.isEnabled.selector = %%-input-selector-%%

### option: Frame.isEnabled.timeout = %%-input-timeout-%%

## async method: Frame.isHidden
- returns: <[boolean]>

Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).

### param: Frame.isHidden.selector = %%-input-selector-%%

### option: Frame.isHidden.timeout = %%-input-timeout-%%

## async method: Frame.isVisible
- returns: <[boolean]>

Returns whether the element is [visible](./actionability.md#visible).

### param: Frame.isVisible.selector = %%-input-selector-%%

### option: Frame.isVisible.timeout = %%-input-timeout-%%

## method: Frame.name
- returns: <[string]>

Expand Down
45 changes: 45 additions & 0 deletions docs/src/api/class-page.md
Expand Up @@ -972,6 +972,51 @@ Returns `element.innerText`.

Indicates that the page has been closed.

## async method: Page.isDisabled
- returns: <[boolean]>

Returns whether the element is disabled, the opposite of [enabled](./actionability.md#enabled).

### param: Page.isDisabled.selector = %%-input-selector-%%

### option: Page.isDisabled.timeout = %%-input-timeout-%%

## async method: Page.isEditable
- returns: <[boolean]>

Returns whether the element is [editable](./actionability.md#editable).

### param: Page.isEditable.selector = %%-input-selector-%%

### option: Page.isEditable.timeout = %%-input-timeout-%%

## async method: Page.isEnabled
- returns: <[boolean]>

Returns whether the element is [enabled](./actionability.md#enabled).

### param: Page.isEnabled.selector = %%-input-selector-%%

### option: Page.isEnabled.timeout = %%-input-timeout-%%

## async method: Page.isHidden
- returns: <[boolean]>

Returns whether the element is hidden, the opposite of [visible](./actionability.md#visible).

### param: Page.isHidden.selector = %%-input-selector-%%

### option: Page.isHidden.timeout = %%-input-timeout-%%

## async method: Page.isVisible
- returns: <[boolean]>

Returns whether the element is [visible](./actionability.md#visible).

### param: Page.isVisible.selector = %%-input-selector-%%

### option: Page.isVisible.timeout = %%-input-timeout-%%

## property: Page.keyboard
- type: <[Keyboard]>

Expand Down
30 changes: 30 additions & 0 deletions src/client/elementHandle.ts
Expand Up @@ -87,6 +87,36 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
});
}

async isDisabled(): Promise<boolean> {
return this._wrapApiCall('elementHandle.isDisabled', async () => {
return (await this._elementChannel.isDisabled()).value;
});
}

async isEditable(): Promise<boolean> {
return this._wrapApiCall('elementHandle.isEditable', async () => {
return (await this._elementChannel.isEditable()).value;
});
}

async isEnabled(): Promise<boolean> {
return this._wrapApiCall('elementHandle.isEnabled', async () => {
return (await this._elementChannel.isEnabled()).value;
});
}

async isHidden(): Promise<boolean> {
return this._wrapApiCall('elementHandle.isHidden', async () => {
return (await this._elementChannel.isHidden()).value;
});
}

async isVisible(): Promise<boolean> {
return this._wrapApiCall('elementHandle.isVisible', async () => {
return (await this._elementChannel.isVisible()).value;
});
}

async dispatchEvent(type: string, eventInit: Object = {}) {
return this._wrapApiCall('elementHandle.dispatchEvent', async () => {
await this._elementChannel.dispatchEvent({ type, eventInit: serializeArgument(eventInit) });
Expand Down
30 changes: 30 additions & 0 deletions src/client/frame.ts
Expand Up @@ -362,6 +362,36 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}

async isDisabled(selector: string, options: channels.FrameIsDisabledOptions = {}): Promise<boolean> {
return this._wrapApiCall(this._apiName('isDisabled'), async () => {
return (await this._channel.isDisabled({ selector, ...options })).value;
});
}

async isEditable(selector: string, options: channels.FrameIsEditableOptions = {}): Promise<boolean> {
return this._wrapApiCall(this._apiName('isEditable'), async () => {
return (await this._channel.isEditable({ selector, ...options })).value;
});
}

async isEnabled(selector: string, options: channels.FrameIsEnabledOptions = {}): Promise<boolean> {
return this._wrapApiCall(this._apiName('isEnabled'), async () => {
return (await this._channel.isEnabled({ selector, ...options })).value;
});
}

async isHidden(selector: string, options: channels.FrameIsHiddenOptions = {}): Promise<boolean> {
return this._wrapApiCall(this._apiName('isHidden'), async () => {
return (await this._channel.isHidden({ selector, ...options })).value;
});
}

async isVisible(selector: string, options: channels.FrameIsVisibleOptions = {}): Promise<boolean> {
return this._wrapApiCall(this._apiName('isVisible'), async () => {
return (await this._channel.isVisible({ selector, ...options })).value;
});
}

async hover(selector: string, options: channels.FrameHoverOptions = {}) {
return this._wrapApiCall(this._apiName('hover'), async () => {
await this._channel.hover({ selector, ...options });
Expand Down
20 changes: 20 additions & 0 deletions src/client/page.ts
Expand Up @@ -530,6 +530,26 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
return this._attributeToPage(() => this._mainFrame.getAttribute(selector, name, options));
}

async isDisabled(selector: string, options?: channels.FrameIsDisabledOptions): Promise<boolean> {
return this._attributeToPage(() => this._mainFrame.isDisabled(selector, options));
}

async isEditable(selector: string, options?: channels.FrameIsEditableOptions): Promise<boolean> {
return this._attributeToPage(() => this._mainFrame.isEditable(selector, options));
}

async isEnabled(selector: string, options?: channels.FrameIsEnabledOptions): Promise<boolean> {
return this._attributeToPage(() => this._mainFrame.isEnabled(selector, options));
}

async isHidden(selector: string, options?: channels.FrameIsHiddenOptions): Promise<boolean> {
return this._attributeToPage(() => this._mainFrame.isHidden(selector, options));
}

async isVisible(selector: string, options?: channels.FrameIsVisibleOptions): Promise<boolean> {
return this._attributeToPage(() => this._mainFrame.isVisible(selector, options));
}

async hover(selector: string, options?: channels.FrameHoverOptions) {
return this._attributeToPage(() => this._mainFrame.hover(selector, options));
}
Expand Down
20 changes: 20 additions & 0 deletions src/dispatchers/elementHandlerDispatcher.ts
Expand Up @@ -66,6 +66,26 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
return { value: await this._elementHandle.innerHTML() };
}

async isDisabled(): Promise<channels.ElementHandleIsDisabledResult> {
return { value: await this._elementHandle.isDisabled() };
}

async isEditable(): Promise<channels.ElementHandleIsEditableResult> {
return { value: await this._elementHandle.isEditable() };
}

async isEnabled(): Promise<channels.ElementHandleIsEnabledResult> {
return { value: await this._elementHandle.isEnabled() };
}

async isHidden(): Promise<channels.ElementHandleIsHiddenResult> {
return { value: await this._elementHandle.isHidden() };
}

async isVisible(): Promise<channels.ElementHandleIsVisibleResult> {
return { value: await this._elementHandle.isVisible() };
}

async dispatchEvent(params: channels.ElementHandleDispatchEventParams): Promise<void> {
await this._elementHandle.dispatchEvent(params.type, parseArgument(params.eventInit));
}
Expand Down
20 changes: 20 additions & 0 deletions src/dispatchers/frameDispatcher.ts
Expand Up @@ -159,6 +159,26 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
return { value: value === null ? undefined : value };
}

async isDisabled(params: channels.FrameIsDisabledParams): Promise<channels.FrameIsDisabledResult> {
return { value: await this._frame.isDisabled(params.selector, params) };
}

async isEditable(params: channels.FrameIsEditableParams): Promise<channels.FrameIsEditableResult> {
return { value: await this._frame.isEditable(params.selector, params) };
}

async isEnabled(params: channels.FrameIsEnabledParams): Promise<channels.FrameIsEnabledResult> {
return { value: await this._frame.isEnabled(params.selector, params) };
}

async isHidden(params: channels.FrameIsHiddenParams): Promise<channels.FrameIsHiddenResult> {
return { value: await this._frame.isHidden(params.selector, params) };
}

async isVisible(params: channels.FrameIsVisibleParams): Promise<channels.FrameIsVisibleResult> {
return { value: await this._frame.isVisible(params.selector, params) };
}

async hover(params: channels.FrameHoverParams, metadata?: channels.Metadata): Promise<void> {
return runAction(async controller => {
return await this._frame.hover(controller, params.selector, params);
Expand Down

0 comments on commit 3f90405

Please sign in to comment.