diff --git a/docs/api.md b/docs/api.md index 4693614de2b..b8936a37e27 100644 --- a/docs/api.md +++ b/docs/api.md @@ -33,6 +33,7 @@ * [page.setViewportSize(size)](#pagesetviewportsizesize) * [page.title()](#pagetitle) * [page.type()](#pagetype) + * [page.uploadFile(selector, ...filePaths)](#pageuploadfileselector-filepaths) * [page.url()](#pageurl) * [page.userAgent()](#pageuseragent) * [page.viewportSize()](#pageviewportsize) @@ -236,6 +237,11 @@ Pages could be closed by `page.close()` method. #### page.type() +#### page.uploadFile(selector, ...filePaths) +- `selector` <[string]> A query selector to a file input +- `...filePaths` <[string]> Sets the value of the file input these paths +- returns: <[Promise]> Promise which resolves when the value is set. + #### page.url() - returns: <[Promise]<[string]>> Promise which resolves with the current page url. diff --git a/lib/NetworkManager.js b/lib/NetworkManager.js index 4b5c53585eb..2dd7897ef03 100644 --- a/lib/NetworkManager.js +++ b/lib/NetworkManager.js @@ -108,7 +108,10 @@ class NetworkManager extends EventEmitter { * @param {!Object} event */ _onResponseReceived(event) { - let request = this._idToRequest.get(event.requestId) || null; + let request = this._idToRequest.get(event.requestId); + // FileUpload sends a response without a matching request. + if (!request) + return; let response = new Response(request, event.response, this._getResponseBody.bind(this, event.requestId)); request._response = response; this.emit(NetworkManager.Events.Response, response); diff --git a/lib/Page.js b/lib/Page.js index 0c8c26f61fe..c66267e82e6 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -493,10 +493,13 @@ class Page extends EventEmitter { * @param {!Promise} */ async _querySelector(selector) { - return (await this._client.send('DOM.querySelector', { + let {nodeId} = await this._client.send('DOM.querySelector', { nodeId: await this._rootNodeId(), selector - })).nodeId; + }); + if (!nodeId) + throw new Error('No node found for selector: ' + selector); + return nodeId; } /** @@ -569,6 +572,18 @@ class Page extends EventEmitter { waitFor(selector) { return this.mainFrame().waitFor(selector); } + + /** + * @param {string} selector + * @param {!Array} filePaths + * @return {!Promise} + */ + async uploadFile(selector, ...filePaths) { + await this._client.send('DOM.setFileInputFiles', { + nodeId: await this._querySelector(selector), + files: filePaths + }); + } } /** @enum {string} */ diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 05887a90329..cd9e3ed1b9c 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -332,6 +332,17 @@ class WebPage { await(this._page.setContent(html)); } + /** + * @param {string} selector + * @param {(string|!Array)} files + */ + uploadFile(selector, files) { + if (typeof files === 'string') + await(this._page.uploadFile(selector, files)); + else + await(this._page.uploadFile(selector, ...files)); + } + /** * @param {string} html * @param {function()=} callback diff --git a/test/assets/file-to-upload.txt b/test/assets/file-to-upload.txt new file mode 100644 index 00000000000..b4ad1184894 --- /dev/null +++ b/test/assets/file-to-upload.txt @@ -0,0 +1 @@ +contents of the file \ No newline at end of file diff --git a/test/assets/input/fileupload.html b/test/assets/input/fileupload.html new file mode 100644 index 00000000000..55fd7c50060 --- /dev/null +++ b/test/assets/input/fileupload.html @@ -0,0 +1,9 @@ + + + + File upload test + + + + + \ No newline at end of file diff --git a/test/test.js b/test/test.js index 650d384ed5d..3a5e9701145 100644 --- a/test/test.js +++ b/test/test.js @@ -544,6 +544,15 @@ describe('Puppeteer', function() { await page.click('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); })); + it('should fail to click a missing button', SX(async function() { + await page.navigate(STATIC_PREFIX + '/input/button.html'); + try { + await page.click('button.does-not-exist'); + fail('Clicking the button did not throw.'); + } catch (error) { + expect(error.message).toBe('No node found for selector: button.does-not-exist'); + } + })); it('should type into the textarea', SX(async function() { await page.navigate(STATIC_PREFIX + '/input/textarea.html'); await page.focus('textarea'); @@ -557,6 +566,21 @@ describe('Puppeteer', function() { await page.click('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); })); + it('should upload the file', SX(async function(){ + await page.navigate(STATIC_PREFIX + '/input/fileupload.html'); + await page.uploadFile('input', __dirname + '/assets/file-to-upload.txt'); + 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 reader = new FileReader(); + let promise = new Promise(fulfill => reader.onload = fulfill); + reader.readAsText(input.files[0]); + return promise.then(() => reader.result); + })).toBe('contents of the file'); + })); }); describe('Page.setUserAgent', function() { it('should work', SX(async function() { diff --git a/third_party/phantomjs/test/module/webpage/file-upload.js b/third_party/phantomjs/test/module/webpage/file-upload.js index 711ef30c82c..780ffd7bcaa 100644 --- a/third_party/phantomjs/test/module/webpage/file-upload.js +++ b/third_party/phantomjs/test/module/webpage/file-upload.js @@ -1,11 +1,9 @@ -//! unsupported - // Note: uses various files in module/webpage as things to be uploaded. // Which files they are doesn't matter. var page; setup(function () { - page = new WebPage(); + page = require('webpage').create(); page.content = '\n' + '\n' + @@ -21,7 +19,7 @@ function test_one_elt(id, names) { var elt = document.getElementById(id); var rv = []; for (var i = 0; i < elt.files.length; i++) { - rv.push(elt.files[i].fileName); + rv.push(elt.files[i].name); } return rv; }, id); @@ -32,7 +30,7 @@ generate_tests(test_one_elt, [ ["single upload single file", "file", ["file-upload.js"]], ["multiple upload single file", "file2", ["file-upload.js"]], ["multiple upload multiple file", "file3", ["file-upload.js", "object.js"]], -], { expected_fail: true }); +], { expected_fail: false }); async_test(function () { page.onFilePicker = this.step_func(function (oldFile) {