feat(Input): Add keyboard methods to elementHandle (#801)
This patch: - adds input methods to ElementHandle, such as ElementHandle.type and ElementHandle.press - changes `page.type` to accept selector as the first argument - removes `page.press` method. The `page.press` is rarely used and doesn't operate with selectors; if there's a need to press a button, `page.keyboard.press` should be used. BREAKING CHANGE: `page.type` is changed, `page.press` is removed. Fixes #241.
This commit is contained in:
parent
0d0f9b7984
commit
0af0d7dba5
89
docs/api.md
89
docs/api.md
@ -59,7 +59,6 @@
|
|||||||
+ [page.mouse](#pagemouse)
|
+ [page.mouse](#pagemouse)
|
||||||
+ [page.pdf(options)](#pagepdfoptions)
|
+ [page.pdf(options)](#pagepdfoptions)
|
||||||
+ [page.plainText()](#pageplaintext)
|
+ [page.plainText()](#pageplaintext)
|
||||||
+ [page.press(key[, options])](#pagepresskey-options)
|
|
||||||
+ [page.reload(options)](#pagereloadoptions)
|
+ [page.reload(options)](#pagereloadoptions)
|
||||||
+ [page.screenshot([options])](#pagescreenshotoptions)
|
+ [page.screenshot([options])](#pagescreenshotoptions)
|
||||||
+ [page.select(selector, ...values)](#pageselectselector-values)
|
+ [page.select(selector, ...values)](#pageselectselector-values)
|
||||||
@ -74,7 +73,7 @@
|
|||||||
+ [page.title()](#pagetitle)
|
+ [page.title()](#pagetitle)
|
||||||
+ [page.touchscreen](#pagetouchscreen)
|
+ [page.touchscreen](#pagetouchscreen)
|
||||||
+ [page.tracing](#pagetracing)
|
+ [page.tracing](#pagetracing)
|
||||||
+ [page.type(text, options)](#pagetypetext-options)
|
+ [page.type(selector, text[, options])](#pagetypeselector-text-options)
|
||||||
+ [page.url()](#pageurl)
|
+ [page.url()](#pageurl)
|
||||||
+ [page.viewport()](#pageviewport)
|
+ [page.viewport()](#pageviewport)
|
||||||
+ [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
|
+ [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
|
||||||
@ -83,7 +82,9 @@
|
|||||||
+ [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
|
+ [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
|
||||||
* [class: Keyboard](#class-keyboard)
|
* [class: Keyboard](#class-keyboard)
|
||||||
+ [keyboard.down(key[, options])](#keyboarddownkey-options)
|
+ [keyboard.down(key[, options])](#keyboarddownkey-options)
|
||||||
|
+ [keyboard.press(key[, options])](#keyboardpresskey-options)
|
||||||
+ [keyboard.sendCharacter(char)](#keyboardsendcharacterchar)
|
+ [keyboard.sendCharacter(char)](#keyboardsendcharacterchar)
|
||||||
|
+ [keyboard.type(text, options)](#keyboardtypetext-options)
|
||||||
+ [keyboard.up(key)](#keyboardupkey)
|
+ [keyboard.up(key)](#keyboardupkey)
|
||||||
* [class: Mouse](#class-mouse)
|
* [class: Mouse](#class-mouse)
|
||||||
+ [mouse.click(x, y, [options])](#mouseclickx-y-options)
|
+ [mouse.click(x, y, [options])](#mouseclickx-y-options)
|
||||||
@ -139,12 +140,15 @@
|
|||||||
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
||||||
+ [elementHandle.dispose()](#elementhandledispose)
|
+ [elementHandle.dispose()](#elementhandledispose)
|
||||||
+ [elementHandle.executionContext()](#elementhandleexecutioncontext)
|
+ [elementHandle.executionContext()](#elementhandleexecutioncontext)
|
||||||
|
+ [elementHandle.focus()](#elementhandlefocus)
|
||||||
+ [elementHandle.getProperties()](#elementhandlegetproperties)
|
+ [elementHandle.getProperties()](#elementhandlegetproperties)
|
||||||
+ [elementHandle.getProperty(propertyName)](#elementhandlegetpropertypropertyname)
|
+ [elementHandle.getProperty(propertyName)](#elementhandlegetpropertypropertyname)
|
||||||
+ [elementHandle.hover()](#elementhandlehover)
|
+ [elementHandle.hover()](#elementhandlehover)
|
||||||
+ [elementHandle.jsonValue()](#elementhandlejsonvalue)
|
+ [elementHandle.jsonValue()](#elementhandlejsonvalue)
|
||||||
|
+ [elementHandle.press(key[, options])](#elementhandlepresskey-options)
|
||||||
+ [elementHandle.tap()](#elementhandletap)
|
+ [elementHandle.tap()](#elementhandletap)
|
||||||
+ [elementHandle.toString()](#elementhandletostring)
|
+ [elementHandle.toString()](#elementhandletostring)
|
||||||
|
+ [elementHandle.type(text[, options])](#elementhandletypetext-options)
|
||||||
+ [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
|
+ [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
|
||||||
* [class: Request](#class-request)
|
* [class: Request](#class-request)
|
||||||
+ [request.abort()](#requestabort)
|
+ [request.abort()](#requestabort)
|
||||||
@ -759,15 +763,6 @@ The `format` options are:
|
|||||||
#### page.plainText()
|
#### page.plainText()
|
||||||
- returns: <[Promise]<[string]>> Returns page's inner text.
|
- returns: <[Promise]<[string]>> Returns page's inner text.
|
||||||
|
|
||||||
#### page.press(key[, options])
|
|
||||||
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
|
||||||
- `options` <[Object]>
|
|
||||||
- `text` <[string]> If specified, generates an input event with this text.
|
|
||||||
- `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
Shortcut for [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
|
|
||||||
|
|
||||||
#### page.reload(options)
|
#### page.reload(options)
|
||||||
- `options` <[Object]> Navigation parameters which might have the following properties:
|
- `options` <[Object]> Navigation parameters which might have the following properties:
|
||||||
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout.
|
- `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout.
|
||||||
@ -896,7 +891,8 @@ Shortcut for [page.mainFrame().title()](#frametitle).
|
|||||||
#### page.tracing
|
#### page.tracing
|
||||||
- returns: <[Tracing]>
|
- returns: <[Tracing]>
|
||||||
|
|
||||||
#### page.type(text, options)
|
#### page.type(selector, text[, options])
|
||||||
|
- `selector` <[string]> A [selector] of an element to type into. If there are multiple elements satisfying the selector, the first will be used.
|
||||||
- `text` <[string]> A text to type into a focused element.
|
- `text` <[string]> A text to type into a focused element.
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
|
- `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
|
||||||
@ -904,11 +900,11 @@ Shortcut for [page.mainFrame().title()](#frametitle).
|
|||||||
|
|
||||||
Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
|
Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
|
||||||
|
|
||||||
To press a special key, use [`page.press`](#pagepresskey-options).
|
To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#pagekeyboardpresskey-options).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
page.type('Hello'); // Types instantly
|
page.type('#mytextarea', 'Hello'); // Types instantly
|
||||||
page.type('World', {delay: 100}); // Types slower, like a user
|
page.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
|
||||||
```
|
```
|
||||||
|
|
||||||
#### page.url()
|
#### page.url()
|
||||||
@ -1003,21 +999,21 @@ Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitf
|
|||||||
|
|
||||||
### class: Keyboard
|
### class: Keyboard
|
||||||
|
|
||||||
Keyboard provides an api for managing a virtual keyboard. The high level api is [`page.type`](#pagetypetext-options), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
|
Keyboard provides an api for managing a virtual keyboard. The high level api is [`keyboard.type`](#keyboardtypetext-options), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
|
||||||
|
|
||||||
For finer control, you can use [`keyboard.down`](#keyboarddownkey-options), [`keyboard.up`](#keyboardupkey), and [`keyboard.sendCharacter`](#keyboardsendcharacterchar) to manually fire events as if they were generated from a real keyboard.
|
For finer control, you can use [`keyboard.down`](#keyboarddownkey-options), [`keyboard.up`](#keyboardupkey), and [`keyboard.sendCharacter`](#keyboardsendcharacterchar) to manually fire events as if they were generated from a real keyboard.
|
||||||
|
|
||||||
An example of holding down `Shift` in order to select and delete some text:
|
An example of holding down `Shift` in order to select and delete some text:
|
||||||
```js
|
```js
|
||||||
page.type('Hello World!');
|
page.keyboard.type('Hello World!');
|
||||||
page.press('ArrowLeft');
|
page.keyboard.press('ArrowLeft');
|
||||||
|
|
||||||
page.keyboard.down('Shift');
|
page.keyboard.down('Shift');
|
||||||
for (let i = 0; i < ' World'.length; i++)
|
for (let i = 0; i < ' World'.length; i++)
|
||||||
page.press('ArrowLeft');
|
page.keyboard.press('ArrowLeft');
|
||||||
page.keyboard.up('Shift');
|
page.keyboard.up('Shift');
|
||||||
|
|
||||||
page.press('Backspace');
|
page.keyboard.press('Backspace');
|
||||||
// Result text will end up saying 'Hello!'
|
// Result text will end up saying 'Hello!'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1035,6 +1031,15 @@ If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key
|
|||||||
|
|
||||||
After the key is pressed once, subsequent calls to [`keyboard.down`](#keyboarddownkey-options) will have [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use [`keyboard.up`](#keyboardupkey).
|
After the key is pressed once, subsequent calls to [`keyboard.down`](#keyboarddownkey-options) will have [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use [`keyboard.up`](#keyboardupkey).
|
||||||
|
|
||||||
|
#### keyboard.press(key[, options])
|
||||||
|
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `text` <[string]> If specified, generates an input event with this text.
|
||||||
|
- `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Shortcut for [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
|
||||||
|
|
||||||
#### keyboard.sendCharacter(char)
|
#### keyboard.sendCharacter(char)
|
||||||
- `char` <[string]> Character to send into the page.
|
- `char` <[string]> Character to send into the page.
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
@ -1045,6 +1050,21 @@ Dispatches a `keypress` and `input` event. This does not send a `keydown` or `ke
|
|||||||
page.keyboard.sendCharacter('嗨');
|
page.keyboard.sendCharacter('嗨');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### keyboard.type(text, options)
|
||||||
|
- `text` <[string]> A text to type into a focused element.
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
|
||||||
|
|
||||||
|
To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#keyboardpresskey-options).
|
||||||
|
|
||||||
|
```js
|
||||||
|
page.keyboard.type('Hello'); // Types instantly
|
||||||
|
page.keyboard.type('World', {delay: 100}); // Types slower, like a user
|
||||||
|
```
|
||||||
|
|
||||||
#### keyboard.up(key)
|
#### keyboard.up(key)
|
||||||
- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
@ -1527,6 +1547,11 @@ The `elementHandle.dispose` method stops referencing the element handle.
|
|||||||
#### elementHandle.executionContext()
|
#### elementHandle.executionContext()
|
||||||
- returns: [ExecutionContext]
|
- returns: [ExecutionContext]
|
||||||
|
|
||||||
|
#### elementHandle.focus()
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
|
||||||
|
|
||||||
#### elementHandle.getProperties()
|
#### elementHandle.getProperties()
|
||||||
- returns: <[Promise]<[Map]<[string], [JSHandle]>>>
|
- returns: <[Promise]<[Map]<[string], [JSHandle]>>>
|
||||||
|
|
||||||
@ -1563,6 +1588,15 @@ Returns a JSON representation of the object. The JSON is generated by running [`
|
|||||||
|
|
||||||
> **NOTE** The method will throw if the referenced object is not stringifiable.
|
> **NOTE** The method will throw if the referenced object is not stringifiable.
|
||||||
|
|
||||||
|
#### elementHandle.press(key[, options])
|
||||||
|
- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `text` <[string]> If specified, generates an input event with this text.
|
||||||
|
- `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Focuses the element, and then uses [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
|
||||||
|
|
||||||
#### elementHandle.tap()
|
#### elementHandle.tap()
|
||||||
- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.
|
- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.
|
||||||
|
|
||||||
@ -1572,6 +1606,21 @@ If the element is detached from DOM, the method throws an error.
|
|||||||
#### elementHandle.toString()
|
#### elementHandle.toString()
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
||||||
|
#### elementHandle.type(text[, options])
|
||||||
|
- `text` <[string]> A text to type into a focused element.
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
|
||||||
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
|
||||||
|
|
||||||
|
To press a special key, like `Control` or `ArrowDown`, use [`elementHandle.press`](#elementhandlepresskey-options).
|
||||||
|
|
||||||
|
```js
|
||||||
|
elementHandle.type('Hello'); // Types instantly
|
||||||
|
elementHandle.type('World', {delay: 100}); // Types slower, like a user
|
||||||
|
```
|
||||||
|
|
||||||
#### elementHandle.uploadFile(...filePaths)
|
#### elementHandle.uploadFile(...filePaths)
|
||||||
- `...filePaths` <...[string]> Sets the value of the file input these paths. If some of the `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
|
- `...filePaths` <...[string]> Sets the value of the file input these paths. If some of the `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
@ -22,13 +22,14 @@ class ElementHandle extends JSHandle {
|
|||||||
* @param {!ExecutionContext} context
|
* @param {!ExecutionContext} context
|
||||||
* @param {!Session} client
|
* @param {!Session} client
|
||||||
* @param {!Object} remoteObject
|
* @param {!Object} remoteObject
|
||||||
* @param {!Mouse} mouse
|
* @param {!Page} page
|
||||||
* @param {!Touchscreen} touchscreen;
|
|
||||||
*/
|
*/
|
||||||
constructor(context, client, remoteObject, mouse, touchscreen) {
|
constructor(context, client, remoteObject, page) {
|
||||||
super(context, client, remoteObject);
|
super(context, client, remoteObject);
|
||||||
this._mouse = mouse;
|
this._client = client;
|
||||||
this._touchscreen = touchscreen;
|
this._remoteObject = remoteObject;
|
||||||
|
this._page = page;
|
||||||
|
this._disposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +64,7 @@ class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async hover() {
|
async hover() {
|
||||||
const {x, y} = await this._visibleCenter();
|
const {x, y} = await this._visibleCenter();
|
||||||
await this._mouse.move(x, y);
|
await this._page.mouse.move(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,7 +72,7 @@ class ElementHandle extends JSHandle {
|
|||||||
*/
|
*/
|
||||||
async click(options) {
|
async click(options) {
|
||||||
const {x, y} = await this._visibleCenter();
|
const {x, y} = await this._visibleCenter();
|
||||||
await this._mouse.click(x, y, options);
|
await this._page.mouse.click(x, y, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +87,29 @@ class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async tap() {
|
async tap() {
|
||||||
const {x, y} = await this._visibleCenter();
|
const {x, y} = await this._visibleCenter();
|
||||||
await this._touchscreen.tap(x, y);
|
await this._page.touchscreen.tap(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
async focus() {
|
||||||
|
await this.executionContext().evaluate(element => element.focus(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {{delay: (number|undefined)}=} options
|
||||||
|
*/
|
||||||
|
async type(text, options) {
|
||||||
|
await this.focus();
|
||||||
|
await this._page.keyboard.type(text, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {!Object=} options
|
||||||
|
*/
|
||||||
|
async press(key, options) {
|
||||||
|
await this.focus();
|
||||||
|
await this._page.keyboard.press(key, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +23,12 @@ const ElementHandle = require('./ElementHandle');
|
|||||||
class FrameManager extends EventEmitter {
|
class FrameManager extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {!Session} client
|
* @param {!Session} client
|
||||||
* @param {!Object} frameTree
|
* @param {!Page} page
|
||||||
* @param {!Mouse} mouse
|
|
||||||
* @param {!Touchscreen} touchscreen
|
|
||||||
*/
|
*/
|
||||||
constructor(client, mouse, touchscreen) {
|
constructor(client, page) {
|
||||||
super();
|
super();
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._mouse = mouse;
|
this._page = page;
|
||||||
this._touchscreen = touchscreen;
|
|
||||||
/** @type {!Map<string, !Frame>} */
|
/** @type {!Map<string, !Frame>} */
|
||||||
this._frames = new Map();
|
this._frames = new Map();
|
||||||
/** @type {!Map<string, !ExecutionContext>} */
|
/** @type {!Map<string, !ExecutionContext>} */
|
||||||
@ -67,7 +64,7 @@ class FrameManager extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
console.assert(parentFrameId);
|
console.assert(parentFrameId);
|
||||||
const parentFrame = this._frames.get(parentFrameId);
|
const parentFrame = this._frames.get(parentFrameId);
|
||||||
const frame = new Frame(this._client, this._mouse, this._touchscreen, parentFrame, frameId);
|
const frame = new Frame(this._client, this._page, parentFrame, frameId);
|
||||||
this._frames.set(frame._id, frame);
|
this._frames.set(frame._id, frame);
|
||||||
this.emit(FrameManager.Events.FrameAttached, frame);
|
this.emit(FrameManager.Events.FrameAttached, frame);
|
||||||
}
|
}
|
||||||
@ -94,7 +91,7 @@ class FrameManager extends EventEmitter {
|
|||||||
frame._id = framePayload.id;
|
frame._id = framePayload.id;
|
||||||
} else {
|
} else {
|
||||||
// Initial main frame navigation.
|
// Initial main frame navigation.
|
||||||
frame = new Frame(this._client, this._mouse, this._touchscreen, null, framePayload.id);
|
frame = new Frame(this._client, this._page, null, framePayload.id);
|
||||||
}
|
}
|
||||||
this._frames.set(framePayload.id, frame);
|
this._frames.set(framePayload.id, frame);
|
||||||
this._mainFrame = frame;
|
this._mainFrame = frame;
|
||||||
@ -141,7 +138,7 @@ class FrameManager extends EventEmitter {
|
|||||||
const context = this._contextIdToContext.get(contextId);
|
const context = this._contextIdToContext.get(contextId);
|
||||||
console.assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
console.assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
||||||
if (remoteObject.subtype === 'node')
|
if (remoteObject.subtype === 'node')
|
||||||
return new ElementHandle(context, this._client, remoteObject, this._mouse, this._touchscreen);
|
return new ElementHandle(context, this._client, remoteObject, this._page);
|
||||||
return new JSHandle(context, this._client, remoteObject);
|
return new JSHandle(context, this._client, remoteObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,15 +175,12 @@ FrameManager.Events = {
|
|||||||
class Frame {
|
class Frame {
|
||||||
/**
|
/**
|
||||||
* @param {!Session} client
|
* @param {!Session} client
|
||||||
* @param {!Mouse} mouse
|
|
||||||
* @param {!Touchscreen} touchscreen
|
|
||||||
* @param {?Frame} parentFrame
|
* @param {?Frame} parentFrame
|
||||||
* @param {string} frameId
|
* @param {string} frameId
|
||||||
*/
|
*/
|
||||||
constructor(client, mouse, touchscreen, parentFrame, frameId) {
|
constructor(client, page, parentFrame, frameId) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._mouse = mouse;
|
this._page = page;
|
||||||
this._touchscreen = touchscreen;
|
|
||||||
this._parentFrame = parentFrame;
|
this._parentFrame = parentFrame;
|
||||||
this._url = '';
|
this._url = '';
|
||||||
this._id = frameId;
|
this._id = frameId;
|
||||||
|
26
lib/Input.js
26
lib/Input.js
@ -88,6 +88,32 @@ class Keyboard {
|
|||||||
unmodifiedText: char
|
unmodifiedText: char
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {{delay: (number|undefined)}=} options
|
||||||
|
*/
|
||||||
|
async type(text, options) {
|
||||||
|
let delay = 0;
|
||||||
|
if (options && options.delay)
|
||||||
|
delay = options.delay;
|
||||||
|
for (const char of text) {
|
||||||
|
await this.press(char, {text: char, delay});
|
||||||
|
if (delay)
|
||||||
|
await new Promise(f => setTimeout(f, delay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @param {!Object=} options
|
||||||
|
*/
|
||||||
|
async press(key, options) {
|
||||||
|
await this.down(key, options);
|
||||||
|
if (options && options.delay)
|
||||||
|
await new Promise(f => setTimeout(f, options.delay));
|
||||||
|
await this.up(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Mouse {
|
class Mouse {
|
||||||
|
31
lib/Page.js
31
lib/Page.js
@ -63,7 +63,7 @@ class Page extends EventEmitter {
|
|||||||
this._keyboard = new Keyboard(client);
|
this._keyboard = new Keyboard(client);
|
||||||
this._mouse = new Mouse(client, this._keyboard);
|
this._mouse = new Mouse(client, this._keyboard);
|
||||||
this._touchscreen = new Touchscreen(client, this._keyboard);
|
this._touchscreen = new Touchscreen(client, this._keyboard);
|
||||||
this._frameManager = new FrameManager(client, this._mouse, this._touchscreen);
|
this._frameManager = new FrameManager(client, this);
|
||||||
this._networkManager = new NetworkManager(client);
|
this._networkManager = new NetworkManager(client);
|
||||||
this._emulationManager = new EmulationManager(client);
|
this._emulationManager = new EmulationManager(client);
|
||||||
this._tracing = new Tracing(client);
|
this._tracing = new Tracing(client);
|
||||||
@ -711,7 +711,7 @@ class Page extends EventEmitter {
|
|||||||
async focus(selector) {
|
async focus(selector) {
|
||||||
const handle = await this.$(selector);
|
const handle = await this.$(selector);
|
||||||
console.assert(handle, 'No node found for selector: ' + selector);
|
console.assert(handle, 'No node found for selector: ' + selector);
|
||||||
await this.evaluate(element => element.focus(), handle);
|
await handle.focus();
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,31 +739,14 @@ class Page extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {string} selector
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @param {{delay: (number|undefined)}=} options
|
* @param {{delay: (number|undefined)}=} options
|
||||||
*/
|
*/
|
||||||
async type(text, options) {
|
async type(selector, text, options) {
|
||||||
let delay = 0;
|
const handle = await this.$(selector);
|
||||||
if (options && options.delay)
|
await handle.type(text, options);
|
||||||
delay = options.delay;
|
await handle.dispose();
|
||||||
let last;
|
|
||||||
for (const char of text) {
|
|
||||||
last = this.press(char, {text: char, delay});
|
|
||||||
if (delay)
|
|
||||||
await new Promise(f => setTimeout(f, delay));
|
|
||||||
}
|
|
||||||
await last;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} text
|
|
||||||
* @param {!Object=} options
|
|
||||||
*/
|
|
||||||
async press(key, options) {
|
|
||||||
this._keyboard.down(key, options);
|
|
||||||
if (options && options.delay)
|
|
||||||
await new Promise(f => setTimeout(f, options.delay));
|
|
||||||
await this._keyboard.up(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
38
test/test.js
38
test/test.js
@ -1426,8 +1426,9 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
it('should type into the textarea', SX(async function() {
|
it('should type into the textarea', SX(async function() {
|
||||||
await page.goto(PREFIX + '/input/textarea.html');
|
await page.goto(PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
|
||||||
await page.type('Type in this text!');
|
const textarea = await page.$('textarea');
|
||||||
|
await textarea.type('Type in this text!');
|
||||||
expect(await page.evaluate(() => result)).toBe('Type in this text!');
|
expect(await page.evaluate(() => result)).toBe('Type in this text!');
|
||||||
}));
|
}));
|
||||||
it('should click the button after navigation ', SX(async function() {
|
it('should click the button after navigation ', SX(async function() {
|
||||||
@ -1452,29 +1453,28 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
it('should move with the arrow keys', SX(async function(){
|
it('should move with the arrow keys', SX(async function(){
|
||||||
await page.goto(PREFIX + '/input/textarea.html');
|
await page.goto(PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
await page.type('textarea', 'Hello World!');
|
||||||
await page.type('Hello World!');
|
|
||||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||||
for (let i = 0; i < 'World!'.length; i++)
|
for (let i = 0; i < 'World!'.length; i++)
|
||||||
page.press('ArrowLeft');
|
page.keyboard.press('ArrowLeft');
|
||||||
await page.type('inserted ');
|
await page.keyboard.type('inserted ');
|
||||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
|
||||||
page.keyboard.down('Shift');
|
page.keyboard.down('Shift');
|
||||||
for (let i = 0; i < 'inserted '.length; i++)
|
for (let i = 0; i < 'inserted '.length; i++)
|
||||||
page.press('ArrowLeft');
|
page.keyboard.press('ArrowLeft');
|
||||||
page.keyboard.up('Shift');
|
page.keyboard.up('Shift');
|
||||||
await page.press('Backspace');
|
await page.keyboard.press('Backspace');
|
||||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
|
||||||
}));
|
}));
|
||||||
it('should send a character with Page.press', SX(async function() {
|
it('should send a character with ElementHandle.press', SX(async function() {
|
||||||
await page.goto(PREFIX + '/input/textarea.html');
|
await page.goto(PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
const textarea = await page.$('textarea');
|
||||||
await page.press('a', {text: 'f'});
|
await textarea.press('a', {text: 'f'});
|
||||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
|
||||||
|
|
||||||
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
|
||||||
|
|
||||||
await page.press('a', {text: 'y'});
|
await textarea.press('a', {text: 'y'});
|
||||||
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
|
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
|
||||||
}));
|
}));
|
||||||
it('should send a character with sendCharacter', SX(async function() {
|
it('should send a character with sendCharacter', SX(async function() {
|
||||||
@ -1519,12 +1519,12 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
it('should send proper codes while typing', SX(async function(){
|
it('should send proper codes while typing', SX(async function(){
|
||||||
await page.goto(PREFIX + '/input/keyboard.html');
|
await page.goto(PREFIX + '/input/keyboard.html');
|
||||||
await page.type('!');
|
await page.keyboard.type('!');
|
||||||
expect(await page.evaluate(() => getResult())).toBe(
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
[ 'Keydown: ! 49 []',
|
[ 'Keydown: ! 49 []',
|
||||||
'Keypress: ! 33 33 33 []',
|
'Keypress: ! 33 33 33 []',
|
||||||
'Keyup: ! 49 []'].join('\n'));
|
'Keyup: ! 49 []'].join('\n'));
|
||||||
await page.type('^');
|
await page.keyboard.type('^');
|
||||||
expect(await page.evaluate(() => getResult())).toBe(
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
[ 'Keydown: ^ 54 []',
|
[ 'Keydown: ^ 54 []',
|
||||||
'Keypress: ^ 94 94 94 []',
|
'Keypress: ^ 94 94 94 []',
|
||||||
@ -1534,7 +1534,7 @@ describe('Page', function() {
|
|||||||
await page.goto(PREFIX + '/input/keyboard.html');
|
await page.goto(PREFIX + '/input/keyboard.html');
|
||||||
const keyboard = page.keyboard;
|
const keyboard = page.keyboard;
|
||||||
await keyboard.down('Shift');
|
await keyboard.down('Shift');
|
||||||
await page.type('~');
|
await page.keyboard.type('~');
|
||||||
expect(await page.evaluate(() => getResult())).toBe(
|
expect(await page.evaluate(() => getResult())).toBe(
|
||||||
[ 'Keydown: Shift 16 [Shift]',
|
[ 'Keydown: Shift 16 [Shift]',
|
||||||
'Keydown: ~ 192 [Shift]', // 192 is ` keyCode
|
'Keydown: ~ 192 [Shift]', // 192 is ` keyCode
|
||||||
@ -1555,7 +1555,7 @@ describe('Page', function() {
|
|||||||
Promise.resolve().then(() => event.preventDefault());
|
Promise.resolve().then(() => event.preventDefault());
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
await page.type('Hello World!');
|
await page.keyboard.type('Hello World!');
|
||||||
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
|
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
|
||||||
}));
|
}));
|
||||||
it('keyboard.modifiers()', SX(async function(){
|
it('keyboard.modifiers()', SX(async function(){
|
||||||
@ -1603,7 +1603,7 @@ describe('Page', function() {
|
|||||||
await page.goto(PREFIX + '/input/textarea.html');
|
await page.goto(PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
await page.focus('textarea');
|
||||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||||
await page.type(text);
|
await page.keyboard.type(text);
|
||||||
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
|
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
|
||||||
const {x, y} = await page.evaluate(dimensions);
|
const {x, y} = await page.evaluate(dimensions);
|
||||||
await page.mouse.move(x + 2,y + 2);
|
await page.mouse.move(x + 2,y + 2);
|
||||||
@ -1616,7 +1616,7 @@ describe('Page', function() {
|
|||||||
await page.goto(PREFIX + '/input/textarea.html');
|
await page.goto(PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
await page.focus('textarea');
|
||||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||||
await page.type(text);
|
await page.keyboard.type(text);
|
||||||
await page.click('textarea');
|
await page.click('textarea');
|
||||||
await page.click('textarea', {clickCount: 2});
|
await page.click('textarea', {clickCount: 2});
|
||||||
await page.click('textarea', {clickCount: 3});
|
await page.click('textarea', {clickCount: 3});
|
||||||
@ -1659,7 +1659,7 @@ describe('Page', function() {
|
|||||||
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
|
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
|
||||||
await page.keyboard.down('a', {text: 'a'});
|
await page.keyboard.down('a', {text: 'a'});
|
||||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
|
||||||
await page.press('a');
|
await page.keyboard.press('a');
|
||||||
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
|
||||||
}));
|
}));
|
||||||
// @see https://github.com/GoogleChrome/puppeteer/issues/206
|
// @see https://github.com/GoogleChrome/puppeteer/issues/206
|
||||||
|
Loading…
Reference in New Issue
Block a user