diff --git a/experimental/puppeteer-firefox/lib/DOMWorld.js b/experimental/puppeteer-firefox/lib/DOMWorld.js index 97bafc03..096c06f2 100644 --- a/experimental/puppeteer-firefox/lib/DOMWorld.js +++ b/experimental/puppeteer-firefox/lib/DOMWorld.js @@ -319,6 +319,16 @@ class DOMWorld { }, values); } + /** + * @param {string} selector + */ + async tap(selector) { + const handle = await this.$(selector); + assert(handle, 'No node found for selector: ' + selector); + await handle.tap(); + await handle.dispose(); + } + /** * @param {string} selector * @param {string} text diff --git a/experimental/puppeteer-firefox/lib/FrameManager.js b/experimental/puppeteer-firefox/lib/FrameManager.js index 2eae0136..f5411809 100644 --- a/experimental/puppeteer-firefox/lib/FrameManager.js +++ b/experimental/puppeteer-firefox/lib/FrameManager.js @@ -253,6 +253,13 @@ class Frame { return this._mainWorld.click(selector, options); } + /** + * @param {string} selector + */ + async tap(selector) { + return this._mainWorld.tap(selector); + } + /** * @param {string} selector * @param {string} text diff --git a/experimental/puppeteer-firefox/lib/Input.js b/experimental/puppeteer-firefox/lib/Input.js index 1a11efff..11096ca8 100644 --- a/experimental/puppeteer-firefox/lib/Input.js +++ b/experimental/puppeteer-firefox/lib/Input.js @@ -292,4 +292,43 @@ class Mouse { } } -module.exports = { Keyboard, Mouse }; +class Touchscreen { + /** + * @param {Puppeteer.JugglerSession} client + * @param {Keyboard} keyboard + * @param {Mouse} mouse + */ + constructor(client, keyboard, mouse) { + this._client = client; + this._keyboard = keyboard; + this._mouse = mouse; + } + + /** + * @param {number} x + * @param {number} y + */ + async tap(x, y) { + const touchPoints = [{x: Math.round(x), y: Math.round(y)}]; + let {defaultPrevented} = (await this._client.send('Page.dispatchTouchEvent', { + type: 'touchStart', + touchPoints, + modifiers: this._keyboard._modifiers + })); + defaultPrevented = (await this._client.send('Page.dispatchTouchEvent', { + type: 'touchEnd', + touchPoints, + modifiers: this._keyboard._modifiers + })).defaultPrevented || defaultPrevented; + // Do not dispatch related mouse events if either of touch events + // were prevented. + // See https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent#Event_order + if (defaultPrevented) + return; + await this._mouse.move(x, y); + await this._mouse.down(); + await this._mouse.up(); + } +} + +module.exports = { Keyboard, Mouse, Touchscreen }; diff --git a/experimental/puppeteer-firefox/lib/JSHandle.js b/experimental/puppeteer-firefox/lib/JSHandle.js index c6445cda..a6ef8cfa 100644 --- a/experimental/puppeteer-firefox/lib/JSHandle.js +++ b/experimental/puppeteer-firefox/lib/JSHandle.js @@ -332,6 +332,12 @@ class ElementHandle extends JSHandle { await this._frame._page.mouse.click(x, y, options); } + async tap() { + await this._scrollIntoViewIfNeeded(); + const {x, y} = await this._clickablePoint(); + await this._frame._page.touchscreen.tap(x, y); + } + /** * @param {!Array} filePaths */ diff --git a/experimental/puppeteer-firefox/lib/Page.js b/experimental/puppeteer-firefox/lib/Page.js index 55b4a6f4..64a9fde4 100644 --- a/experimental/puppeteer-firefox/lib/Page.js +++ b/experimental/puppeteer-firefox/lib/Page.js @@ -1,5 +1,5 @@ const {helper, debugError, assert} = require('./helper'); -const {Keyboard, Mouse} = require('./Input'); +const {Keyboard, Mouse, Touchscreen} = require('./Input'); const {Dialog} = require('./Dialog'); const {TimeoutError} = require('./Errors'); const fs = require('fs'); @@ -46,6 +46,7 @@ class Page extends EventEmitter { this._target = target; this._keyboard = new Keyboard(session); this._mouse = new Mouse(session, this._keyboard); + this._touchscreen = new Touchscreen(session, this._keyboard, this._mouse); this._closed = false; /** @type {!Map} */ this._pageBindings = new Map(); @@ -337,6 +338,10 @@ class Page extends EventEmitter { return this._mouse; } + get touchscreen(){ + return this._touchscreen; + } + /** * @param {!{timeout?: number, waitUntil?: string|!Array}} options */ @@ -505,6 +510,13 @@ class Page extends EventEmitter { return await this._frameManager.mainFrame().click(selector, options); } + /** + * @param {string} selector + */ + tap(selector) { + return this.mainFrame().tap(selector); + } + /** * @param {string} selector * @param {string} text diff --git a/experimental/puppeteer-firefox/lib/api.js b/experimental/puppeteer-firefox/lib/api.js index f1e97f37..fbd92299 100644 --- a/experimental/puppeteer-firefox/lib/api.js +++ b/experimental/puppeteer-firefox/lib/api.js @@ -16,5 +16,6 @@ module.exports = { Response: require('./NetworkManager').Response, SecurityDetails: require('./NetworkManager').SecurityDetails, Target: require('./Browser').Target, + Touchscreen: require('./Input').Touchscreen, TimeoutError: require('./Errors').TimeoutError, }; diff --git a/experimental/puppeteer-firefox/package.json b/experimental/puppeteer-firefox/package.json index 4be4a567..985c0749 100644 --- a/experimental/puppeteer-firefox/package.json +++ b/experimental/puppeteer-firefox/package.json @@ -9,7 +9,7 @@ "node": ">=8.9.4" }, "puppeteer": { - "firefox_revision": "764023af0aa07d232984dec6bc81d9e904f25ddb" + "firefox_revision": "6237be74b2870ab50cc165b9d5be46a85091674f" }, "scripts": { "install": "node install.js", diff --git a/test/touchscreen.spec.js b/test/touchscreen.spec.js index 11f537f4..5c9cc98c 100644 --- a/test/touchscreen.spec.js +++ b/test/touchscreen.spec.js @@ -14,17 +14,20 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect}) { +module.exports.addTests = function({testRunner, expect, DeviceDescriptors}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit, it_fails_ffox} = testRunner; + const iPhone = DeviceDescriptors['iPhone 6']; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('Touchscreen', function() { - it_fails_ffox('should tap the button', async({page, server}) => { + it('should tap the button', async({page, server}) => { + await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/button.html'); await page.tap('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); }); - xit('should report touches', async({page, server}) => { + it('should report touches', async({page, server}) => { + await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/touches.html'); const button = await page.$('button'); await button.tap();