mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(inputValue): implement *.inputValue() (#7285)
This commit is contained in:
parent
0d9cfd76c3
commit
5732307280
@ -499,6 +499,13 @@ Returns the `element.innerHTML`.
|
||||
|
||||
Returns the `element.innerText`.
|
||||
|
||||
## async method: ElementHandle.inputValue
|
||||
- returns: <[string]>
|
||||
|
||||
Returns `input.value` for `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
|
||||
### option: ElementHandle.inputValue.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: ElementHandle.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -854,6 +854,14 @@ Returns `element.innerText`.
|
||||
|
||||
### option: Frame.innerText.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Frame.inputValue
|
||||
- returns: <[string]>
|
||||
|
||||
Returns `input.value` for the selected `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
|
||||
### param: Frame.inputValue.selector = %%-input-selector-%%
|
||||
### option: Frame.inputValue.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Frame.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -1960,6 +1960,14 @@ Returns `element.innerText`.
|
||||
|
||||
### option: Page.innerText.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Page.inputValue
|
||||
- returns: <[string]>
|
||||
|
||||
Returns `input.value` for the selected `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
|
||||
### param: Page.inputValue.selector = %%-input-selector-%%
|
||||
### option: Page.inputValue.timeout = %%-input-timeout-%%
|
||||
|
||||
## async method: Page.isChecked
|
||||
- returns: <[boolean]>
|
||||
|
||||
|
@ -65,6 +65,12 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
||||
});
|
||||
}
|
||||
|
||||
async inputValue(): Promise<string> {
|
||||
return this._wrapApiCall('elementHandle.inputValue', async (channel: channels.ElementHandleChannel) => {
|
||||
return (await channel.inputValue()).value;
|
||||
});
|
||||
}
|
||||
|
||||
async textContent(): Promise<string | null> {
|
||||
return this._wrapApiCall('elementHandle.textContent', async (channel: channels.ElementHandleChannel) => {
|
||||
const value = (await channel.textContent()).value;
|
||||
|
@ -346,6 +346,12 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
|
||||
});
|
||||
}
|
||||
|
||||
async inputValue(selector: string, options: channels.FrameInputValueOptions = {}): Promise<string> {
|
||||
return this._wrapApiCall(this._apiName('inputValue'), async (channel: channels.FrameChannel) => {
|
||||
return (await channel.inputValue({ selector, ...options })).value;
|
||||
});
|
||||
}
|
||||
|
||||
async isChecked(selector: string, options: channels.FrameIsCheckedOptions = {}): Promise<boolean> {
|
||||
return this._wrapApiCall(this._apiName('isChecked'), async (channel: channels.FrameChannel) => {
|
||||
return (await channel.isChecked({ selector, ...options })).value;
|
||||
|
@ -549,6 +549,10 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
||||
return this._attributeToPage(() => this._mainFrame.getAttribute(selector, name, options));
|
||||
}
|
||||
|
||||
async inputValue(selector: string, options?: channels.FrameInputValueOptions): Promise<string> {
|
||||
return this._attributeToPage(() => this._mainFrame.inputValue(selector, options));
|
||||
}
|
||||
|
||||
async isChecked(selector: string, options?: channels.FrameIsCheckedOptions): Promise<boolean> {
|
||||
return this._attributeToPage(() => this._mainFrame.isChecked(selector, options));
|
||||
}
|
||||
|
@ -60,6 +60,11 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
|
||||
return { value: value === null ? undefined : value };
|
||||
}
|
||||
|
||||
async inputValue(params: channels.ElementHandleInputValueParams, metadata: CallMetadata): Promise<channels.ElementHandleInputValueResult> {
|
||||
const value = await this._elementHandle.inputValue();
|
||||
return { value };
|
||||
}
|
||||
|
||||
async textContent(params: channels.ElementHandleTextContentParams, metadata: CallMetadata): Promise<channels.ElementHandleTextContentResult> {
|
||||
const value = await this._elementHandle.textContent();
|
||||
return { value: value === null ? undefined : value };
|
||||
|
@ -147,6 +147,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameInitializer
|
||||
return { value: value === null ? undefined : value };
|
||||
}
|
||||
|
||||
async inputValue(params: channels.FrameInputValueParams, metadata: CallMetadata): Promise<channels.FrameInputValueResult> {
|
||||
const value = await this._frame.inputValue(metadata, params.selector, params);
|
||||
return { value };
|
||||
}
|
||||
|
||||
async isChecked(params: channels.FrameIsCheckedParams, metadata: CallMetadata): Promise<channels.FrameIsCheckedResult> {
|
||||
return { value: await this._frame.isChecked(metadata, params.selector, params) };
|
||||
}
|
||||
|
@ -1307,6 +1307,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>;
|
||||
inputValue(params: FrameInputValueParams, metadata?: Metadata): Promise<FrameInputValueResult>;
|
||||
isChecked(params: FrameIsCheckedParams, metadata?: Metadata): Promise<FrameIsCheckedResult>;
|
||||
isDisabled(params: FrameIsDisabledParams, metadata?: Metadata): Promise<FrameIsDisabledResult>;
|
||||
isEnabled(params: FrameIsEnabledParams, metadata?: Metadata): Promise<FrameIsEnabledResult>;
|
||||
@ -1571,6 +1572,16 @@ export type FrameInnerTextOptions = {
|
||||
export type FrameInnerTextResult = {
|
||||
value: string,
|
||||
};
|
||||
export type FrameInputValueParams = {
|
||||
selector: string,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameInputValueOptions = {
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameInputValueResult = {
|
||||
value: string,
|
||||
};
|
||||
export type FrameIsCheckedParams = {
|
||||
selector: string,
|
||||
timeout?: number,
|
||||
@ -1915,6 +1926,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>;
|
||||
inputValue(params?: ElementHandleInputValueParams, metadata?: Metadata): Promise<ElementHandleInputValueResult>;
|
||||
isChecked(params?: ElementHandleIsCheckedParams, metadata?: Metadata): Promise<ElementHandleIsCheckedResult>;
|
||||
isDisabled(params?: ElementHandleIsDisabledParams, metadata?: Metadata): Promise<ElementHandleIsDisabledResult>;
|
||||
isEditable(params?: ElementHandleIsEditableParams, metadata?: Metadata): Promise<ElementHandleIsEditableResult>;
|
||||
@ -2085,6 +2097,11 @@ export type ElementHandleInnerTextOptions = {};
|
||||
export type ElementHandleInnerTextResult = {
|
||||
value: string,
|
||||
};
|
||||
export type ElementHandleInputValueParams = {};
|
||||
export type ElementHandleInputValueOptions = {};
|
||||
export type ElementHandleInputValueResult = {
|
||||
value: string,
|
||||
};
|
||||
export type ElementHandleIsCheckedParams = {};
|
||||
export type ElementHandleIsCheckedOptions = {};
|
||||
export type ElementHandleIsCheckedResult = {
|
||||
|
@ -1243,6 +1243,13 @@ Frame:
|
||||
returns:
|
||||
value: string
|
||||
|
||||
inputValue:
|
||||
parameters:
|
||||
selector: string
|
||||
timeout: number?
|
||||
returns:
|
||||
value: string
|
||||
|
||||
isChecked:
|
||||
parameters:
|
||||
selector: string
|
||||
@ -1669,6 +1676,10 @@ ElementHandle:
|
||||
returns:
|
||||
value: string
|
||||
|
||||
inputValue:
|
||||
returns:
|
||||
value: string
|
||||
|
||||
isChecked:
|
||||
returns:
|
||||
value: boolean
|
||||
|
@ -643,6 +643,10 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameInputValueParams = tObject({
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameIsCheckedParams = tObject({
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
@ -843,6 +847,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||
});
|
||||
scheme.ElementHandleInnerHTMLParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleInnerTextParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleInputValueParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsCheckedParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsDisabledParams = tOptional(tObject({}));
|
||||
scheme.ElementHandleIsEditableParams = tOptional(tObject({}));
|
||||
|
@ -22,6 +22,7 @@ export type FatalDOMError =
|
||||
'error:notfillablenumberinput' |
|
||||
'error:notvaliddate' |
|
||||
'error:notinput' |
|
||||
'error:notinputvalue' |
|
||||
'error:notselect' |
|
||||
'error:notcheckbox';
|
||||
|
||||
|
@ -180,6 +180,15 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
}, name)).value;
|
||||
}
|
||||
|
||||
async inputValue(): Promise<string> {
|
||||
return throwFatalDOMError(await this.evaluateInUtility(([injeced, node]) => {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE || (node.nodeName !== 'INPUT' && node.nodeName !== 'TEXTAREA'))
|
||||
return 'error:notinputvalue';
|
||||
const element = node as unknown as (HTMLInputElement | HTMLTextAreaElement);
|
||||
return { value: element.value };
|
||||
}, undefined)).value;
|
||||
}
|
||||
|
||||
async textContent(): Promise<string | null> {
|
||||
return this.evaluateInUtility(([injected, node]) => node.textContent, {});
|
||||
}
|
||||
@ -874,6 +883,8 @@ export function throwFatalDOMError<T>(result: T | FatalDOMError): T {
|
||||
throw new Error(`Malformed value`);
|
||||
if (result === 'error:notinput')
|
||||
throw new Error('Node is not an HTMLInputElement');
|
||||
if (result === 'error:notinputvalue')
|
||||
throw new Error('Node is not an HTMLInputElement or HTMLTextAreaElement');
|
||||
if (result === 'error:notselect')
|
||||
throw new Error('Element is not a <select> element.');
|
||||
if (result === 'error:notcheckbox')
|
||||
@ -1012,6 +1023,20 @@ export function getAttributeTask(selector: SelectorInfo, name: string): Schedula
|
||||
}, { parsed: selector.parsed, name });
|
||||
}
|
||||
|
||||
export function inputValueTask(selector: SelectorInfo): SchedulableTask<string> {
|
||||
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)}`);
|
||||
if (element.nodeName !== 'INPUT' && element.nodeName !== 'TEXTAREA')
|
||||
return 'error:notinputvalue';
|
||||
return (element as any).value;
|
||||
});
|
||||
}, { parsed: selector.parsed });
|
||||
}
|
||||
|
||||
export function elementStateTask(selector: SelectorInfo, state: ElementStateWithoutStable): SchedulableTask<boolean | 'error:notconnected' | FatalDOMError> {
|
||||
return injectedScript => injectedScript.evaluateHandle((injected, { parsed, state }) => {
|
||||
return injected.pollRaf((progress, continuePolling) => {
|
||||
|
@ -1040,6 +1040,16 @@ export class Frame extends SdkObject {
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async inputValue(metadata: CallMetadata, selector: string, options: types.TimeoutOptions = {}): Promise<string> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
const info = this._page.selectors._parseSelector(selector);
|
||||
const task = dom.inputValueTask(info);
|
||||
return controller.run(async progress => {
|
||||
progress.log(` retrieving value from "${selector}"`);
|
||||
return dom.throwFatalDOMError(await this._scheduleRerunnableTask(progress, info.world, task));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
private async _checkElementState(metadata: CallMetadata, selector: string, state: ElementStateWithoutStable, options: types.TimeoutOptions = {}): Promise<boolean> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
const info = this._page.selectors._parseSelector(selector);
|
||||
|
@ -1,2 +1,4 @@
|
||||
<div id="outer" name="value"><div id="inner">Text,
|
||||
more text</div></div><input id="check" type=checkbox checked foo="bar"">
|
||||
<input id="input"></input>
|
||||
<textarea id="textarea"></textarea>
|
||||
|
@ -39,6 +39,22 @@ it('getAttribute should work', async ({ page, server }) => {
|
||||
expect(await page.getAttribute('#outer', 'foo')).toBe(null);
|
||||
});
|
||||
|
||||
it('inputValue should work', async ({ page, server }) => {
|
||||
await page.goto(`${server.PREFIX}/dom.html`);
|
||||
|
||||
await page.fill('#textarea', 'text value');
|
||||
expect(await page.inputValue('#textarea')).toBe('text value');
|
||||
|
||||
await page.fill('#input', 'input value');
|
||||
expect(await page.inputValue('#input')).toBe('input value');
|
||||
const handle = await page.$('#input');
|
||||
expect(await handle.inputValue()).toBe('input value');
|
||||
|
||||
expect(await page.inputValue('#inner').catch(e => e.message)).toContain('Node is not an HTMLInputElement or HTMLTextAreaElement');
|
||||
const handle2 = await page.$('#inner');
|
||||
expect(await handle2.inputValue().catch(e => e.message)).toContain('Node is not an HTMLInputElement or HTMLTextAreaElement');
|
||||
});
|
||||
|
||||
it('innerHTML should work', async ({ page, server }) => {
|
||||
await page.goto(`${server.PREFIX}/dom.html`);
|
||||
const handle = await page.$('#outer');
|
||||
|
44
types/types.d.ts
vendored
44
types/types.d.ts
vendored
@ -2024,6 +2024,21 @@ export interface Page {
|
||||
timeout?: number;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns `input.value` for the selected `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
* @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://playwright.dev/docs/selectors) for more details.
|
||||
* @param options
|
||||
*/
|
||||
inputValue(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(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<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://playwright.dev/docs/selectors) for more details.
|
||||
@ -4051,6 +4066,21 @@ export interface Frame {
|
||||
timeout?: number;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns `input.value` for the selected `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
* @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://playwright.dev/docs/selectors) for more details.
|
||||
* @param options
|
||||
*/
|
||||
inputValue(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(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<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://playwright.dev/docs/selectors) for more details.
|
||||
@ -6371,6 +6401,20 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
|
||||
*/
|
||||
innerText(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Returns `input.value` for `<input>` or `<textarea>` element. Throws for non-input elements.
|
||||
* @param options
|
||||
*/
|
||||
inputValue(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<string>;
|
||||
|
||||
/**
|
||||
* Returns whether the element is checked. Throws if the element is not a checkbox or radio input.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user