mirror of
https://github.com/microsoft/playwright.git
synced 2024-09-21 01:17:43 +03:00
feat(firefox): implemented *.fill (#63)
This commit is contained in:
parent
c4c8d498bd
commit
3190044c00
@ -17,7 +17,7 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import { assert, debugError, helper } from '../helper';
|
||||
import { ClickOptions, Modifier, MultiClickOptions, PointerActionOptions, SelectOption, selectFunction } from '../input';
|
||||
import { ClickOptions, Modifier, MultiClickOptions, PointerActionOptions, SelectOption, selectFunction, fillFunction } from '../input';
|
||||
import { CDPSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { Frame } from './Frame';
|
||||
@ -321,34 +321,7 @@ export class ElementHandle extends JSHandle {
|
||||
|
||||
async fill(value: string): Promise<void> {
|
||||
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
|
||||
const error = await this.evaluate((element: HTMLElement) => {
|
||||
if (element.nodeType !== Node.ELEMENT_NODE)
|
||||
return 'Node is not of type HTMLElement';
|
||||
if (element.nodeName.toLowerCase() === 'input') {
|
||||
const input = element as HTMLInputElement;
|
||||
const type = input.getAttribute('type') || '';
|
||||
const kTextInputTypes = new Set(['', 'password', 'search', 'tel', 'text', 'url']);
|
||||
if (!kTextInputTypes.has(type.toLowerCase()))
|
||||
return 'Cannot fill input of type "' + type + '".';
|
||||
input.selectionStart = 0;
|
||||
input.selectionEnd = input.value.length;
|
||||
} else if (element.nodeName.toLowerCase() === 'textarea') {
|
||||
const textarea = element as HTMLTextAreaElement;
|
||||
textarea.selectionStart = 0;
|
||||
textarea.selectionEnd = textarea.value.length;
|
||||
} else if (element.isContentEditable) {
|
||||
if (!element.ownerDocument || !element.ownerDocument.defaultView)
|
||||
return 'Element does not belong to a window';
|
||||
const range = element.ownerDocument.createRange();
|
||||
range.selectNodeContents(element);
|
||||
const selection = element.ownerDocument.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const error = await this.evaluate(fillFunction);
|
||||
if (error)
|
||||
throw new Error(error);
|
||||
await this.focus();
|
||||
|
@ -281,6 +281,13 @@ export class Frame {
|
||||
return result;
|
||||
}
|
||||
|
||||
async fill(selector: string, value: string) {
|
||||
const handle = await this.$(selector);
|
||||
assert(handle, 'No node found for selector: ' + selector);
|
||||
await handle.fill(value);
|
||||
await handle.dispose();
|
||||
}
|
||||
|
||||
async type(selector: string, text: string, options: { delay: (number | undefined); } | undefined) {
|
||||
const handle = await this.$(selector);
|
||||
assert(handle, 'No node found for selector: ' + selector);
|
||||
|
@ -15,15 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {assert, debugError, helper} from '../helper';
|
||||
import * as path from 'path';
|
||||
import {ExecutionContext} from './ExecutionContext';
|
||||
import {Frame} from './FrameManager';
|
||||
import { assert, debugError, helper } from '../helper';
|
||||
import { ClickOptions, fillFunction, MultiClickOptions, selectFunction, SelectOption } from '../input';
|
||||
import { JugglerSession } from './Connection';
|
||||
import { MultiClickOptions, ClickOptions, selectFunction, SelectOption } from '../input';
|
||||
import Injected from '../injected/injected';
|
||||
|
||||
type SelectorRoot = Element | ShadowRoot | Document;
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { Frame } from './FrameManager';
|
||||
|
||||
export class JSHandle {
|
||||
_context: ExecutionContext;
|
||||
@ -361,6 +361,15 @@ export class ElementHandle extends JSHandle {
|
||||
return this.evaluate(selectFunction, ...options);
|
||||
}
|
||||
|
||||
async fill(value: string): Promise<void> {
|
||||
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
|
||||
const error = await this.evaluate(fillFunction);
|
||||
if (error)
|
||||
throw new Error(error);
|
||||
await this.focus();
|
||||
await this._frame._page.keyboard.sendCharacter(value);
|
||||
}
|
||||
|
||||
async _clickablePoint(): Promise<{ x: number; y: number; }> {
|
||||
const result = await this._session.send('Page.getContentQuads', {
|
||||
frameId: this._frameId,
|
||||
|
@ -460,88 +460,92 @@ export class Page extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async evaluate(pageFunction, ...args) {
|
||||
return await this.mainFrame().evaluate(pageFunction, ...args);
|
||||
evaluate(pageFunction, ...args) {
|
||||
return this.mainFrame().evaluate(pageFunction, ...args);
|
||||
}
|
||||
|
||||
async addScriptTag(options: { content?: string; path?: string; type?: string; url?: string; }): Promise<ElementHandle> {
|
||||
return await this.mainFrame().addScriptTag(options);
|
||||
addScriptTag(options: { content?: string; path?: string; type?: string; url?: string; }): Promise<ElementHandle> {
|
||||
return this.mainFrame().addScriptTag(options);
|
||||
}
|
||||
|
||||
async addStyleTag(options: { content?: string; path?: string; url?: string; }): Promise<ElementHandle> {
|
||||
return await this.mainFrame().addStyleTag(options);
|
||||
addStyleTag(options: { content?: string; path?: string; url?: string; }): Promise<ElementHandle> {
|
||||
return this.mainFrame().addStyleTag(options);
|
||||
}
|
||||
|
||||
async click(selector: string, options?: ClickOptions) {
|
||||
return await this.mainFrame().click(selector, options);
|
||||
click(selector: string, options?: ClickOptions) {
|
||||
return this.mainFrame().click(selector, options);
|
||||
}
|
||||
|
||||
async dblclick(selector: string, options?: MultiClickOptions) {
|
||||
dblclick(selector: string, options?: MultiClickOptions) {
|
||||
return this.mainFrame().dblclick(selector, options);
|
||||
}
|
||||
|
||||
async tripleclick(selector: string, options?: MultiClickOptions) {
|
||||
tripleclick(selector: string, options?: MultiClickOptions) {
|
||||
return this.mainFrame().tripleclick(selector, options);
|
||||
}
|
||||
|
||||
async type(selector: string, text: string, options: { delay: (number | undefined); } | undefined) {
|
||||
return await this._frameManager.mainFrame().type(selector, text, options);
|
||||
fill(selector: string, value: string) {
|
||||
return this.mainFrame().fill(selector, value);
|
||||
}
|
||||
|
||||
async focus(selector: string) {
|
||||
return await this._frameManager.mainFrame().focus(selector);
|
||||
select(selector: string, ...values: Array<string>): Promise<Array<string>> {
|
||||
return this._frameManager.mainFrame().select(selector, ...values);
|
||||
}
|
||||
|
||||
async hover(selector: string) {
|
||||
return await this._frameManager.mainFrame().hover(selector);
|
||||
type(selector: string, text: string, options: { delay: (number | undefined); } | undefined) {
|
||||
return this._frameManager.mainFrame().type(selector, text, options);
|
||||
}
|
||||
|
||||
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: { polling?: string | number; timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}, ...args: Array<any>): Promise<JSHandle> {
|
||||
return await this._frameManager.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
|
||||
focus(selector: string) {
|
||||
return this._frameManager.mainFrame().focus(selector);
|
||||
}
|
||||
|
||||
async waitForFunction(pageFunction: Function | string, options: { polling?: string | number; timeout?: number; } | undefined = {}, ...args): Promise<JSHandle> {
|
||||
return await this._frameManager.mainFrame().waitForFunction(pageFunction, options, ...args);
|
||||
hover(selector: string) {
|
||||
return this._frameManager.mainFrame().hover(selector);
|
||||
}
|
||||
|
||||
async waitForSelector(selector: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}): Promise<ElementHandle> {
|
||||
return await this._frameManager.mainFrame().waitForSelector(selector, options);
|
||||
waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options: { polling?: string | number; timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}, ...args: Array<any>): Promise<JSHandle> {
|
||||
return this._frameManager.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
|
||||
}
|
||||
|
||||
async waitForXPath(xpath: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}): Promise<ElementHandle> {
|
||||
return await this._frameManager.mainFrame().waitForXPath(xpath, options);
|
||||
waitForFunction(pageFunction: Function | string, options: { polling?: string | number; timeout?: number; } | undefined = {}, ...args): Promise<JSHandle> {
|
||||
return this._frameManager.mainFrame().waitForFunction(pageFunction, options, ...args);
|
||||
}
|
||||
|
||||
async title(): Promise<string> {
|
||||
return await this._frameManager.mainFrame().title();
|
||||
waitForSelector(selector: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}): Promise<ElementHandle> {
|
||||
return this._frameManager.mainFrame().waitForSelector(selector, options);
|
||||
}
|
||||
|
||||
async $(selector: string): Promise<ElementHandle | null> {
|
||||
return await this._frameManager.mainFrame().$(selector);
|
||||
waitForXPath(xpath: string, options: { timeout?: number; visible?: boolean; hidden?: boolean; } | undefined = {}): Promise<ElementHandle> {
|
||||
return this._frameManager.mainFrame().waitForXPath(xpath, options);
|
||||
}
|
||||
|
||||
async $$(selector: string): Promise<Array<ElementHandle>> {
|
||||
return await this._frameManager.mainFrame().$$(selector);
|
||||
title(): Promise<string> {
|
||||
return this._frameManager.mainFrame().title();
|
||||
}
|
||||
|
||||
async $eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
||||
return await this._frameManager.mainFrame().$eval(selector, pageFunction, ...args);
|
||||
$(selector: string): Promise<ElementHandle | null> {
|
||||
return this._frameManager.mainFrame().$(selector);
|
||||
}
|
||||
|
||||
async $$eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
||||
return await this._frameManager.mainFrame().$$eval(selector, pageFunction, ...args);
|
||||
$$(selector: string): Promise<Array<ElementHandle>> {
|
||||
return this._frameManager.mainFrame().$$(selector);
|
||||
}
|
||||
|
||||
async $x(expression: string): Promise<Array<ElementHandle>> {
|
||||
return await this._frameManager.mainFrame().$x(expression);
|
||||
$eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
||||
return this._frameManager.mainFrame().$eval(selector, pageFunction, ...args);
|
||||
}
|
||||
|
||||
async evaluateHandle(pageFunction, ...args) {
|
||||
return await this._frameManager.mainFrame().evaluateHandle(pageFunction, ...args);
|
||||
$$eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
||||
return this._frameManager.mainFrame().$$eval(selector, pageFunction, ...args);
|
||||
}
|
||||
|
||||
async select(selector: string, ...values: Array<string>): Promise<Array<string>> {
|
||||
return await this._frameManager.mainFrame().select(selector, ...values);
|
||||
$x(expression: string): Promise<Array<ElementHandle>> {
|
||||
return this._frameManager.mainFrame().$x(expression);
|
||||
}
|
||||
|
||||
evaluateHandle(pageFunction, ...args) {
|
||||
return this._frameManager.mainFrame().evaluateHandle(pageFunction, ...args);
|
||||
}
|
||||
|
||||
async close(options: any = {}) {
|
||||
|
29
src/input.ts
29
src/input.ts
@ -140,3 +140,32 @@ export const selectFunction = (element: HTMLSelectElement, ...optionsToSelect: (
|
||||
element.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||
return options.filter(option => option.selected).map(option => option.value);
|
||||
};
|
||||
|
||||
export const fillFunction = (element: HTMLElement) => {
|
||||
if (element.nodeType !== Node.ELEMENT_NODE)
|
||||
return 'Node is not of type HTMLElement';
|
||||
if (element.nodeName.toLowerCase() === 'input') {
|
||||
const input = element as HTMLInputElement;
|
||||
const type = input.getAttribute('type') || '';
|
||||
const kTextInputTypes = new Set(['', 'password', 'search', 'tel', 'text', 'url']);
|
||||
if (!kTextInputTypes.has(type.toLowerCase()))
|
||||
return 'Cannot fill input of type "' + type + '".';
|
||||
input.selectionStart = 0;
|
||||
input.selectionEnd = input.value.length;
|
||||
} else if (element.nodeName.toLowerCase() === 'textarea') {
|
||||
const textarea = element as HTMLTextAreaElement;
|
||||
textarea.selectionStart = 0;
|
||||
textarea.selectionEnd = textarea.value.length;
|
||||
} else if (element.isContentEditable) {
|
||||
if (!element.ownerDocument || !element.ownerDocument.defaultView)
|
||||
return 'Element does not belong to a window';
|
||||
const range = element.ownerDocument.createRange();
|
||||
range.selectNodeContents(element);
|
||||
const selection = element.ownerDocument.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import { assert, debugError, helper } from '../helper';
|
||||
import { ClickOptions, MultiClickOptions, selectFunction, SelectOption } from '../input';
|
||||
import { ClickOptions, MultiClickOptions, selectFunction, SelectOption, fillFunction } from '../input';
|
||||
import { TargetSession } from './Connection';
|
||||
import { ExecutionContext } from './ExecutionContext';
|
||||
import { FrameManager } from './FrameManager';
|
||||
@ -250,34 +250,7 @@ export class ElementHandle extends JSHandle {
|
||||
|
||||
async fill(value: string): Promise<void> {
|
||||
assert(helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
|
||||
const error = await this.evaluate((element: HTMLElement) => {
|
||||
if (element.nodeType !== Node.ELEMENT_NODE)
|
||||
return 'Node is not of type HTMLElement';
|
||||
if (element.nodeName.toLowerCase() === 'input') {
|
||||
const input = element as HTMLInputElement;
|
||||
const type = input.getAttribute('type') || '';
|
||||
const kTextInputTypes = new Set(['', 'password', 'search', 'tel', 'text', 'url']);
|
||||
if (!kTextInputTypes.has(type.toLowerCase()))
|
||||
return 'Cannot fill input of type "' + type + '".';
|
||||
input.selectionStart = 0;
|
||||
input.selectionEnd = input.value.length;
|
||||
} else if (element.nodeName.toLowerCase() === 'textarea') {
|
||||
const textarea = element as HTMLTextAreaElement;
|
||||
textarea.selectionStart = 0;
|
||||
textarea.selectionEnd = textarea.value.length;
|
||||
} else if (element.isContentEditable) {
|
||||
if (!element.ownerDocument || !element.ownerDocument.defaultView)
|
||||
return 'Element does not belong to a window';
|
||||
const range = element.ownerDocument.createRange();
|
||||
range.selectNodeContents(element);
|
||||
const selection = element.ownerDocument.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} else {
|
||||
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const error = await this.evaluate(fillFunction);
|
||||
if (error)
|
||||
throw new Error(error);
|
||||
await this.focus();
|
||||
|
@ -1046,7 +1046,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX)('Page.fill', function() {
|
||||
describe('Page.fill', function() {
|
||||
it('should fill textarea', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||
await page.fill('textarea', 'some value');
|
||||
|
Loading…
Reference in New Issue
Block a user