From 5f0afd5547eef746e94e95c87f6cc633f73a75ce Mon Sep 17 00:00:00 2001 From: JoelEinbinder Date: Thu, 17 Aug 2017 14:53:37 -0700 Subject: [PATCH] Move uploadFiles onto ElementHandle (#348) This patch: - removes the `page.uploadFile` method - adds `elementHandle.uploadFile` method. Motivation: `elementHandle.uploadFile` is rarely used, so it doesn't worth it to keep it on page. --- docs/api.md | 21 +++++++-------------- lib/ElementHandle.js | 11 +++++++++++ lib/FrameManager.js | 16 ---------------- lib/Page.js | 9 --------- phantom_shim/WebPage.js | 4 ++-- test/test.js | 13 +++++-------- 6 files changed, 25 insertions(+), 49 deletions(-) diff --git a/docs/api.md b/docs/api.md index d46d0dfc503..07bbb138b42 100644 --- a/docs/api.md +++ b/docs/api.md @@ -57,7 +57,6 @@ + [page.title()](#pagetitle) + [page.tracing](#pagetracing) + [page.type(text, options)](#pagetypetext-options) - + [page.uploadFile(selector, ...filePaths)](#pageuploadfileselector-filepaths) + [page.url()](#pageurl) + [page.viewport()](#pageviewport) + [page.waitFor(selectorOrFunctionOrTimeout[, options])](#pagewaitforselectororfunctionortimeout-options) @@ -92,7 +91,6 @@ + [frame.name()](#framename) + [frame.parentFrame()](#frameparentframe) + [frame.title()](#frametitle) - + [frame.uploadFile(selector, ...filePaths)](#frameuploadfileselector-filepaths) + [frame.url()](#frameurl) + [frame.waitFor(selectorOrFunctionOrTimeout[, options])](#framewaitforselectororfunctionortimeout-options) + [frame.waitForFunction(pageFunction[, options, ...args])](#framewaitforfunctionpagefunction-options-args) @@ -102,6 +100,7 @@ + [elementHandle.dispose()](#elementhandledispose) + [elementHandle.evaluate(pageFunction, ...args)](#elementhandleevaluatepagefunction-args) + [elementHandle.hover()](#elementhandlehover) + + [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths) * [class: Request](#class-request) + [request.abort()](#requestabort) + [request.continue([overrides])](#requestcontinueoverrides) @@ -684,13 +683,6 @@ page.type('Hello'); // Types instantly page.type('World', {delay: 100}); // Types slower, like a user ``` -#### page.uploadFile(selector, ...filePaths) -- `selector` <[string]> A [selector] to a file input -- `...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]> Promise which resolves when the value is set. - -Shortcut for [page.mainFrame().uploadFile(selector, ...filePaths)](#frameuploadfileselector-filepaths). - #### page.url() - returns: <[string]> @@ -1014,11 +1006,6 @@ If the name is empty, returns the id attribute instead. #### frame.title() - returns: <[Promise]<[string]>> Returns page's title. -#### frame.uploadFile(selector, ...filePaths) -- `selector` <[string]> A [selector] to a file input -- `...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]> Promise which resolves when the value is set. - #### frame.url() - returns: <[string]> @@ -1130,6 +1117,12 @@ The function will be passed in the element ifself as a first argument. This method scrolls element into view if needed, and then uses [page.mouse](#pagemouse) to hover over the center of the element. If the element is detached from DOM, the method throws an error. +#### 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]> + +This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). + ### class: Request Whenever the page sends a request, the following events are emitted by puppeteer's page: diff --git a/lib/ElementHandle.js b/lib/ElementHandle.js index 89281a03229..bd0667cfd69 100644 --- a/lib/ElementHandle.js +++ b/lib/ElementHandle.js @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const path = require('path'); const helper = require('./helper'); class ElementHandle { @@ -92,6 +93,16 @@ class ElementHandle { let {x, y} = await this._visibleCenter(); await this._mouse.click(x, y, options); } + + /** + * @param {!Array} filePaths + * @return {!Promise} + */ + async uploadFile(...filePaths) { + let files = filePaths.map(filePath => path.resolve(filePath)); + const objectId = this._remoteObject.objectId; + return this._client.send('DOM.setFileInputFiles', { objectId, files }); + } } module.exports = ElementHandle; diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 2b3754d694a..df3aa74b4aa 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -15,7 +15,6 @@ */ const fs = require('fs'); -const path = require('path'); const EventEmitter = require('events'); const helper = require('./helper'); const ElementHandle = require('./ElementHandle'); @@ -325,21 +324,6 @@ class Frame { return new WaitTask(this, predicateCode, polling, timeout).promise; } - /** - * @param {string} selector - * @param {!Array} filePaths - * @return {!Promise} - */ - async uploadFile(selector, ...filePaths) { - let expression = helper.evaluationString(selector => document.querySelector(selector), selector); - const {result} = await this._client.send('Runtime.evaluate', { expression }); - if (!result) - return; - const objectId = result.objectId; - filePaths = filePaths.map(filePath => path.resolve(filePath)); - return this._client.send('DOM.setFileInputFiles', { objectId, files: filePaths }); - } - /** * @return {!Promise} */ diff --git a/lib/Page.js b/lib/Page.js index 33cdae3396d..d9e6230b22e 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -647,15 +647,6 @@ class Page extends EventEmitter { waitForFunction(pageFunction, options = {}, ...args) { return this.mainFrame().waitForFunction(pageFunction, options, ...args); } - - /** - * @param {string} selector - * @param {!Array} filePaths - * @return {!Promise} - */ - async uploadFile(selector, ...filePaths) { - return this.mainFrame().uploadFile(selector, ...filePaths); - } } /** @enum {string} */ diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 86ed77cbc49..0619b70e74c 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -420,9 +420,9 @@ class WebPage { */ uploadFile(selector, files) { if (typeof files === 'string') - await(this._page.uploadFile(selector, files)); + await(await(this._page.$(selector)).uploadFile(files)); else - await(this._page.uploadFile(selector, ...files)); + await(await(this._page.$(selector)).uploadFile(...files)); } /** diff --git a/test/test.js b/test/test.js index b0d29b66741..1796a1a9a90 100644 --- a/test/test.js +++ b/test/test.js @@ -1171,16 +1171,13 @@ describe('Page', function() { it('should upload the file', SX(async function(){ await page.goto(PREFIX + '/input/fileupload.html'); const filePath = path.relative(process.cwd(), __dirname + '/assets/file-to-upload.txt'); - await page.uploadFile('input', filePath); - expect(await page.evaluate(() => { - let input = document.querySelector('input'); - return input.files[0].name; - })).toBe('file-to-upload.txt'); - expect(await page.evaluate(() => { - let input = document.querySelector('input'); + let input = await page.$('input'); + await input.uploadFile(filePath); + expect(await input.evaluate(e => e.files[0].name)).toBe('file-to-upload.txt'); + expect(await input.evaluate(e => { let reader = new FileReader(); let promise = new Promise(fulfill => reader.onload = fulfill); - reader.readAsText(input.files[0]); + reader.readAsText(e.files[0]); return promise.then(() => reader.result); })).toBe('contents of the file'); }));