Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 70 additions & 21 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
+ [page.mouse](#pagemouse)
+ [page.pdf(options)](#pagepdfoptions)
+ [page.plainText()](#pageplaintext)
+ [page.press(key[, options])](#pagepresskey-options)
+ [page.reload(options)](#pagereloadoptions)
+ [page.screenshot([options])](#pagescreenshotoptions)
+ [page.select(selector, ...values)](#pageselectselector-values)
Expand All @@ -74,7 +73,7 @@
+ [page.title()](#pagetitle)
+ [page.touchscreen](#pagetouchscreen)
+ [page.tracing](#pagetracing)
+ [page.type(text, options)](#pagetypetext-options)
+ [page.type(selector, text[, options])](#pagetypeselector-text-options)
+ [page.url()](#pageurl)
+ [page.viewport()](#pageviewport)
+ [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
Expand All @@ -83,7 +82,9 @@
+ [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
* [class: Keyboard](#class-keyboard)
+ [keyboard.down(key[, options])](#keyboarddownkey-options)
+ [keyboard.press(key[, options])](#keyboardpresskey-options)
+ [keyboard.sendCharacter(char)](#keyboardsendcharacterchar)
+ [keyboard.type(text, options)](#keyboardtypetext-options)
+ [keyboard.up(key)](#keyboardupkey)
* [class: Mouse](#class-mouse)
+ [mouse.click(x, y, [options])](#mouseclickx-y-options)
Expand Down Expand Up @@ -139,12 +140,15 @@
+ [elementHandle.click([options])](#elementhandleclickoptions)
+ [elementHandle.dispose()](#elementhandledispose)
+ [elementHandle.executionContext()](#elementhandleexecutioncontext)
+ [elementHandle.focus()](#elementhandlefocus)
+ [elementHandle.getProperties()](#elementhandlegetproperties)
+ [elementHandle.getProperty(propertyName)](#elementhandlegetpropertypropertyname)
+ [elementHandle.hover()](#elementhandlehover)
+ [elementHandle.jsonValue()](#elementhandlejsonvalue)
+ [elementHandle.press(key[, options])](#elementhandlepresskey-options)
+ [elementHandle.tap()](#elementhandletap)
+ [elementHandle.toString()](#elementhandletostring)
+ [elementHandle.type(text[, options])](#elementhandletypetext-options)
+ [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
* [class: Request](#class-request)
+ [request.abort()](#requestabort)
Expand Down Expand Up @@ -759,15 +763,6 @@ The `format` options are:
#### page.plainText()
- 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)
- `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.
Expand Down Expand Up @@ -896,19 +891,20 @@ Shortcut for [page.mainFrame().title()](#frametitle).
#### page.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.
- `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, use [`page.press`](#pagepresskey-options).
To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#pagekeyboardpresskey-options).

```js
page.type('Hello'); // Types instantly
page.type('World', {delay: 100}); // Types slower, like a user
page.type('#mytextarea', 'Hello'); // Types instantly
page.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
```

#### page.url()
Expand Down Expand Up @@ -1003,21 +999,21 @@ Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitf

### 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.

An example of holding down `Shift` in order to select and delete some text:
```js
page.type('Hello World!');
page.press('ArrowLeft');
page.keyboard.type('Hello World!');
page.keyboard.press('ArrowLeft');

page.keyboard.down('Shift');
for (let i = 0; i < ' World'.length; i++)
page.press('ArrowLeft');
page.keyboard.press('ArrowLeft');
page.keyboard.up('Shift');

page.press('Backspace');
page.keyboard.press('Backspace');
// Result text will end up saying 'Hello!'
```

Expand All @@ -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).

#### 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)
- `char` <[string]> Character to send into the page.
- returns: <[Promise]>
Expand All @@ -1045,6 +1050,21 @@ Dispatches a `keypress` and `input` event. This does not send a `keydown` or `ke
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)
- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [KeyboardEvent.key](https://www.w3.org/TR/uievents-key/)
- returns: <[Promise]>
Expand Down Expand Up @@ -1396,7 +1416,7 @@ const twoHandle = await executionContext.evaluateHandle(() => 2);
const result = await executionContext.evaluate((a, b) => a + b, oneHandle, twoHandle);
await oneHandle.dispose();
await twoHandle.dispose();
console.log(result); // prints '3'.
console.log(result); // prints '3'.
```

#### executionContext.evaluateHandle(pageFunction, ...args)
Expand Down Expand Up @@ -1527,6 +1547,11 @@ The `elementHandle.dispose` method stops referencing the element handle.
#### elementHandle.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()
- returns: <[Promise]<[Map]<[string], [JSHandle]>>>

Expand Down Expand Up @@ -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.

#### 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()
- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.

Expand All @@ -1572,6 +1606,21 @@ If the element is detached from DOM, the method throws an error.
#### elementHandle.toString()
- 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)
- `...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]>
Expand Down
39 changes: 31 additions & 8 deletions lib/ElementHandle.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ class ElementHandle extends JSHandle {
* @param {!ExecutionContext} context
* @param {!Session} client
* @param {!Object} remoteObject
* @param {!Mouse} mouse
* @param {!Touchscreen} touchscreen;
* @param {!Page} page
*/
constructor(context, client, remoteObject, mouse, touchscreen) {
constructor(context, client, remoteObject, page) {
super(context, client, remoteObject);
this._mouse = mouse;
this._touchscreen = touchscreen;
this._client = client;
this._remoteObject = remoteObject;
this._page = page;
this._disposed = false;
}

/**
Expand Down Expand Up @@ -63,15 +64,15 @@ class ElementHandle extends JSHandle {

async hover() {
const {x, y} = await this._visibleCenter();
await this._mouse.move(x, y);
await this._page.mouse.move(x, y);
}

/**
* @param {!Object=} options
*/
async click(options) {
const {x, y} = await this._visibleCenter();
await this._mouse.click(x, y, options);
await this._page.mouse.click(x, y, options);
}

/**
Expand All @@ -86,7 +87,29 @@ class ElementHandle extends JSHandle {

async tap() {
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);
}
}

Expand Down
22 changes: 8 additions & 14 deletions lib/FrameManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ const ElementHandle = require('./ElementHandle');
class FrameManager extends EventEmitter {
/**
* @param {!Session} client
* @param {!Object} frameTree
* @param {!Mouse} mouse
* @param {!Touchscreen} touchscreen
* @param {!Page} page
*/
constructor(client, mouse, touchscreen) {
constructor(client, page) {
super();
this._client = client;
this._mouse = mouse;
this._touchscreen = touchscreen;
this._page = page;
/** @type {!Map<string, !Frame>} */
this._frames = new Map();
/** @type {!Map<string, !ExecutionContext>} */
Expand Down Expand Up @@ -67,7 +64,7 @@ class FrameManager extends EventEmitter {
return;
console.assert(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.emit(FrameManager.Events.FrameAttached, frame);
}
Expand All @@ -94,7 +91,7 @@ class FrameManager extends EventEmitter {
frame._id = framePayload.id;
} else {
// 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._mainFrame = frame;
Expand Down Expand Up @@ -141,7 +138,7 @@ class FrameManager extends EventEmitter {
const context = this._contextIdToContext.get(contextId);
console.assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
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);
}

Expand Down Expand Up @@ -178,15 +175,12 @@ FrameManager.Events = {
class Frame {
/**
* @param {!Session} client
* @param {!Mouse} mouse
* @param {!Touchscreen} touchscreen
* @param {?Frame} parentFrame
* @param {string} frameId
*/
constructor(client, mouse, touchscreen, parentFrame, frameId) {
constructor(client, page, parentFrame, frameId) {
this._client = client;
this._mouse = mouse;
this._touchscreen = touchscreen;
this._page = page;
this._parentFrame = parentFrame;
this._url = '';
this._id = frameId;
Expand Down
26 changes: 26 additions & 0 deletions lib/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,32 @@ class Keyboard {
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 {
Expand Down
Loading