From 64968862db568a8f57d4c243ea5262388a184b67 Mon Sep 17 00:00:00 2001 From: JoelEinbinder Date: Tue, 25 Jul 2017 16:05:23 -0700 Subject: [PATCH] Implement mouse.click(x,y) (#131) Closes #107 --- docs/api.md | 20 +++++++++++--------- lib/FrameManager.js | 21 ++++++++++++++------- lib/Input.js | 11 ++++++++++- phantom_shim/WebPage.js | 12 +++++++----- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/api.md b/docs/api.md index 3817fdc0..2511a83a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -66,9 +66,9 @@ + [keyboard.sendCharacter(char)](#keyboardsendcharacterchar) + [keyboard.up(key)](#keyboardupkey) * [class: Mouse](#class-mouse) + + [mouse.click(x, y, [options])](#mouseclickx-y-options) + [mouse.down([options])](#mousedownoptions) + [mouse.move(x, y)](#mousemovex-y) - + [mouse.press([options])](#mousepressoptions) + [mouse.up([options])](#mouseupoptions) * [class: Dialog](#class-dialog) + [dialog.accept([promptText])](#dialogacceptprompttext) @@ -679,6 +679,16 @@ Dispatches a `keyup` event. ### class: Mouse +#### mouse.click(x, y, [options]) +- `x` <[number]> +- `y` <[number]> +- `options` <[Object]> + - `button` <[string]> `left`, `right`, or `middle`, defaults to `left`. + - `clickCount` <[number]> defaults to 1 +- returns: <[Promise]> + +Shortcut for [`mouse.move`](#mousemovexy), [`mouse.down`](#mousedownkey) and [`mouse.up`](#mouseupkey). + #### mouse.down([options]) - `options` <[Object]> - `button` <[string]> `left`, `right`, or `middle`, defaults to `left`. @@ -694,14 +704,6 @@ Dispatches a `mousedown` event. Dispatches a `mousemove` event. -#### mouse.press([options]) -- `options` <[Object]> - - `button` <[string]> `left`, `right`, or `middle`, defaults to `left`. - - `clickCount` <[number]> defaults to 1 -- returns: <[Promise]> - -Shortcut for [`mouse.down`](#mousedownkey) and [`mouse.up`](#mouseupkey). - #### mouse.up([options]) - `options` <[Object]> - `button` <[string]> `left`, `right`, or `middle`, defaults to `left`. diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 1d32da62..b3670895 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -345,9 +345,9 @@ class Frame { /** * @param {string} selector - * @return {!Promise} + * @return {!Promise<{x: number, y: number}>} */ - async hover(selector) { + async _centerOfElement(selector) { let center = await this.evaluate(selector => { let element = document.querySelector(selector); if (!element) @@ -361,7 +361,16 @@ class Frame { }, selector); if (!center) throw new Error('No node found for selector: ' + selector); - await this._mouse.move(center.x, center.y); + return center; + } + + /** + * @param {string} selector + * @return {!Promise} + */ + async hover(selector) { + let {x, y} = await this._centerOfElement(selector); + await this._mouse.move(x, y); } /** @@ -370,10 +379,8 @@ class Frame { * @return {!Promise} */ async click(selector, options) { - await this.hover(selector); - await this._mouse.press(options); - // This is a hack for now, to make clicking less race-prone - await this.evaluate(() => new Promise(f => requestAnimationFrame(f))); + let {x, y} = await this._centerOfElement(selector); + await this._mouse.click(x, y, options); } /** diff --git a/lib/Input.js b/lib/Input.js index 0c244d3f..02ce5d8b 100644 --- a/lib/Input.js +++ b/lib/Input.js @@ -118,11 +118,20 @@ class Mouse { } /** + * @param {number} x + * @param {number} y * @param {!Object=} options */ - async press(options) { + async click(x, y, options) { + await this.move(x, y); await this.down(options); await this.up(options); + // This is a hack for now, to make clicking less race-prone. See crbug.com/747647 + await this._client.send('Runtime.evaluate', { + expression: 'new Promise(f => requestAnimationFrame(f))', + awaitPromise: true, + returnByValue: true + }); } /** diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 2b0a08d4..84583b49 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -466,25 +466,27 @@ class WebPage { _sendMouseEvent(eventType, x, y, button, modifier) { if (modifier) await(this._page.keyboard.down(modifier)); - await(this._page.mouse.move(x, y)); switch (eventType) { case 'mousemove': + await(this._page.mouse.move(x, y)); break; case 'mousedown': + await(this._page.mouse.move(x, y)); await(this._page.mouse.down({button})); break; case 'mouseup': + await(this._page.mouse.move(x, y)); await(this._page.mouse.up({button})); break; case 'doubleclick': - await(this._page.mouse.press({button})); - await(this._page.mouse.press({button, clickCount: 2})); + await(this._page.mouse.click(x, y, {button})); + await(this._page.mouse.click(x, y, {button, clickCount: 2})); break; case 'click': - await(this._page.mouse.press({button})); + await(this._page.mouse.click(x, y, {button})); break; case 'contextmenu': - await(this._page.mouse.press({button: 'right'})); + await(this._page.mouse.click(x, y, {button: 'right'})); break; } if (modifier)