diff --git a/test/browser.spec.js b/test/browser.spec.js
new file mode 100644
index 00000000000..17cbecc6f6a
--- /dev/null
+++ b/test/browser.spec.js
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+ const headless = defaultBrowserOptions.headless;
+
+ describe('Browser.version', function() {
+ it('should return whether we are in headless', async({browser}) => {
+ const version = await browser.version();
+ expect(version.length).toBeGreaterThan(0);
+ expect(version.startsWith('Headless')).toBe(headless);
+ });
+ });
+
+ describe('Browser.userAgent', function() {
+ it('should include WebKit', async({browser}) => {
+ const userAgent = await browser.userAgent();
+ expect(userAgent.length).toBeGreaterThan(0);
+ expect(userAgent).toContain('WebKit');
+ });
+ });
+
+ describe('Browser.process', function() {
+ it('should return child_process instance', async function({browser}) {
+ const process = await browser.process();
+ expect(process.pid).toBeGreaterThan(0);
+ const browserWSEndpoint = browser.wsEndpoint();
+ const remoteBrowser = await puppeteer.connect({browserWSEndpoint});
+ expect(remoteBrowser.process()).toBe(null);
+ await remoteBrowser.disconnect();
+ });
+ });
+
+ describe('Browser.Events.disconnected', function() {
+ it('should emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => {
+ const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
+ const browserWSEndpoint = originalBrowser.wsEndpoint();
+ const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint});
+ const remoteBrowser2 = await puppeteer.connect({browserWSEndpoint});
+
+ let disconnectedOriginal = 0;
+ let disconnectedRemote1 = 0;
+ let disconnectedRemote2 = 0;
+ originalBrowser.on('disconnected', () => ++disconnectedOriginal);
+ remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
+ remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
+
+ await remoteBrowser2.disconnect();
+ expect(disconnectedOriginal).toBe(0);
+ expect(disconnectedRemote1).toBe(0);
+ expect(disconnectedRemote2).toBe(1);
+
+ await originalBrowser.close();
+ expect(disconnectedOriginal).toBe(1);
+ expect(disconnectedRemote1).toBe(1);
+ expect(disconnectedRemote2).toBe(1);
+ });
+ });
+};
\ No newline at end of file
diff --git a/test/elementhandle.spec.js b/test/elementhandle.spec.js
new file mode 100644
index 00000000000..aded656c7cd
--- /dev/null
+++ b/test/elementhandle.spec.js
@@ -0,0 +1,272 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const utils = require('./utils');
+
+module.exports.addTests = function({testRunner, expect}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ describe('ElementHandle.boundingBox', function() {
+ it('should work', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.goto(server.PREFIX + '/grid.html');
+ const elementHandle = await page.$('.box:nth-of-type(13)');
+ const box = await elementHandle.boundingBox();
+ expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
+ });
+ it('should handle nested frames', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.goto(server.PREFIX + '/frames/nested-frames.html');
+ const nestedFrame = page.frames()[1].childFrames()[1];
+ const elementHandle = await nestedFrame.$('div');
+ const box = await elementHandle.boundingBox();
+ expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
+ });
+ it('should return null for invisible elements', async({page, server}) => {
+ await page.setContent('
hi
');
+ const element = await page.$('div');
+ expect(await element.boundingBox()).toBe(null);
+ });
+ it('should force a layout', async({page, server}) => {
+ await page.setViewport({ width: 500, height: 500 });
+ await page.setContent('hello
');
+ const elementHandle = await page.$('div');
+ await page.evaluate(element => element.style.height = '200px', elementHandle);
+ const box = await elementHandle.boundingBox();
+ expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
+ });
+ });
+
+ describe('ElementHandle.contentFrame', function() {
+ it('should work', async({page,server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ const elementHandle = await page.$('#frame1');
+ const frame = await elementHandle.contentFrame();
+ expect(frame).toBe(page.frames()[1]);
+ });
+ });
+
+ describe('ElementHandle.click', function() {
+ it('should work', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ const button = await page.$('button');
+ await button.click();
+ expect(await page.evaluate(() => result)).toBe('Clicked');
+ });
+ it('should work for Shadow DOM v1', async({page, server}) => {
+ await page.goto(server.PREFIX + '/shadow.html');
+ const buttonHandle = await page.evaluateHandle(() => button);
+ await buttonHandle.click();
+ expect(await page.evaluate(() => clicked)).toBe(true);
+ });
+ it('should work for TextNodes', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
+ let error = null;
+ await buttonTextNode.click().catch(err => error = err);
+ expect(error.message).toBe('Node is not of type HTMLElement');
+ });
+ it('should throw for detached nodes', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ const button = await page.$('button');
+ await page.evaluate(button => button.remove(), button);
+ let error = null;
+ await button.click().catch(err => error = err);
+ expect(error.message).toBe('Node is detached from document');
+ });
+ it('should throw for hidden nodes', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ const button = await page.$('button');
+ await page.evaluate(button => button.style.display = 'none', button);
+ const error = await button.click().catch(err => err);
+ expect(error.message).toBe('Node is either not visible or not an HTMLElement');
+ });
+ it('should throw for recursively hidden nodes', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ const button = await page.$('button');
+ await page.evaluate(button => button.parentElement.style.display = 'none', button);
+ const error = await button.click().catch(err => err);
+ expect(error.message).toBe('Node is either not visible or not an HTMLElement');
+ });
+ it('should throw for
elements', async({page, server}) => {
+ await page.setContent('hello
goodbye');
+ const br = await page.$('br');
+ const error = await br.click().catch(err => err);
+ expect(error.message).toBe('Node is either not visible or not an HTMLElement');
+ });
+ });
+
+ describe('ElementHandle.hover', function() {
+ it('should work', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ const button = await page.$('#button-6');
+ await button.hover();
+ expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
+ });
+ });
+
+ describe('ElementHandle.screenshot', function() {
+ it('should work', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.goto(server.PREFIX + '/grid.html');
+ await page.evaluate(() => window.scrollBy(50, 100));
+ const elementHandle = await page.$('.box:nth-of-type(3)');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
+ });
+ it('should take into account padding and border', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.setContent(`
+ something above
+
+
+ `);
+ const elementHandle = await page.$('div');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
+ });
+ it('should capture full element when larger than viewport', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+
+ await page.setContent(`
+ something above
+
+
+ `);
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
+
+ expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
+ });
+ it('should scroll element into view', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.setContent(`
+ something above
+
+
+
+ `);
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
+ });
+ it('should work with a rotated element', async({page, server}) => {
+ await page.setViewport({width: 500, height: 500});
+ await page.setContent(`
`);
+ const elementHandle = await page.$('div');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-rotate.png');
+ });
+ it('should fail to screenshot a detached element', async({page, server}) => {
+ await page.setContent('remove this
');
+ const elementHandle = await page.$('h1');
+ await page.evaluate(element => element.remove(), elementHandle);
+ const screenshotError = await elementHandle.screenshot().catch(error => error);
+ expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement');
+ });
+ });
+
+ describe('ElementHandle.$', function() {
+ it('should query existing element', async({page, server}) => {
+ await page.goto(server.PREFIX + '/playground.html');
+ await page.setContent('');
+ const html = await page.$('html');
+ const second = await html.$('.second');
+ const inner = await second.$('.inner');
+ const content = await page.evaluate(e => e.textContent, inner);
+ expect(content).toBe('A');
+ });
+
+ it('should return null for non-existing element', async({page, server}) => {
+ await page.setContent('');
+ const html = await page.$('html');
+ const second = await html.$('.third');
+ expect(second).toBe(null);
+ });
+ });
+
+ describe('ElementHandle.$$', function() {
+ it('should query existing elements', async({page, server}) => {
+ await page.setContent('A
B
');
+ const html = await page.$('html');
+ const elements = await html.$$('div');
+ expect(elements.length).toBe(2);
+ const promises = elements.map(element => page.evaluate(e => e.textContent, element));
+ expect(await Promise.all(promises)).toEqual(['A', 'B']);
+ });
+
+ it('should return empty array for non-existing elements', async({page, server}) => {
+ await page.setContent('A
B');
+ const html = await page.$('html');
+ const elements = await html.$$('div');
+ expect(elements.length).toBe(0);
+ });
+ });
+
+
+ describe('ElementHandle.$x', function() {
+ it('should query existing element', async({page, server}) => {
+ await page.goto(server.PREFIX + '/playground.html');
+ await page.setContent('');
+ const html = await page.$('html');
+ const second = await html.$x(`./body/div[contains(@class, 'second')]`);
+ const inner = await second[0].$x(`./div[contains(@class, 'inner')]`);
+ const content = await page.evaluate(e => e.textContent, inner[0]);
+ expect(content).toBe('A');
+ });
+
+ it('should return null for non-existing element', async({page, server}) => {
+ await page.setContent('');
+ const html = await page.$('html');
+ const second = await html.$x(`/div[contains(@class, 'third')]`);
+ expect(second).toEqual([]);
+ });
+ });
+};
\ No newline at end of file
diff --git a/test/frame.spec.js b/test/frame.spec.js
new file mode 100644
index 00000000000..88c39b25cde
--- /dev/null
+++ b/test/frame.spec.js
@@ -0,0 +1,371 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const utils = require('./utils');
+
+module.exports.addTests = function({testRunner, expect}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ describe('Frame.context', function() {
+ it('should work', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ expect(page.frames().length).toBe(2);
+ const [frame1, frame2] = page.frames();
+ const context1 = await frame1.executionContext();
+ const context2 = await frame2.executionContext();
+ expect(context1).toBeTruthy();
+ expect(context2).toBeTruthy();
+ expect(context1 !== context2).toBeTruthy();
+ expect(context1.frame()).toBe(frame1);
+ expect(context2.frame()).toBe(frame2);
+
+ await Promise.all([
+ context1.evaluate(() => window.a = 1),
+ context2.evaluate(() => window.a = 2)
+ ]);
+ const [a1, a2] = await Promise.all([
+ context1.evaluate(() => window.a),
+ context2.evaluate(() => window.a)
+ ]);
+ expect(a1).toBe(1);
+ expect(a2).toBe(2);
+ });
+ });
+
+ describe('Frame.evaluateHandle', function() {
+ it('should work', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const mainFrame = page.mainFrame();
+ const windowHandle = await mainFrame.evaluateHandle(() => window);
+ expect(windowHandle).toBeTruthy();
+ });
+ });
+
+ describe('Frame.evaluate', function() {
+ it('should have different execution contexts', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ expect(page.frames().length).toBe(2);
+ const frame1 = page.frames()[0];
+ const frame2 = page.frames()[1];
+ await frame1.evaluate(() => window.FOO = 'foo');
+ await frame2.evaluate(() => window.FOO = 'bar');
+ expect(await frame1.evaluate(() => window.FOO)).toBe('foo');
+ expect(await frame2.evaluate(() => window.FOO)).toBe('bar');
+ });
+ it('should execute after cross-site navigation', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const mainFrame = page.mainFrame();
+ expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost');
+ await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
+ expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
+ });
+ });
+
+ describe('Frame.waitForFunction', function() {
+ it('should accept a string', async({page, server}) => {
+ const watchdog = page.waitForFunction('window.__FOO === 1');
+ await page.evaluate(() => window.__FOO = 1);
+ await watchdog;
+ });
+ it('should poll on interval', async({page, server}) => {
+ let success = false;
+ const startTime = Date.now();
+ const polling = 100;
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling})
+ .then(() => success = true);
+ await page.evaluate(() => window.__FOO = 'hit');
+ expect(success).toBe(false);
+ await page.evaluate(() => document.body.appendChild(document.createElement('div')));
+ await watchdog;
+ expect(Date.now() - startTime).not.toBeLessThan(polling / 2);
+ });
+ it('should poll on mutation', async({page, server}) => {
+ let success = false;
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'})
+ .then(() => success = true);
+ await page.evaluate(() => window.__FOO = 'hit');
+ expect(success).toBe(false);
+ await page.evaluate(() => document.body.appendChild(document.createElement('div')));
+ await watchdog;
+ });
+ it('should poll on raf', async({page, server}) => {
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'});
+ await page.evaluate(() => window.__FOO = 'hit');
+ await watchdog;
+ });
+ it('should throw on bad polling value', async({page, server}) => {
+ let error = null;
+ try {
+ await page.waitForFunction(() => !!document.body, {polling: 'unknown'});
+ } catch (e) {
+ error = e;
+ }
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('polling');
+ });
+ it('should throw negative polling interval', async({page, server}) => {
+ let error = null;
+ try {
+ await page.waitForFunction(() => !!document.body, {polling: -10});
+ } catch (e) {
+ error = e;
+ }
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('Cannot poll with non-positive interval');
+ });
+ it('should return the success value as a JSHandle', async({page}) => {
+ expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
+ });
+ it('should return the window as a success value', async({ page }) => {
+ expect(await page.waitForFunction(() => window)).toBeTruthy();
+ });
+ it('should accept ElementHandle arguments', async({page}) => {
+ await page.setContent('');
+ const div = await page.$('div');
+ let resolved = false;
+ const waitForFunction = page.waitForFunction(element => !element.parentElement, {}, div).then(() => resolved = true);
+ expect(resolved).toBe(false);
+ await page.evaluate(element => element.remove(), div);
+ await waitForFunction;
+ });
+ });
+
+ describe('Frame.waitForSelector', function() {
+ const addElement = tag => document.body.appendChild(document.createElement(tag));
+
+ it('should immediately resolve promise if node exists', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const frame = page.mainFrame();
+ await frame.waitForSelector('*');
+ await frame.evaluate(addElement, 'div');
+ await frame.waitForSelector('div');
+ });
+
+ it('should resolve promise when node is added', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const frame = page.mainFrame();
+ const watchdog = frame.waitForSelector('div');
+ await frame.evaluate(addElement, 'br');
+ await frame.evaluate(addElement, 'div');
+ const eHandle = await watchdog;
+ const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
+ expect(tagName).toBe('DIV');
+ });
+
+ it('should work when node is added through innerHTML', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const watchdog = page.waitForSelector('h3 div');
+ await page.evaluate(addElement, 'span');
+ await page.evaluate(() => document.querySelector('span').innerHTML = '');
+ await watchdog;
+ });
+
+ it('Page.waitForSelector is shortcut for main frame', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ const otherFrame = page.frames()[1];
+ const watchdog = page.waitForSelector('div');
+ await otherFrame.evaluate(addElement, 'div');
+ await page.evaluate(addElement, 'div');
+ const eHandle = await watchdog;
+ expect(eHandle.executionContext().frame()).toBe(page.mainFrame());
+ });
+
+ it('should run in specified frame', async({page, server}) => {
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
+ const frame1 = page.frames()[1];
+ const frame2 = page.frames()[2];
+ const waitForSelectorPromise = frame2.waitForSelector('div');
+ await frame1.evaluate(addElement, 'div');
+ await frame2.evaluate(addElement, 'div');
+ const eHandle = await waitForSelectorPromise;
+ expect(eHandle.executionContext().frame()).toBe(frame2);
+ });
+
+ it('should throw if evaluation failed', async({page, server}) => {
+ await page.evaluateOnNewDocument(function() {
+ document.querySelector = null;
+ });
+ await page.goto(server.EMPTY_PAGE);
+ let error = null;
+ await page.waitForSelector('*').catch(e => error = e);
+ expect(error.message).toContain('document.querySelector is not a function');
+ });
+ it('should throw when frame is detached', async({page, server}) => {
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ const frame = page.frames()[1];
+ let waitError = null;
+ const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
+ await utils.detachFrame(page, 'frame1');
+ await waitPromise;
+ expect(waitError).toBeTruthy();
+ expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
+ });
+ it('should survive cross-process navigation', async({page, server}) => {
+ let boxFound = false;
+ const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
+ await page.goto(server.EMPTY_PAGE);
+ expect(boxFound).toBe(false);
+ await page.reload();
+ expect(boxFound).toBe(false);
+ await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
+ await waitForSelector;
+ expect(boxFound).toBe(true);
+ });
+ it('should wait for visible', async({page, server}) => {
+ let divFound = false;
+ const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true);
+ await page.setContent(`1
`);
+ expect(divFound).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
+ expect(divFound).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
+ expect(await waitForSelector).toBe(true);
+ expect(divFound).toBe(true);
+ });
+ it('should wait for visible recursively', async({page, server}) => {
+ let divVisible = false;
+ const waitForSelector = page.waitForSelector('div#inner', {visible: true}).then(() => divVisible = true);
+ await page.setContent(``);
+ expect(divVisible).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
+ expect(divVisible).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
+ expect(await waitForSelector).toBe(true);
+ expect(divVisible).toBe(true);
+ });
+ it('hidden should wait for visibility: hidden', async({page, server}) => {
+ let divHidden = false;
+ await page.setContent(``);
+ const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
+ await page.waitForSelector('div'); // do a round trip
+ expect(divHidden).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
+ expect(await waitForSelector).toBe(true);
+ expect(divHidden).toBe(true);
+ });
+ it('hidden should wait for display: none', async({page, server}) => {
+ let divHidden = false;
+ await page.setContent(``);
+ const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
+ await page.waitForSelector('div'); // do a round trip
+ expect(divHidden).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
+ expect(await waitForSelector).toBe(true);
+ expect(divHidden).toBe(true);
+ });
+ it('hidden should wait for removal', async({page, server}) => {
+ await page.setContent(``);
+ let divRemoved = false;
+ const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divRemoved = true);
+ await page.waitForSelector('div'); // do a round trip
+ expect(divRemoved).toBe(false);
+ await page.evaluate(() => document.querySelector('div').remove());
+ expect(await waitForSelector).toBe(true);
+ expect(divRemoved).toBe(true);
+ });
+ it('should respect timeout', async({page, server}) => {
+ let error = null;
+ await page.waitForSelector('div', {timeout: 10}).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting failed: timeout');
+ });
+
+ it('should respond to node attribute mutation', async({page, server}) => {
+ let divFound = false;
+ const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
+ await page.setContent(``);
+ expect(divFound).toBe(false);
+ await page.evaluate(() => document.querySelector('div').className = 'zombo');
+ expect(await waitForSelector).toBe(true);
+ });
+ it('should return the element handle', async({page, server}) => {
+ const waitForSelector = page.waitForSelector('.zombo');
+ await page.setContent(`anything
`);
+ expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
+ });
+ });
+
+ describe('Frame.waitForXPath', function() {
+ const addElement = tag => document.body.appendChild(document.createElement(tag));
+
+ it('should support some fancy xpath', async({page, server}) => {
+ await page.setContent(`red herring
hello world
`);
+ const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]');
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
+ });
+ it('should run in specified frame', async({page, server}) => {
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
+ const frame1 = page.frames()[1];
+ const frame2 = page.frames()[2];
+ const waitForXPathPromise = frame2.waitForXPath('//div');
+ await frame1.evaluate(addElement, 'div');
+ await frame2.evaluate(addElement, 'div');
+ const eHandle = await waitForXPathPromise;
+ expect(eHandle.executionContext().frame()).toBe(frame2);
+ });
+ it('should throw if evaluation failed', async({page, server}) => {
+ await page.evaluateOnNewDocument(function() {
+ document.evaluate = null;
+ });
+ await page.goto(server.EMPTY_PAGE);
+ let error = null;
+ await page.waitForXPath('*').catch(e => error = e);
+ expect(error.message).toContain('document.evaluate is not a function');
+ });
+ it('should throw when frame is detached', async({page, server}) => {
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ const frame = page.frames()[1];
+ let waitError = null;
+ const waitPromise = frame.waitForXPath('//*[@class="box"]').catch(e => waitError = e);
+ await utils.detachFrame(page, 'frame1');
+ await waitPromise;
+ expect(waitError).toBeTruthy();
+ expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
+ });
+ it('hidden should wait for display: none', async({page, server}) => {
+ let divHidden = false;
+ await page.setContent(``);
+ const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true);
+ await page.waitForXPath('//div'); // do a round trip
+ expect(divHidden).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
+ expect(await waitForXPath).toBe(true);
+ expect(divHidden).toBe(true);
+ });
+ it('should return the element handle', async({page, server}) => {
+ const waitForXPath = page.waitForXPath('//*[@class="zombo"]');
+ await page.setContent(`anything
`);
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
+ });
+ it('should allow you to select a text node', async({page, server}) => {
+ await page.setContent(`some text
`);
+ const text = await page.waitForXPath('//div/text()');
+ expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */);
+ });
+ it('should allow you to select an element with single slash', async({page, server}) => {
+ await page.setContent(`some text
`);
+ const waitForXPath = page.waitForXPath('/html/body/div');
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
+ });
+ });
+};
\ No newline at end of file
diff --git a/test/input.spec.js b/test/input.spec.js
new file mode 100644
index 00000000000..ff07275257d
--- /dev/null
+++ b/test/input.spec.js
@@ -0,0 +1,444 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const path = require('path');
+const utils = require('./utils');
+
+module.exports.addTests = function({testRunner, expect, PROJECT_ROOT}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+ const DeviceDescriptors = require(path.join(PROJECT_ROOT, 'DeviceDescriptors'));
+ const iPhone = DeviceDescriptors['iPhone 6'];
+ describe('input', function() {
+ it('should click the button', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.click('button');
+ expect(await page.evaluate(() => result)).toBe('Clicked');
+ });
+
+ it('should click on checkbox input and toggle', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/checkbox.html');
+ expect(await page.evaluate(() => result.check)).toBe(null);
+ await page.click('input#agree');
+ expect(await page.evaluate(() => result.check)).toBe(true);
+ expect(await page.evaluate(() => result.events)).toEqual([
+ 'mouseover',
+ 'mouseenter',
+ 'mousemove',
+ 'mousedown',
+ 'mouseup',
+ 'click',
+ 'input',
+ 'change',
+ ]);
+ await page.click('input#agree');
+ expect(await page.evaluate(() => result.check)).toBe(false);
+ });
+
+ it('should click on checkbox label and toggle', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/checkbox.html');
+ expect(await page.evaluate(() => result.check)).toBe(null);
+ await page.click('label[for="agree"]');
+ expect(await page.evaluate(() => result.check)).toBe(true);
+ expect(await page.evaluate(() => result.events)).toEqual([
+ 'click',
+ 'input',
+ 'change',
+ ]);
+ await page.click('label[for="agree"]');
+ expect(await page.evaluate(() => result.check)).toBe(false);
+ });
+
+ it('should fail to click a missing button', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ let error = null;
+ await page.click('button.does-not-exist').catch(e => error = e);
+ expect(error.message).toBe('No node found for selector: button.does-not-exist');
+ });
+ // @see https://github.com/GoogleChrome/puppeteer/issues/161
+ it('should not hang with touch-enabled viewports', async({page, server}) => {
+ await page.setViewport(iPhone.viewport);
+ await page.mouse.down();
+ await page.mouse.move(100, 10);
+ await page.mouse.up();
+ });
+ it('should type into the textarea', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+
+ const textarea = await page.$('textarea');
+ await textarea.type('Type in this text!');
+ expect(await page.evaluate(() => result)).toBe('Type in this text!');
+ });
+ it('should click the button after navigation ', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.click('button');
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.click('button');
+ expect(await page.evaluate(() => result)).toBe('Clicked');
+ });
+ it('should upload the file', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/fileupload.html');
+ const filePath = path.relative(process.cwd(), __dirname + '/assets/file-to-upload.txt');
+ const input = await page.$('input');
+ await input.uploadFile(filePath);
+ expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt');
+ expect(await page.evaluate(e => {
+ const reader = new FileReader();
+ const promise = new Promise(fulfill => reader.onload = fulfill);
+ reader.readAsText(e.files[0]);
+ return promise.then(() => reader.result);
+ }, input)).toBe('contents of the file');
+ });
+ it('should move with the arrow keys', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.type('textarea', 'Hello World!');
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
+ for (let i = 0; i < 'World!'.length; i++)
+ page.keyboard.press('ArrowLeft');
+ await page.keyboard.type('inserted ');
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
+ page.keyboard.down('Shift');
+ for (let i = 0; i < 'inserted '.length; i++)
+ page.keyboard.press('ArrowLeft');
+ page.keyboard.up('Shift');
+ await page.keyboard.press('Backspace');
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
+ });
+ it('should send a character with ElementHandle.press', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ const textarea = await page.$('textarea');
+ await textarea.press('a', {text: 'f'});
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
+
+ await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
+
+ await textarea.press('a', {text: 'y'});
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
+ });
+ it('should send a character with sendCharacter', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ await page.keyboard.sendCharacter('嗨');
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
+ await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
+ await page.keyboard.sendCharacter('a');
+ expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
+ });
+ it('should report shiftKey', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/keyboard.html');
+ const keyboard = page.keyboard;
+ const codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17};
+ for (const modifierKey in codeForKey) {
+ await keyboard.down(modifierKey);
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
+ await keyboard.down('!');
+ // Shift+! will generate a keypress
+ if (modifierKey === 'Shift')
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 33 [' + modifierKey + ']');
+ else
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
+
+ await keyboard.up('!');
+ expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
+ await keyboard.up(modifierKey);
+ expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
+ }
+ });
+ it('should report multiple modifiers', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/keyboard.html');
+ const keyboard = page.keyboard;
+ await keyboard.down('Control');
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
+ await keyboard.down('Meta');
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: Meta MetaLeft 91 [Control Meta]');
+ await keyboard.down(';');
+ expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Control Meta]');
+ await keyboard.up(';');
+ expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Control Meta]');
+ await keyboard.up('Control');
+ expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Meta]');
+ await keyboard.up('Meta');
+ expect(await page.evaluate(() => getResult())).toBe('Keyup: Meta MetaLeft 91 []');
+ });
+ it('should send proper codes while typing', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/keyboard.html');
+ await page.keyboard.type('!');
+ expect(await page.evaluate(() => getResult())).toBe(
+ [ 'Keydown: ! Digit1 49 []',
+ 'Keypress: ! Digit1 33 33 33 []',
+ 'Keyup: ! Digit1 49 []'].join('\n'));
+ await page.keyboard.type('^');
+ expect(await page.evaluate(() => getResult())).toBe(
+ [ 'Keydown: ^ Digit6 54 []',
+ 'Keypress: ^ Digit6 94 94 94 []',
+ 'Keyup: ^ Digit6 54 []'].join('\n'));
+ });
+ it('should send proper codes while typing with shift', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/keyboard.html');
+ const keyboard = page.keyboard;
+ await keyboard.down('Shift');
+ await page.keyboard.type('~');
+ expect(await page.evaluate(() => getResult())).toBe(
+ [ 'Keydown: Shift ShiftLeft 16 [Shift]',
+ 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
+ 'Keypress: ~ Backquote 126 126 126 [Shift]', // 126 is ~ charCode
+ 'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
+ await keyboard.up('Shift');
+ });
+ it('should not type canceled events', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ await page.evaluate(() => {
+ window.addEventListener('keydown', event => {
+ event.stopPropagation();
+ event.stopImmediatePropagation();
+ if (event.key === 'l')
+ event.preventDefault();
+ if (event.key === 'o')
+ Promise.resolve().then(() => event.preventDefault());
+ }, false);
+ });
+ await page.keyboard.type('Hello World!');
+ expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
+ });
+ it('keyboard.modifiers()', async({page, server}) => {
+ const keyboard = page.keyboard;
+ expect(keyboard._modifiers).toBe(0);
+ await keyboard.down('Shift');
+ expect(keyboard._modifiers).toBe(8);
+ await keyboard.down('Alt');
+ expect(keyboard._modifiers).toBe(9);
+ await keyboard.up('Shift');
+ await keyboard.up('Alt');
+ expect(keyboard._modifiers).toBe(0);
+ });
+ it('should resize the textarea', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ const {x, y, width, height} = await page.evaluate(dimensions);
+ const mouse = page.mouse;
+ await mouse.move(x + width - 4, y + height - 4);
+ await mouse.down();
+ await mouse.move(x + width + 100, y + height + 100);
+ await mouse.up();
+ const newDimensions = await page.evaluate(dimensions);
+ expect(newDimensions.width).toBe(width + 104);
+ expect(newDimensions.height).toBe(height + 104);
+ });
+ it('should scroll and click the button', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.click('#button-5');
+ expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked');
+ await page.click('#button-80');
+ expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked');
+ });
+ it('should double click the button', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.evaluate(() => {
+ window.double = false;
+ const button = document.querySelector('button');
+ button.addEventListener('dblclick', event => {
+ window.double = true;
+ });
+ });
+ const button = await page.$('button');
+ await button.click({ clickCount: 2 });
+ expect(await page.evaluate('double')).toBe(true);
+ expect(await page.evaluate('result')).toBe('Clicked');
+ });
+ it('should click a partially obscured button', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.evaluate(() => {
+ const button = document.querySelector('button');
+ button.textContent = 'Some really long text that will go offscreen';
+ button.style.position = 'absolute';
+ button.style.left = '368px';
+ });
+ await page.click('button');
+ expect(await page.evaluate(() => window.result)).toBe('Clicked');
+ });
+ it('should select the text with mouse', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
+ await page.keyboard.type(text);
+ await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
+ const {x, y} = await page.evaluate(dimensions);
+ await page.mouse.move(x + 2,y + 2);
+ await page.mouse.down();
+ await page.mouse.move(100,100);
+ await page.mouse.up();
+ expect(await page.evaluate(() => window.getSelection().toString())).toBe(text);
+ });
+ it('should select the text by triple clicking', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
+ await page.keyboard.type(text);
+ await page.click('textarea');
+ await page.click('textarea', {clickCount: 2});
+ await page.click('textarea', {clickCount: 3});
+ expect(await page.evaluate(() => window.getSelection().toString())).toBe(text);
+ });
+ it('should trigger hover state', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.hover('#button-6');
+ expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
+ await page.hover('#button-2');
+ expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
+ await page.hover('#button-91');
+ expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
+ });
+ it('should fire contextmenu event on right click', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.click('#button-8', {button: 'right'});
+ expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu');
+ });
+ it('should set modifier keys on click', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true));
+ const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'};
+ for (const modifier in modifiers) {
+ await page.keyboard.down(modifier);
+ await page.click('#button-3');
+ if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
+ fail(modifiers[modifier] + ' should be true');
+ await page.keyboard.up(modifier);
+ }
+ await page.click('#button-3');
+ for (const modifier in modifiers) {
+ if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
+ fail(modifiers[modifier] + ' should be false');
+ }
+ });
+ it('should specify repeat property', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
+ await page.keyboard.down('a');
+ expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
+ await page.keyboard.press('a');
+ expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
+
+ await page.keyboard.down('b');
+ expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
+ await page.keyboard.down('b');
+ expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
+
+ await page.keyboard.up('a');
+ await page.keyboard.down('a');
+ expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
+ });
+ // @see https://github.com/GoogleChrome/puppeteer/issues/206
+ it('should click links which cause navigation', async({page, server}) => {
+ await page.setContent(`empty.html`);
+ // This await should not hang.
+ await page.click('a');
+ });
+ it('should tween mouse movement', async({page, server}) => {
+ await page.mouse.move(100, 100);
+ await page.evaluate(() => {
+ window.result = [];
+ document.addEventListener('mousemove', event => {
+ window.result.push([event.clientX, event.clientY]);
+ });
+ });
+ await page.mouse.move(200, 300, {steps: 5});
+ expect(await page.evaluate('result')).toEqual([
+ [120, 140],
+ [140, 180],
+ [160, 220],
+ [180, 260],
+ [200, 300]
+ ]);
+ });
+ it('should tap the button', async({page, server}) => {
+ 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}) => {
+ await page.goto(server.PREFIX + '/input/touches.html');
+ const button = await page.$('button');
+ await button.tap();
+ expect(await page.evaluate(() => getResult())).toEqual(['Touchstart: 0', 'Touchend: 0']);
+ });
+ it('should click the button inside an iframe', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ await page.setContent('spacer
');
+ await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
+ const frame = page.frames()[1];
+ const button = await frame.$('button');
+ await button.click();
+ expect(await frame.evaluate(() => window.result)).toBe('Clicked');
+ });
+ it('should click the button with deviceScaleFactor set', async({page, server}) => {
+ await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5});
+ expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5);
+ await page.setContent('spacer
');
+ await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
+ const frame = page.frames()[1];
+ const button = await frame.$('button');
+ await button.click();
+ expect(await frame.evaluate(() => window.result)).toBe('Clicked');
+ });
+ it('should type all kinds of characters', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.focus('textarea');
+ const text = 'This text goes onto two lines.\nThis character is 嗨.';
+ await page.keyboard.type(text);
+ expect(await page.evaluate('result')).toBe(text);
+ });
+ it('should specify location', async({page, server}) => {
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ await page.evaluate(() => {
+ window.addEventListener('keydown', event => window.keyLocation = event.location, true);
+ });
+ const textarea = await page.$('textarea');
+
+ await textarea.press('Digit5');
+ expect(await page.evaluate('keyLocation')).toBe(0);
+
+ await textarea.press('ControlLeft');
+ expect(await page.evaluate('keyLocation')).toBe(1);
+
+ await textarea.press('ControlRight');
+ expect(await page.evaluate('keyLocation')).toBe(2);
+
+ await textarea.press('NumpadSubtract');
+ expect(await page.evaluate('keyLocation')).toBe(3);
+ });
+ it('should throw on unknown keys', async({page, server}) => {
+ let error = await page.keyboard.press('NotARealKey').catch(e => e);
+ expect(error.message).toBe('Unknown key: "NotARealKey"');
+
+ error = await page.keyboard.press('ё').catch(e => e);
+ expect(error && error.message).toBe('Unknown key: "ё"');
+
+ error = await page.keyboard.press('😊').catch(e => e);
+ expect(error && error.message).toBe('Unknown key: "😊"');
+ });
+ function dimensions() {
+ const rect = document.querySelector('textarea').getBoundingClientRect();
+ return {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height
+ };
+ }
+ });
+};
\ No newline at end of file
diff --git a/test/jshandle.spec.js b/test/jshandle.spec.js
new file mode 100644
index 00000000000..7ba74cfed78
--- /dev/null
+++ b/test/jshandle.spec.js
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+module.exports.addTests = function({testRunner, expect}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ describe('JSHandle.getProperty', function() {
+ it('should work', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => ({
+ one: 1,
+ two: 2,
+ three: 3
+ }));
+ const twoHandle = await aHandle.getProperty('two');
+ expect(await twoHandle.jsonValue()).toEqual(2);
+ });
+ });
+
+ describe('JSHandle.jsonValue', function() {
+ it('should work', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
+ const json = await aHandle.jsonValue();
+ expect(json).toEqual({foo: 'bar'});
+ });
+ it('should not work with dates', async({page, server}) => {
+ const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
+ const json = await dateHandle.jsonValue();
+ expect(json).toEqual({});
+ });
+ it('should throw for circular objects', async({page, server}) => {
+ const windowHandle = await page.evaluateHandle('window');
+ let error = null;
+ await windowHandle.jsonValue().catch(e => error = e);
+ expect(error.message).toContain('Object reference chain is too long');
+ });
+ });
+
+ describe('JSHandle.getProperties', function() {
+ it('should work', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => ({
+ foo: 'bar'
+ }));
+ const properties = await aHandle.getProperties();
+ const foo = properties.get('foo');
+ expect(foo).toBeTruthy();
+ expect(await foo.jsonValue()).toBe('bar');
+ });
+ it('should return even non-own properties', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => {
+ class A {
+ constructor() {
+ this.a = '1';
+ }
+ }
+ class B extends A {
+ constructor() {
+ super();
+ this.b = '2';
+ }
+ }
+ return new B();
+ });
+ const properties = await aHandle.getProperties();
+ expect(await properties.get('a').jsonValue()).toBe('1');
+ expect(await properties.get('b').jsonValue()).toBe('2');
+ });
+ });
+
+ describe('JSHandle.asElement', function() {
+ it('should work', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => document.body);
+ const element = aHandle.asElement();
+ expect(element).toBeTruthy();
+ });
+ it('should return null for non-elements', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => 2);
+ const element = aHandle.asElement();
+ expect(element).toBeFalsy();
+ });
+ it('should return ElementHandle for TextNodes', async({page, server}) => {
+ await page.setContent('ee!
');
+ const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
+ const element = aHandle.asElement();
+ expect(element).toBeTruthy();
+ expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element));
+ });
+ });
+
+ describe('JSHandle.toString', function() {
+ it('should work for primitives', async({page, server}) => {
+ const numberHandle = await page.evaluateHandle(() => 2);
+ expect(numberHandle.toString()).toBe('JSHandle:2');
+ const stringHandle = await page.evaluateHandle(() => 'a');
+ expect(stringHandle.toString()).toBe('JSHandle:a');
+ });
+ it('should work for complicated objects', async({page, server}) => {
+ const aHandle = await page.evaluateHandle(() => window);
+ expect(aHandle.toString()).toBe('JSHandle@object');
+ });
+ });
+};
\ No newline at end of file
diff --git a/test/network.spec.js b/test/network.spec.js
new file mode 100644
index 00000000000..ad48ff6f741
--- /dev/null
+++ b/test/network.spec.js
@@ -0,0 +1,188 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const {waitForEvents} = require('./utils');
+
+module.exports.addTests = function({testRunner, expect}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ describe('Network Events', function() {
+ it('Page.Events.Request', async({page, server}) => {
+ const requests = [];
+ page.on('request', request => requests.push(request));
+ await page.goto(server.EMPTY_PAGE);
+ expect(requests.length).toBe(1);
+ expect(requests[0].url()).toBe(server.EMPTY_PAGE);
+ expect(requests[0].resourceType()).toBe('document');
+ expect(requests[0].method()).toBe('GET');
+ expect(requests[0].response()).toBeTruthy();
+ expect(requests[0].frame() === page.mainFrame()).toBe(true);
+ expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
+ });
+ it('Page.Events.Request should report post data', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ server.setRoute('/post', (req, res) => res.end());
+ let request = null;
+ page.on('request', r => request = r);
+ await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
+ expect(request).toBeTruthy();
+ expect(request.postData()).toBe('{"foo":"bar"}');
+ });
+ it('Page.Events.Response', async({page, server}) => {
+ const responses = [];
+ page.on('response', response => responses.push(response));
+ await page.goto(server.EMPTY_PAGE);
+ expect(responses.length).toBe(1);
+ expect(responses[0].url()).toBe(server.EMPTY_PAGE);
+ expect(responses[0].status()).toBe(200);
+ expect(responses[0].ok()).toBe(true);
+ expect(responses[0].fromCache()).toBe(false);
+ expect(responses[0].fromServiceWorker()).toBe(false);
+ expect(responses[0].request()).toBeTruthy();
+ });
+
+ it('Response.fromCache()', async({page, server}) => {
+ const responses = new Map();
+ page.on('response', r => responses.set(r.url().split('/').pop(), r));
+
+ // Load and re-load to make sure it's cached.
+ await page.goto(server.PREFIX + '/cached/one-style.html');
+ await page.reload();
+
+ expect(responses.size).toBe(2);
+ expect(responses.get('one-style.html').status()).toBe(304);
+ expect(responses.get('one-style.html').fromCache()).toBe(false);
+ expect(responses.get('one-style.css').status()).toBe(200);
+ expect(responses.get('one-style.css').fromCache()).toBe(true);
+ });
+ it('Response.fromServiceWorker', async({page, server}) => {
+ const responses = new Map();
+ page.on('response', r => responses.set(r.url().split('/').pop(), r));
+
+ // Load and re-load to make sure serviceworker is installed and running.
+ await page.goto(server.PREFIX + '/serviceworkers/fetch/sw.html', {waitUntil: 'networkidle2'});
+ await page.evaluate(async() => await window.activationPromise);
+ await page.reload();
+
+ expect(responses.size).toBe(2);
+ expect(responses.get('sw.html').status()).toBe(200);
+ expect(responses.get('sw.html').fromServiceWorker()).toBe(true);
+ expect(responses.get('style.css').status()).toBe(200);
+ expect(responses.get('style.css').fromServiceWorker()).toBe(true);
+ });
+
+ it('Page.Events.Response should provide body', async({page, server}) => {
+ let response = null;
+ page.on('response', r => response = r);
+ await page.goto(server.PREFIX + '/simple.json');
+ expect(response).toBeTruthy();
+ expect(await response.text()).toBe('{"foo": "bar"}\n');
+ expect(await response.json()).toEqual({foo: 'bar'});
+ });
+ it('Page.Events.Response should not report body unless request is finished', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ // Setup server to trap request.
+ let serverResponse = null;
+ server.setRoute('/get', (req, res) => {
+ serverResponse = res;
+ res.write('hello ');
+ });
+ // Setup page to trap response.
+ let pageResponse = null;
+ let requestFinished = false;
+ page.on('response', r => pageResponse = r);
+ page.on('requestfinished', () => requestFinished = true);
+ // send request and wait for server response
+ await Promise.all([
+ page.evaluate(() => fetch('./get', { method: 'GET'})),
+ waitForEvents(page, 'response')
+ ]);
+
+ expect(serverResponse).toBeTruthy();
+ expect(pageResponse).toBeTruthy();
+ expect(pageResponse.status()).toBe(200);
+ expect(requestFinished).toBe(false);
+
+ const responseText = pageResponse.text();
+ // Write part of the response and wait for it to be flushed.
+ await new Promise(x => serverResponse.write('wor', x));
+ // Finish response.
+ await new Promise(x => serverResponse.end('ld!', x));
+ expect(await responseText).toBe('hello world!');
+ });
+ it('Page.Events.RequestFailed', async({page, server}) => {
+ await page.setRequestInterception(true);
+ page.on('request', request => {
+ if (request.url().endsWith('css'))
+ request.abort();
+ else
+ request.continue();
+ });
+ const failedRequests = [];
+ page.on('requestfailed', request => failedRequests.push(request));
+ await page.goto(server.PREFIX + '/one-style.html');
+ expect(failedRequests.length).toBe(1);
+ expect(failedRequests[0].url()).toContain('one-style.css');
+ expect(failedRequests[0].response()).toBe(null);
+ expect(failedRequests[0].resourceType()).toBe('stylesheet');
+ expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED');
+ expect(failedRequests[0].frame()).toBeTruthy();
+ });
+ it('Page.Events.RequestFinished', async({page, server}) => {
+ const requests = [];
+ page.on('requestfinished', request => requests.push(request));
+ await page.goto(server.EMPTY_PAGE);
+ expect(requests.length).toBe(1);
+ expect(requests[0].url()).toBe(server.EMPTY_PAGE);
+ expect(requests[0].response()).toBeTruthy();
+ expect(requests[0].frame() === page.mainFrame()).toBe(true);
+ expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
+ });
+ it('should fire events in proper order', async({page, server}) => {
+ const events = [];
+ page.on('request', request => events.push('request'));
+ page.on('response', response => events.push('response'));
+ page.on('requestfinished', request => events.push('requestfinished'));
+ await page.goto(server.EMPTY_PAGE);
+ expect(events).toEqual(['request', 'response', 'requestfinished']);
+ });
+ it('should support redirects', async({page, server}) => {
+ const events = [];
+ page.on('request', request => events.push(`${request.method()} ${request.url()}`));
+ page.on('response', response => events.push(`${response.status()} ${response.url()}`));
+ page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
+ page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
+ server.setRedirect('/foo.html', '/empty.html');
+ const FOO_URL = server.PREFIX + '/foo.html';
+ const response = await page.goto(FOO_URL);
+ expect(events).toEqual([
+ `GET ${FOO_URL}`,
+ `302 ${FOO_URL}`,
+ `DONE ${FOO_URL}`,
+ `GET ${server.EMPTY_PAGE}`,
+ `200 ${server.EMPTY_PAGE}`,
+ `DONE ${server.EMPTY_PAGE}`
+ ]);
+
+ // Check redirect chain
+ const redirectChain = response.request().redirectChain();
+ expect(redirectChain.length).toBe(1);
+ expect(redirectChain[0].url()).toContain('/foo.html');
+ });
+ });
+};
diff --git a/test/page.spec.js b/test/page.spec.js
index fe71e765187..7b2c345707e 100644
--- a/test/page.spec.js
+++ b/test/page.spec.js
@@ -15,13 +15,13 @@
*/
const fs = require('fs');
const path = require('path');
-const FrameUtils = require('./frame-utils');
-
-module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppeteer, PROJECT_ROOT) {
- const {describe, xdescribe, fdescribe} = runner;
- const {it, fit, xit} = runner;
- const {beforeAll, beforeEach, afterAll, afterEach} = runner;
+const utils = require('./utils');
+const {waitForEvents, getPDFPages, cssPixelsToInches} = require('./utils');
+module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, PROJECT_ROOT}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const DeviceDescriptors = require(path.join(PROJECT_ROOT, 'DeviceDescriptors'));
const iPhone = DeviceDescriptors['iPhone 6'];
const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape'];
@@ -46,58 +46,21 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
state.page = null;
});
- describe('Browser.version', function() {
- it('should return whether we are in headless', async({browser}) => {
- const version = await browser.version();
- expect(version.length).toBeGreaterThan(0);
- expect(version.startsWith('Headless')).toBe(headless);
- });
- });
+ const testFiles = [
+ 'browser.spec.js',
+ 'elementhandle.spec.js',
+ 'jshandle.spec.js',
+ 'tracing.spec.js',
+ 'frame.spec.js',
+ 'input.spec.js',
+ 'network.spec.js'
+ ];
- describe('Browser.userAgent', function() {
- it('should include WebKit', async({browser}) => {
- const userAgent = await browser.userAgent();
- expect(userAgent.length).toBeGreaterThan(0);
- expect(userAgent).toContain('WebKit');
- });
- });
-
- describe('Browser.process', function() {
- it('should return child_process instance', async function({browser}) {
- const process = await browser.process();
- expect(process.pid).toBeGreaterThan(0);
- const browserWSEndpoint = browser.wsEndpoint();
- const remoteBrowser = await puppeteer.connect({browserWSEndpoint});
- expect(remoteBrowser.process()).toBe(null);
- await remoteBrowser.disconnect();
- });
- });
-
- describe('Browser.Events.disconnected', function() {
- it('should emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => {
- const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
- const browserWSEndpoint = originalBrowser.wsEndpoint();
- const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint});
- const remoteBrowser2 = await puppeteer.connect({browserWSEndpoint});
-
- let disconnectedOriginal = 0;
- let disconnectedRemote1 = 0;
- let disconnectedRemote2 = 0;
- originalBrowser.on('disconnected', () => ++disconnectedOriginal);
- remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
- remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
-
- await remoteBrowser2.disconnect();
- expect(disconnectedOriginal).toBe(0);
- expect(disconnectedRemote1).toBe(0);
- expect(disconnectedRemote2).toBe(1);
-
- await originalBrowser.close();
- expect(disconnectedOriginal).toBe(1);
- expect(disconnectedRemote1).toBe(1);
- expect(disconnectedRemote2).toBe(1);
- });
- });
+ testFiles
+ .map(file => path.join(__dirname, file))
+ .forEach(file =>
+ require(file).addTests({testRunner, expect, defaultBrowserOptions, PROJECT_ROOT, puppeteer})
+ );
describe('Page.close', function() {
it('should reject all promises when page is closed', async({browser}) => {
@@ -239,7 +202,7 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
expect(error.message).toContain('JSHandle is disposed');
});
it('should throw if elementHandles are from other frames', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const bodyHandle = await page.frames()[1].$('body');
let error = null;
await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => error = e);
@@ -310,449 +273,6 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
- describe('JSHandle.getProperty', function() {
- it('should work', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => ({
- one: 1,
- two: 2,
- three: 3
- }));
- const twoHandle = await aHandle.getProperty('two');
- expect(await twoHandle.jsonValue()).toEqual(2);
- });
- });
-
- describe('JSHandle.jsonValue', function() {
- it('should work', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
- const json = await aHandle.jsonValue();
- expect(json).toEqual({foo: 'bar'});
- });
- it('should not work with dates', async({page, server}) => {
- const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
- const json = await dateHandle.jsonValue();
- expect(json).toEqual({});
- });
- it('should throw for circular objects', async({page, server}) => {
- const windowHandle = await page.evaluateHandle('window');
- let error = null;
- await windowHandle.jsonValue().catch(e => error = e);
- expect(error.message).toContain('Object reference chain is too long');
- });
- });
-
- describe('JSHandle.getProperties', function() {
- it('should work', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => ({
- foo: 'bar'
- }));
- const properties = await aHandle.getProperties();
- const foo = properties.get('foo');
- expect(foo).toBeTruthy();
- expect(await foo.jsonValue()).toBe('bar');
- });
- it('should return even non-own properties', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => {
- class A {
- constructor() {
- this.a = '1';
- }
- }
- class B extends A {
- constructor() {
- super();
- this.b = '2';
- }
- }
- return new B();
- });
- const properties = await aHandle.getProperties();
- expect(await properties.get('a').jsonValue()).toBe('1');
- expect(await properties.get('b').jsonValue()).toBe('2');
- });
- });
-
- describe('JSHandle.asElement', function() {
- it('should work', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => document.body);
- const element = aHandle.asElement();
- expect(element).toBeTruthy();
- });
- it('should return null for non-elements', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => 2);
- const element = aHandle.asElement();
- expect(element).toBeFalsy();
- });
- it('should return ElementHandle for TextNodes', async({page, server}) => {
- await page.setContent('ee!
');
- const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
- const element = aHandle.asElement();
- expect(element).toBeTruthy();
- expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element));
- });
- });
-
- describe('JSHandle.toString', function() {
- it('should work for primitives', async({page, server}) => {
- const numberHandle = await page.evaluateHandle(() => 2);
- expect(numberHandle.toString()).toBe('JSHandle:2');
- const stringHandle = await page.evaluateHandle(() => 'a');
- expect(stringHandle.toString()).toBe('JSHandle:a');
- });
- it('should work for complicated objects', async({page, server}) => {
- const aHandle = await page.evaluateHandle(() => window);
- expect(aHandle.toString()).toBe('JSHandle@object');
- });
- });
-
- describe('Frame.context', function() {
- it('should work', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- expect(page.frames().length).toBe(2);
- const [frame1, frame2] = page.frames();
- const context1 = await frame1.executionContext();
- const context2 = await frame2.executionContext();
- expect(context1).toBeTruthy();
- expect(context2).toBeTruthy();
- expect(context1 !== context2).toBeTruthy();
- expect(context1.frame()).toBe(frame1);
- expect(context2.frame()).toBe(frame2);
-
- await Promise.all([
- context1.evaluate(() => window.a = 1),
- context2.evaluate(() => window.a = 2)
- ]);
- const [a1, a2] = await Promise.all([
- context1.evaluate(() => window.a),
- context2.evaluate(() => window.a)
- ]);
- expect(a1).toBe(1);
- expect(a2).toBe(2);
- });
- });
-
- describe('Frame.evaluateHandle', function() {
- it('should work', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const mainFrame = page.mainFrame();
- const windowHandle = await mainFrame.evaluateHandle(() => window);
- expect(windowHandle).toBeTruthy();
- });
- });
-
- describe('Frame.evaluate', function() {
- it('should have different execution contexts', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- expect(page.frames().length).toBe(2);
- const frame1 = page.frames()[0];
- const frame2 = page.frames()[1];
- await frame1.evaluate(() => window.FOO = 'foo');
- await frame2.evaluate(() => window.FOO = 'bar');
- expect(await frame1.evaluate(() => window.FOO)).toBe('foo');
- expect(await frame2.evaluate(() => window.FOO)).toBe('bar');
- });
- it('should execute after cross-site navigation', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const mainFrame = page.mainFrame();
- expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost');
- await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
- expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
- });
- });
-
- describe('Frame.waitForFunction', function() {
- it('should accept a string', async({page, server}) => {
- const watchdog = page.waitForFunction('window.__FOO === 1');
- await page.evaluate(() => window.__FOO = 1);
- await watchdog;
- });
- it('should poll on interval', async({page, server}) => {
- let success = false;
- const startTime = Date.now();
- const polling = 100;
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling})
- .then(() => success = true);
- await page.evaluate(() => window.__FOO = 'hit');
- expect(success).toBe(false);
- await page.evaluate(() => document.body.appendChild(document.createElement('div')));
- await watchdog;
- expect(Date.now() - startTime).not.toBeLessThan(polling / 2);
- });
- it('should poll on mutation', async({page, server}) => {
- let success = false;
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'})
- .then(() => success = true);
- await page.evaluate(() => window.__FOO = 'hit');
- expect(success).toBe(false);
- await page.evaluate(() => document.body.appendChild(document.createElement('div')));
- await watchdog;
- });
- it('should poll on raf', async({page, server}) => {
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'});
- await page.evaluate(() => window.__FOO = 'hit');
- await watchdog;
- });
- it('should throw on bad polling value', async({page, server}) => {
- let error = null;
- try {
- await page.waitForFunction(() => !!document.body, {polling: 'unknown'});
- } catch (e) {
- error = e;
- }
- expect(error).toBeTruthy();
- expect(error.message).toContain('polling');
- });
- it('should throw negative polling interval', async({page, server}) => {
- let error = null;
- try {
- await page.waitForFunction(() => !!document.body, {polling: -10});
- } catch (e) {
- error = e;
- }
- expect(error).toBeTruthy();
- expect(error.message).toContain('Cannot poll with non-positive interval');
- });
- it('should return the success value as a JSHandle', async({page}) => {
- expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
- });
- it('should return the window as a success value', async({ page }) => {
- expect(await page.waitForFunction(() => window)).toBeTruthy();
- });
- it('should accept ElementHandle arguments', async({page}) => {
- await page.setContent('');
- const div = await page.$('div');
- let resolved = false;
- const waitForFunction = page.waitForFunction(element => !element.parentElement, {}, div).then(() => resolved = true);
- expect(resolved).toBe(false);
- await page.evaluate(element => element.remove(), div);
- await waitForFunction;
- });
- });
-
- describe('Frame.waitForSelector', function() {
- const addElement = tag => document.body.appendChild(document.createElement(tag));
-
- it('should immediately resolve promise if node exists', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const frame = page.mainFrame();
- await frame.waitForSelector('*');
- await frame.evaluate(addElement, 'div');
- await frame.waitForSelector('div');
- });
-
- it('should resolve promise when node is added', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const frame = page.mainFrame();
- const watchdog = frame.waitForSelector('div');
- await frame.evaluate(addElement, 'br');
- await frame.evaluate(addElement, 'div');
- const eHandle = await watchdog;
- const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
- expect(tagName).toBe('DIV');
- });
-
- it('should work when node is added through innerHTML', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const watchdog = page.waitForSelector('h3 div');
- await page.evaluate(addElement, 'span');
- await page.evaluate(() => document.querySelector('span').innerHTML = '');
- await watchdog;
- });
-
- it('Page.waitForSelector is shortcut for main frame', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- const otherFrame = page.frames()[1];
- const watchdog = page.waitForSelector('div');
- await otherFrame.evaluate(addElement, 'div');
- await page.evaluate(addElement, 'div');
- const eHandle = await watchdog;
- expect(eHandle.executionContext().frame()).toBe(page.mainFrame());
- });
-
- it('should run in specified frame', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
- const frame1 = page.frames()[1];
- const frame2 = page.frames()[2];
- const waitForSelectorPromise = frame2.waitForSelector('div');
- await frame1.evaluate(addElement, 'div');
- await frame2.evaluate(addElement, 'div');
- const eHandle = await waitForSelectorPromise;
- expect(eHandle.executionContext().frame()).toBe(frame2);
- });
-
- it('should throw if evaluation failed', async({page, server}) => {
- await page.evaluateOnNewDocument(function() {
- document.querySelector = null;
- });
- await page.goto(server.EMPTY_PAGE);
- let error = null;
- await page.waitForSelector('*').catch(e => error = e);
- expect(error.message).toContain('document.querySelector is not a function');
- });
- it('should throw when frame is detached', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- const frame = page.frames()[1];
- let waitError = null;
- const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
- await FrameUtils.detachFrame(page, 'frame1');
- await waitPromise;
- expect(waitError).toBeTruthy();
- expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
- });
- it('should survive cross-process navigation', async({page, server}) => {
- let boxFound = false;
- const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
- await page.goto(server.EMPTY_PAGE);
- expect(boxFound).toBe(false);
- await page.reload();
- expect(boxFound).toBe(false);
- await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
- await waitForSelector;
- expect(boxFound).toBe(true);
- });
- it('should wait for visible', async({page, server}) => {
- let divFound = false;
- const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true);
- await page.setContent(`1
`);
- expect(divFound).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
- expect(divFound).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
- expect(await waitForSelector).toBe(true);
- expect(divFound).toBe(true);
- });
- it('should wait for visible recursively', async({page, server}) => {
- let divVisible = false;
- const waitForSelector = page.waitForSelector('div#inner', {visible: true}).then(() => divVisible = true);
- await page.setContent(``);
- expect(divVisible).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
- expect(divVisible).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
- expect(await waitForSelector).toBe(true);
- expect(divVisible).toBe(true);
- });
- it('hidden should wait for visibility: hidden', async({page, server}) => {
- let divHidden = false;
- await page.setContent(``);
- const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
- await page.waitForSelector('div'); // do a round trip
- expect(divHidden).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
- expect(await waitForSelector).toBe(true);
- expect(divHidden).toBe(true);
- });
- it('hidden should wait for display: none', async({page, server}) => {
- let divHidden = false;
- await page.setContent(``);
- const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
- await page.waitForSelector('div'); // do a round trip
- expect(divHidden).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
- expect(await waitForSelector).toBe(true);
- expect(divHidden).toBe(true);
- });
- it('hidden should wait for removal', async({page, server}) => {
- await page.setContent(``);
- let divRemoved = false;
- const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divRemoved = true);
- await page.waitForSelector('div'); // do a round trip
- expect(divRemoved).toBe(false);
- await page.evaluate(() => document.querySelector('div').remove());
- expect(await waitForSelector).toBe(true);
- expect(divRemoved).toBe(true);
- });
- it('should respect timeout', async({page, server}) => {
- let error = null;
- await page.waitForSelector('div', {timeout: 10}).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting failed: timeout');
- });
-
- it('should respond to node attribute mutation', async({page, server}) => {
- let divFound = false;
- const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
- await page.setContent(``);
- expect(divFound).toBe(false);
- await page.evaluate(() => document.querySelector('div').className = 'zombo');
- expect(await waitForSelector).toBe(true);
- });
- it('should return the element handle', async({page, server}) => {
- const waitForSelector = page.waitForSelector('.zombo');
- await page.setContent(`anything
`);
- expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
- });
- });
-
- describe('Frame.waitForXPath', function() {
- const addElement = tag => document.body.appendChild(document.createElement(tag));
-
- it('should support some fancy xpath', async({page, server}) => {
- await page.setContent(`red herring
hello world
`);
- const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]');
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
- });
- it('should run in specified frame', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
- const frame1 = page.frames()[1];
- const frame2 = page.frames()[2];
- const waitForXPathPromise = frame2.waitForXPath('//div');
- await frame1.evaluate(addElement, 'div');
- await frame2.evaluate(addElement, 'div');
- const eHandle = await waitForXPathPromise;
- expect(eHandle.executionContext().frame()).toBe(frame2);
- });
- it('should throw if evaluation failed', async({page, server}) => {
- await page.evaluateOnNewDocument(function() {
- document.evaluate = null;
- });
- await page.goto(server.EMPTY_PAGE);
- let error = null;
- await page.waitForXPath('*').catch(e => error = e);
- expect(error.message).toContain('document.evaluate is not a function');
- });
- it('should throw when frame is detached', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- const frame = page.frames()[1];
- let waitError = null;
- const waitPromise = frame.waitForXPath('//*[@class="box"]').catch(e => waitError = e);
- await FrameUtils.detachFrame(page, 'frame1');
- await waitPromise;
- expect(waitError).toBeTruthy();
- expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
- });
- it('hidden should wait for display: none', async({page, server}) => {
- let divHidden = false;
- await page.setContent(``);
- const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true);
- await page.waitForXPath('//div'); // do a round trip
- expect(divHidden).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
- expect(await waitForXPath).toBe(true);
- expect(divHidden).toBe(true);
- });
- it('should return the element handle', async({page, server}) => {
- const waitForXPath = page.waitForXPath('//*[@class="zombo"]');
- await page.setContent(`anything
`);
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
- });
- it('should allow you to select a text node', async({page, server}) => {
- await page.setContent(`some text
`);
- const text = await page.waitForXPath('//div/text()');
- expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */);
- });
- it('should allow you to select an element with single slash', async({page, server}) => {
- await page.setContent(`some text
`);
- const waitForXPath = page.waitForXPath('/html/body/div');
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
- });
- });
-
describe('Page.waitFor', function() {
it('should wait for selector', async({page, server}) => {
let found = false;
@@ -1555,7 +1075,7 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
expect(requests[0].frame() === page.mainFrame()).toBe(true);
@@ -1569,28 +1089,28 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
describe('Frame Management', function() {
it('should handle nested frames', async({page, server}) => {
await page.goto(server.PREFIX + '/frames/nested-frames.html');
- expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt');
+ expect(utils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt');
});
it('should send events when frames are manipulated dynamically', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
// validate frameattached events
const attachedFrames = [];
page.on('frameattached', frame => attachedFrames.push(frame));
- await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html');
+ await utils.attachFrame(page, 'frame1', './assets/frame.html');
expect(attachedFrames.length).toBe(1);
expect(attachedFrames[0].url()).toContain('/assets/frame.html');
// validate framenavigated events
const navigatedFrames = [];
page.on('framenavigated', frame => navigatedFrames.push(frame));
- await FrameUtils.navigateFrame(page, 'frame1', './empty.html');
+ await utils.navigateFrame(page, 'frame1', './empty.html');
expect(navigatedFrames.length).toBe(1);
expect(navigatedFrames[0].url()).toBe(server.EMPTY_PAGE);
// validate framedetached events
const detachedFrames = [];
page.on('framedetached', frame => detachedFrames.push(frame));
- await FrameUtils.detachFrame(page, 'frame1');
+ await utils.detachFrame(page, 'frame1');
expect(detachedFrames.length).toBe(1);
expect(detachedFrames[0].isDetached()).toBe(true);
});
@@ -1628,7 +1148,7 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
expect(navigatedFrames.length).toBe(1);
});
it('should report frame.name()', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
await page.evaluate(url => {
const frame = document.createElement('iframe');
frame.name = 'theFrameName';
@@ -1641,8 +1161,8 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
expect(page.frames()[2].name()).toBe('theFrameName');
});
it('should report frame.parent()', async({page, server}) => {
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
expect(page.frames()[0].parentFrame()).toBe(null);
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
@@ -1726,674 +1246,6 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
- describe('ElementHandle.boundingBox', function() {
- it('should work', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.goto(server.PREFIX + '/grid.html');
- const elementHandle = await page.$('.box:nth-of-type(13)');
- const box = await elementHandle.boundingBox();
- expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
- });
- it('should handle nested frames', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.goto(server.PREFIX + '/frames/nested-frames.html');
- const nestedFrame = page.frames()[1].childFrames()[1];
- const elementHandle = await nestedFrame.$('div');
- const box = await elementHandle.boundingBox();
- expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
- });
- it('should return null for invisible elements', async({page, server}) => {
- await page.setContent('hi
');
- const element = await page.$('div');
- expect(await element.boundingBox()).toBe(null);
- });
- it('should force a layout', async({page, server}) => {
- await page.setViewport({ width: 500, height: 500 });
- await page.setContent('hello
');
- const elementHandle = await page.$('div');
- await page.evaluate(element => element.style.height = '200px', elementHandle);
- const box = await elementHandle.boundingBox();
- expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 });
- });
- });
-
- describe('ElementHandle.contentFrame', function() {
- it('should work', async({page,server}) => {
- await page.goto(server.EMPTY_PAGE);
- await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- const elementHandle = await page.$('#frame1');
- const frame = await elementHandle.contentFrame();
- expect(frame).toBe(page.frames()[1]);
- });
- });
-
- describe('ElementHandle.click', function() {
- it('should work', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- const button = await page.$('button');
- await button.click();
- expect(await page.evaluate(() => result)).toBe('Clicked');
- });
- it('should work for Shadow DOM v1', async({page, server}) => {
- await page.goto(server.PREFIX + '/shadow.html');
- const buttonHandle = await page.evaluateHandle(() => button);
- await buttonHandle.click();
- expect(await page.evaluate(() => clicked)).toBe(true);
- });
- it('should work for TextNodes', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
- let error = null;
- await buttonTextNode.click().catch(err => error = err);
- expect(error.message).toBe('Node is not of type HTMLElement');
- });
- it('should throw for detached nodes', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- const button = await page.$('button');
- await page.evaluate(button => button.remove(), button);
- let error = null;
- await button.click().catch(err => error = err);
- expect(error.message).toBe('Node is detached from document');
- });
- it('should throw for hidden nodes', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- const button = await page.$('button');
- await page.evaluate(button => button.style.display = 'none', button);
- const error = await button.click().catch(err => err);
- expect(error.message).toBe('Node is either not visible or not an HTMLElement');
- });
- it('should throw for recursively hidden nodes', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- const button = await page.$('button');
- await page.evaluate(button => button.parentElement.style.display = 'none', button);
- const error = await button.click().catch(err => err);
- expect(error.message).toBe('Node is either not visible or not an HTMLElement');
- });
- it('should throw for
elements', async({page, server}) => {
- await page.setContent('hello
goodbye');
- const br = await page.$('br');
- const error = await br.click().catch(err => err);
- expect(error.message).toBe('Node is either not visible or not an HTMLElement');
- });
- });
-
- describe('ElementHandle.hover', function() {
- it('should work', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/scrollable.html');
- const button = await page.$('#button-6');
- await button.hover();
- expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
- });
- });
-
- describe('ElementHandle.screenshot', function() {
- it('should work', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.goto(server.PREFIX + '/grid.html');
- await page.evaluate(() => window.scrollBy(50, 100));
- const elementHandle = await page.$('.box:nth-of-type(3)');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
- });
- it('should take into account padding and border', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.setContent(`
- something above
-
-
- `);
- const elementHandle = await page.$('div');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
- });
- it('should capture full element when larger than viewport', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
-
- await page.setContent(`
- something above
-
-
- `);
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
-
- expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
- });
- it('should scroll element into view', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.setContent(`
- something above
-
-
-
- `);
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
- });
- it('should work with a rotated element', async({page, server}) => {
- await page.setViewport({width: 500, height: 500});
- await page.setContent(`
`);
- const elementHandle = await page.$('div');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-rotate.png');
- });
- it('should fail to screenshot a detached element', async({page, server}) => {
- await page.setContent('remove this
');
- const elementHandle = await page.$('h1');
- await page.evaluate(element => element.remove(), elementHandle);
- const screenshotError = await elementHandle.screenshot().catch(error => error);
- expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement');
- });
- });
-
- describe('ElementHandle.$', function() {
- it('should query existing element', async({page, server}) => {
- await page.goto(server.PREFIX + '/playground.html');
- await page.setContent('');
- const html = await page.$('html');
- const second = await html.$('.second');
- const inner = await second.$('.inner');
- const content = await page.evaluate(e => e.textContent, inner);
- expect(content).toBe('A');
- });
-
- it('should return null for non-existing element', async({page, server}) => {
- await page.setContent('');
- const html = await page.$('html');
- const second = await html.$('.third');
- expect(second).toBe(null);
- });
- });
-
- describe('ElementHandle.$$', function() {
- it('should query existing elements', async({page, server}) => {
- await page.setContent('A
B
');
- const html = await page.$('html');
- const elements = await html.$$('div');
- expect(elements.length).toBe(2);
- const promises = elements.map(element => page.evaluate(e => e.textContent, element));
- expect(await Promise.all(promises)).toEqual(['A', 'B']);
- });
-
- it('should return empty array for non-existing elements', async({page, server}) => {
- await page.setContent('A
B');
- const html = await page.$('html');
- const elements = await html.$$('div');
- expect(elements.length).toBe(0);
- });
- });
-
-
- describe('ElementHandle.$x', function() {
- it('should query existing element', async({page, server}) => {
- await page.goto(server.PREFIX + '/playground.html');
- await page.setContent('');
- const html = await page.$('html');
- const second = await html.$x(`./body/div[contains(@class, 'second')]`);
- const inner = await second[0].$x(`./div[contains(@class, 'inner')]`);
- const content = await page.evaluate(e => e.textContent, inner[0]);
- expect(content).toBe('A');
- });
-
- it('should return null for non-existing element', async({page, server}) => {
- await page.setContent('');
- const html = await page.$('html');
- const second = await html.$x(`/div[contains(@class, 'third')]`);
- expect(second).toEqual([]);
- });
- });
-
- describe('input', function() {
- it('should click the button', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- await page.click('button');
- expect(await page.evaluate(() => result)).toBe('Clicked');
- });
-
- it('should click on checkbox input and toggle', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/checkbox.html');
- expect(await page.evaluate(() => result.check)).toBe(null);
- await page.click('input#agree');
- expect(await page.evaluate(() => result.check)).toBe(true);
- expect(await page.evaluate(() => result.events)).toEqual([
- 'mouseover',
- 'mouseenter',
- 'mousemove',
- 'mousedown',
- 'mouseup',
- 'click',
- 'input',
- 'change',
- ]);
- await page.click('input#agree');
- expect(await page.evaluate(() => result.check)).toBe(false);
- });
-
- it('should click on checkbox label and toggle', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/checkbox.html');
- expect(await page.evaluate(() => result.check)).toBe(null);
- await page.click('label[for="agree"]');
- expect(await page.evaluate(() => result.check)).toBe(true);
- expect(await page.evaluate(() => result.events)).toEqual([
- 'click',
- 'input',
- 'change',
- ]);
- await page.click('label[for="agree"]');
- expect(await page.evaluate(() => result.check)).toBe(false);
- });
-
- it('should fail to click a missing button', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- let error = null;
- await page.click('button.does-not-exist').catch(e => error = e);
- expect(error.message).toBe('No node found for selector: button.does-not-exist');
- });
- // @see https://github.com/GoogleChrome/puppeteer/issues/161
- it('should not hang with touch-enabled viewports', async({page, server}) => {
- await page.setViewport(iPhone.viewport);
- await page.mouse.down();
- await page.mouse.move(100, 10);
- await page.mouse.up();
- });
- it('should type into the textarea', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
-
- const textarea = await page.$('textarea');
- await textarea.type('Type in this text!');
- expect(await page.evaluate(() => result)).toBe('Type in this text!');
- });
- it('should click the button after navigation ', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- await page.click('button');
- await page.goto(server.PREFIX + '/input/button.html');
- await page.click('button');
- expect(await page.evaluate(() => result)).toBe('Clicked');
- });
- it('should upload the file', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/fileupload.html');
- const filePath = path.relative(process.cwd(), __dirname + '/assets/file-to-upload.txt');
- const input = await page.$('input');
- await input.uploadFile(filePath);
- expect(await page.evaluate(e => e.files[0].name, input)).toBe('file-to-upload.txt');
- expect(await page.evaluate(e => {
- const reader = new FileReader();
- const promise = new Promise(fulfill => reader.onload = fulfill);
- reader.readAsText(e.files[0]);
- return promise.then(() => reader.result);
- }, input)).toBe('contents of the file');
- });
- it('should move with the arrow keys', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.type('textarea', 'Hello World!');
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
- for (let i = 0; i < 'World!'.length; i++)
- page.keyboard.press('ArrowLeft');
- await page.keyboard.type('inserted ');
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
- page.keyboard.down('Shift');
- for (let i = 0; i < 'inserted '.length; i++)
- page.keyboard.press('ArrowLeft');
- page.keyboard.up('Shift');
- await page.keyboard.press('Backspace');
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
- });
- it('should send a character with ElementHandle.press', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- const textarea = await page.$('textarea');
- await textarea.press('a', {text: 'f'});
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
-
- await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
-
- await textarea.press('a', {text: 'y'});
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f');
- });
- it('should send a character with sendCharacter', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- await page.keyboard.sendCharacter('嗨');
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
- await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
- await page.keyboard.sendCharacter('a');
- expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
- });
- it('should report shiftKey', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/keyboard.html');
- const keyboard = page.keyboard;
- const codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17};
- for (const modifierKey in codeForKey) {
- await keyboard.down(modifierKey);
- expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
- await keyboard.down('!');
- // Shift+! will generate a keypress
- if (modifierKey === 'Shift')
- expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 33 [' + modifierKey + ']');
- else
- expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
-
- await keyboard.up('!');
- expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
- await keyboard.up(modifierKey);
- expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
- }
- });
- it('should report multiple modifiers', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/keyboard.html');
- const keyboard = page.keyboard;
- await keyboard.down('Control');
- expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
- await keyboard.down('Meta');
- expect(await page.evaluate(() => getResult())).toBe('Keydown: Meta MetaLeft 91 [Control Meta]');
- await keyboard.down(';');
- expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Control Meta]');
- await keyboard.up(';');
- expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Control Meta]');
- await keyboard.up('Control');
- expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Meta]');
- await keyboard.up('Meta');
- expect(await page.evaluate(() => getResult())).toBe('Keyup: Meta MetaLeft 91 []');
- });
- it('should send proper codes while typing', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/keyboard.html');
- await page.keyboard.type('!');
- expect(await page.evaluate(() => getResult())).toBe(
- [ 'Keydown: ! Digit1 49 []',
- 'Keypress: ! Digit1 33 33 33 []',
- 'Keyup: ! Digit1 49 []'].join('\n'));
- await page.keyboard.type('^');
- expect(await page.evaluate(() => getResult())).toBe(
- [ 'Keydown: ^ Digit6 54 []',
- 'Keypress: ^ Digit6 94 94 94 []',
- 'Keyup: ^ Digit6 54 []'].join('\n'));
- });
- it('should send proper codes while typing with shift', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/keyboard.html');
- const keyboard = page.keyboard;
- await keyboard.down('Shift');
- await page.keyboard.type('~');
- expect(await page.evaluate(() => getResult())).toBe(
- [ 'Keydown: Shift ShiftLeft 16 [Shift]',
- 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
- 'Keypress: ~ Backquote 126 126 126 [Shift]', // 126 is ~ charCode
- 'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
- await keyboard.up('Shift');
- });
- it('should not type canceled events', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- await page.evaluate(() => {
- window.addEventListener('keydown', event => {
- event.stopPropagation();
- event.stopImmediatePropagation();
- if (event.key === 'l')
- event.preventDefault();
- if (event.key === 'o')
- Promise.resolve().then(() => event.preventDefault());
- }, false);
- });
- await page.keyboard.type('Hello World!');
- expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
- });
- it('keyboard.modifiers()', async({page, server}) => {
- const keyboard = page.keyboard;
- expect(keyboard._modifiers).toBe(0);
- await keyboard.down('Shift');
- expect(keyboard._modifiers).toBe(8);
- await keyboard.down('Alt');
- expect(keyboard._modifiers).toBe(9);
- await keyboard.up('Shift');
- await keyboard.up('Alt');
- expect(keyboard._modifiers).toBe(0);
- });
- it('should resize the textarea', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- const {x, y, width, height} = await page.evaluate(dimensions);
- const mouse = page.mouse;
- await mouse.move(x + width - 4, y + height - 4);
- await mouse.down();
- await mouse.move(x + width + 100, y + height + 100);
- await mouse.up();
- const newDimensions = await page.evaluate(dimensions);
- expect(newDimensions.width).toBe(width + 104);
- expect(newDimensions.height).toBe(height + 104);
- });
- it('should scroll and click the button', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/scrollable.html');
- await page.click('#button-5');
- expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked');
- await page.click('#button-80');
- expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked');
- });
- it('should double click the button', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- await page.evaluate(() => {
- window.double = false;
- const button = document.querySelector('button');
- button.addEventListener('dblclick', event => {
- window.double = true;
- });
- });
- const button = await page.$('button');
- await button.click({ clickCount: 2 });
- expect(await page.evaluate('double')).toBe(true);
- expect(await page.evaluate('result')).toBe('Clicked');
- });
- it('should click a partially obscured button', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/button.html');
- await page.evaluate(() => {
- const button = document.querySelector('button');
- button.textContent = 'Some really long text that will go offscreen';
- button.style.position = 'absolute';
- button.style.left = '368px';
- });
- await page.click('button');
- expect(await page.evaluate(() => window.result)).toBe('Clicked');
- });
- it('should select the text with mouse', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
- await page.keyboard.type(text);
- await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
- const {x, y} = await page.evaluate(dimensions);
- await page.mouse.move(x + 2,y + 2);
- await page.mouse.down();
- await page.mouse.move(100,100);
- await page.mouse.up();
- expect(await page.evaluate(() => window.getSelection().toString())).toBe(text);
- });
- it('should select the text by triple clicking', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
- await page.keyboard.type(text);
- await page.click('textarea');
- await page.click('textarea', {clickCount: 2});
- await page.click('textarea', {clickCount: 3});
- expect(await page.evaluate(() => window.getSelection().toString())).toBe(text);
- });
- it('should trigger hover state', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/scrollable.html');
- await page.hover('#button-6');
- expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
- await page.hover('#button-2');
- expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
- await page.hover('#button-91');
- expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
- });
- it('should fire contextmenu event on right click', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/scrollable.html');
- await page.click('#button-8', {button: 'right'});
- expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu');
- });
- it('should set modifier keys on click', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/scrollable.html');
- await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true));
- const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'};
- for (const modifier in modifiers) {
- await page.keyboard.down(modifier);
- await page.click('#button-3');
- if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
- fail(modifiers[modifier] + ' should be true');
- await page.keyboard.up(modifier);
- }
- await page.click('#button-3');
- for (const modifier in modifiers) {
- if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
- fail(modifiers[modifier] + ' should be false');
- }
- });
- it('should specify repeat property', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
- await page.keyboard.down('a');
- expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
- await page.keyboard.press('a');
- expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
-
- await page.keyboard.down('b');
- expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
- await page.keyboard.down('b');
- expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
-
- await page.keyboard.up('a');
- await page.keyboard.down('a');
- expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
- });
- // @see https://github.com/GoogleChrome/puppeteer/issues/206
- it('should click links which cause navigation', async({page, server}) => {
- await page.setContent(`empty.html`);
- // This await should not hang.
- await page.click('a');
- });
- it('should tween mouse movement', async({page, server}) => {
- await page.mouse.move(100, 100);
- await page.evaluate(() => {
- window.result = [];
- document.addEventListener('mousemove', event => {
- window.result.push([event.clientX, event.clientY]);
- });
- });
- await page.mouse.move(200, 300, {steps: 5});
- expect(await page.evaluate('result')).toEqual([
- [120, 140],
- [140, 180],
- [160, 220],
- [180, 260],
- [200, 300]
- ]);
- });
- it('should tap the button', async({page, server}) => {
- 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}) => {
- await page.goto(server.PREFIX + '/input/touches.html');
- const button = await page.$('button');
- await button.tap();
- expect(await page.evaluate(() => getResult())).toEqual(['Touchstart: 0', 'Touchend: 0']);
- });
- it('should click the button inside an iframe', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- await page.setContent('spacer
');
- await FrameUtils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
- const frame = page.frames()[1];
- const button = await frame.$('button');
- await button.click();
- expect(await frame.evaluate(() => window.result)).toBe('Clicked');
- });
- it('should click the button with deviceScaleFactor set', async({page, server}) => {
- await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5});
- expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5);
- await page.setContent('spacer
');
- await FrameUtils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html');
- const frame = page.frames()[1];
- const button = await frame.$('button');
- await button.click();
- expect(await frame.evaluate(() => window.result)).toBe('Clicked');
- });
- it('should type all kinds of characters', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.focus('textarea');
- const text = 'This text goes onto two lines.\nThis character is 嗨.';
- await page.keyboard.type(text);
- expect(await page.evaluate('result')).toBe(text);
- });
- it('should specify location', async({page, server}) => {
- await page.goto(server.PREFIX + '/input/textarea.html');
- await page.evaluate(() => {
- window.addEventListener('keydown', event => window.keyLocation = event.location, true);
- });
- const textarea = await page.$('textarea');
-
- await textarea.press('Digit5');
- expect(await page.evaluate('keyLocation')).toBe(0);
-
- await textarea.press('ControlLeft');
- expect(await page.evaluate('keyLocation')).toBe(1);
-
- await textarea.press('ControlRight');
- expect(await page.evaluate('keyLocation')).toBe(2);
-
- await textarea.press('NumpadSubtract');
- expect(await page.evaluate('keyLocation')).toBe(3);
- });
- it('should throw on unknown keys', async({page, server}) => {
- let error = await page.keyboard.press('NotARealKey').catch(e => e);
- expect(error.message).toBe('Unknown key: "NotARealKey"');
-
- error = await page.keyboard.press('ё').catch(e => e);
- expect(error && error.message).toBe('Unknown key: "ё"');
-
- error = await page.keyboard.press('😊').catch(e => e);
- expect(error && error.message).toBe('Unknown key: "😊"');
- });
- function dimensions() {
- const rect = document.querySelector('textarea').getBoundingClientRect();
- return {
- x: rect.left,
- y: rect.top,
- width: rect.width,
- height: rect.height
- };
- }
- });
-
describe('Page.setUserAgent', function() {
it('should work', async({page, server}) => {
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
@@ -2494,171 +1346,6 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
- describe('Network Events', function() {
- it('Page.Events.Request', async({page, server}) => {
- const requests = [];
- page.on('request', request => requests.push(request));
- await page.goto(server.EMPTY_PAGE);
- expect(requests.length).toBe(1);
- expect(requests[0].url()).toBe(server.EMPTY_PAGE);
- expect(requests[0].resourceType()).toBe('document');
- expect(requests[0].method()).toBe('GET');
- expect(requests[0].response()).toBeTruthy();
- expect(requests[0].frame() === page.mainFrame()).toBe(true);
- expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
- });
- it('Page.Events.Request should report post data', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- server.setRoute('/post', (req, res) => res.end());
- let request = null;
- page.on('request', r => request = r);
- await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
- expect(request).toBeTruthy();
- expect(request.postData()).toBe('{"foo":"bar"}');
- });
- it('Page.Events.Response', async({page, server}) => {
- const responses = [];
- page.on('response', response => responses.push(response));
- await page.goto(server.EMPTY_PAGE);
- expect(responses.length).toBe(1);
- expect(responses[0].url()).toBe(server.EMPTY_PAGE);
- expect(responses[0].status()).toBe(200);
- expect(responses[0].ok()).toBe(true);
- expect(responses[0].fromCache()).toBe(false);
- expect(responses[0].fromServiceWorker()).toBe(false);
- expect(responses[0].request()).toBeTruthy();
- });
-
- it('Response.fromCache()', async({page, server}) => {
- const responses = new Map();
- page.on('response', r => responses.set(r.url().split('/').pop(), r));
-
- // Load and re-load to make sure it's cached.
- await page.goto(server.PREFIX + '/cached/one-style.html');
- await page.reload();
-
- expect(responses.size).toBe(2);
- expect(responses.get('one-style.html').status()).toBe(304);
- expect(responses.get('one-style.html').fromCache()).toBe(false);
- expect(responses.get('one-style.css').status()).toBe(200);
- expect(responses.get('one-style.css').fromCache()).toBe(true);
- });
- it('Response.fromServiceWorker', async({page, server}) => {
- const responses = new Map();
- page.on('response', r => responses.set(r.url().split('/').pop(), r));
-
- // Load and re-load to make sure serviceworker is installed and running.
- await page.goto(server.PREFIX + '/serviceworkers/fetch/sw.html', {waitUntil: 'networkidle2'});
- await page.evaluate(async() => await window.activationPromise);
- await page.reload();
-
- expect(responses.size).toBe(2);
- expect(responses.get('sw.html').status()).toBe(200);
- expect(responses.get('sw.html').fromServiceWorker()).toBe(true);
- expect(responses.get('style.css').status()).toBe(200);
- expect(responses.get('style.css').fromServiceWorker()).toBe(true);
- });
-
- it('Page.Events.Response should provide body', async({page, server}) => {
- let response = null;
- page.on('response', r => response = r);
- await page.goto(server.PREFIX + '/simple.json');
- expect(response).toBeTruthy();
- expect(await response.text()).toBe('{"foo": "bar"}\n');
- expect(await response.json()).toEqual({foo: 'bar'});
- });
- it('Page.Events.Response should not report body unless request is finished', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- // Setup server to trap request.
- let serverResponse = null;
- server.setRoute('/get', (req, res) => {
- serverResponse = res;
- res.write('hello ');
- });
- // Setup page to trap response.
- let pageResponse = null;
- let requestFinished = false;
- page.on('response', r => pageResponse = r);
- page.on('requestfinished', () => requestFinished = true);
- // send request and wait for server response
- await Promise.all([
- page.evaluate(() => fetch('./get', { method: 'GET'})),
- waitForEvents(page, 'response')
- ]);
-
- expect(serverResponse).toBeTruthy();
- expect(pageResponse).toBeTruthy();
- expect(pageResponse.status()).toBe(200);
- expect(requestFinished).toBe(false);
-
- const responseText = pageResponse.text();
- // Write part of the response and wait for it to be flushed.
- await new Promise(x => serverResponse.write('wor', x));
- // Finish response.
- await new Promise(x => serverResponse.end('ld!', x));
- expect(await responseText).toBe('hello world!');
- });
- it('Page.Events.RequestFailed', async({page, server}) => {
- await page.setRequestInterception(true);
- page.on('request', request => {
- if (request.url().endsWith('css'))
- request.abort();
- else
- request.continue();
- });
- const failedRequests = [];
- page.on('requestfailed', request => failedRequests.push(request));
- await page.goto(server.PREFIX + '/one-style.html');
- expect(failedRequests.length).toBe(1);
- expect(failedRequests[0].url()).toContain('one-style.css');
- expect(failedRequests[0].response()).toBe(null);
- expect(failedRequests[0].resourceType()).toBe('stylesheet');
- expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED');
- expect(failedRequests[0].frame()).toBeTruthy();
- });
- it('Page.Events.RequestFinished', async({page, server}) => {
- const requests = [];
- page.on('requestfinished', request => requests.push(request));
- await page.goto(server.EMPTY_PAGE);
- expect(requests.length).toBe(1);
- expect(requests[0].url()).toBe(server.EMPTY_PAGE);
- expect(requests[0].response()).toBeTruthy();
- expect(requests[0].frame() === page.mainFrame()).toBe(true);
- expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
- });
- it('should fire events in proper order', async({page, server}) => {
- const events = [];
- page.on('request', request => events.push('request'));
- page.on('response', response => events.push('response'));
- page.on('requestfinished', request => events.push('requestfinished'));
- await page.goto(server.EMPTY_PAGE);
- expect(events).toEqual(['request', 'response', 'requestfinished']);
- });
- it('should support redirects', async({page, server}) => {
- const events = [];
- page.on('request', request => events.push(`${request.method()} ${request.url()}`));
- page.on('response', response => events.push(`${response.status()} ${response.url()}`));
- page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
- page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
- server.setRedirect('/foo.html', '/empty.html');
- const FOO_URL = server.PREFIX + '/foo.html';
- const response = await page.goto(FOO_URL);
- expect(events).toEqual([
- `GET ${FOO_URL}`,
- `302 ${FOO_URL}`,
- `DONE ${FOO_URL}`,
- `GET ${server.EMPTY_PAGE}`,
- `200 ${server.EMPTY_PAGE}`,
- `DONE ${server.EMPTY_PAGE}`
- ]);
-
- // Check redirect chain
- const redirectChain = response.request().redirectChain();
- expect(redirectChain.length).toBe(1);
- expect(redirectChain[0].url()).toContain('/foo.html');
- });
- });
-
describe('Page.addScriptTag', function() {
it('should throw an error if no options are provided', async({page, server}) => {
let error = null;
@@ -3170,38 +1857,6 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
- describe('Tracing', function() {
- beforeEach(function(state) {
- state.outputFile = path.join(__dirname, 'assets', `trace-${state.parallelIndex}.json`);
- });
- afterEach(function(state) {
- fs.unlinkSync(state.outputFile);
- state.outputFile = null;
- });
- it('should output a trace', async({page, server, outputFile}) => {
- await page.tracing.start({screenshots: true, path: outputFile});
- await page.goto(server.PREFIX + '/grid.html');
- await page.tracing.stop();
- expect(fs.existsSync(outputFile)).toBe(true);
- });
- it('should run with custom categories if provided', async({page, outputFile}) => {
- await page.tracing.start({path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
- await page.tracing.stop();
-
- const traceJson = JSON.parse(fs.readFileSync(outputFile));
- expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
- });
- it('should throw if tracing on two pages', async({page, server, browser, outputFile}) => {
- await page.tracing.start({path: outputFile});
- const newPage = await browser.newPage();
- let error = null;
- await newPage.tracing.start({path: outputFile}).catch(e => error = e);
- await newPage.close();
- expect(error).toBeTruthy();
- await page.tracing.stop();
- });
- });
-
describe('Cookies', function() {
afterEach(async({page, server}) => {
const cookies = await page.cookies(server.PREFIX + '/grid.html', server.CROSS_PROCESS_PREFIX);
@@ -3743,59 +2398,4 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
});
-
-
- /**
- * @param {!EventEmitter} emitter
- * @param {string} eventName
- * @param {number=} eventCount
- * @return {!Promise}
- */
- function waitForEvents(emitter, eventName, eventCount = 1) {
- let fulfill;
- const promise = new Promise(x => fulfill = x);
- emitter.on(eventName, onEvent);
- return promise;
-
- function onEvent(event) {
- --eventCount;
- if (eventCount)
- return;
- emitter.removeListener(eventName, onEvent);
- fulfill(event);
- }
- }
};
-
-/**
- * @param {!Buffer} pdfBuffer
- * @return {!Promise>}
- */
-async function getPDFPages(pdfBuffer) {
- const PDFJS = require('pdfjs-dist');
- PDFJS.disableWorker = true;
- const data = new Uint8Array(pdfBuffer);
- const doc = await PDFJS.getDocument(data);
- const pages = [];
- for (let i = 0; i < doc.numPages; ++i) {
- const page = await doc.getPage(i + 1);
- const viewport = page.getViewport(1);
- // Viewport width and height is in PDF points, which is
- // 1/72 of an inch.
- pages.push({
- width: viewport.width / 72,
- height: viewport.height / 72,
- });
- page.cleanup();
- }
- doc.cleanup();
- return pages;
-}
-
-/**
- * @param {number} px
- * @return {number}
- */
-function cssPixelsToInches(px) {
- return px / 96;
-}
diff --git a/test/puppeteer.spec.js b/test/puppeteer.spec.js
index a193d4ef393..e8ec2e1c59e 100644
--- a/test/puppeteer.spec.js
+++ b/test/puppeteer.spec.js
@@ -21,12 +21,12 @@ const {helper} = require('../lib/helper');
const mkdtempAsync = helper.promisify(fs.mkdtemp);
const readFileAsync = helper.promisify(fs.readFile);
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
-const FrameUtils = require('./frame-utils');
+const utils = require('./utils');
-module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppeteer, PROJECT_ROOT) {
- const {describe, xdescribe, fdescribe} = runner;
- const {it, fit, xit} = runner;
- const {beforeAll, beforeEach, afterAll, afterEach} = runner;
+module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, PROJECT_ROOT}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Puppeteer', function() {
describe('BrowserFetcher', function() {
@@ -244,7 +244,7 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
const browser = await puppeteer.connect({browserWSEndpoint});
const pages = await browser.pages();
const restoredPage = pages.find(page => page.url() === server.PREFIX + '/frames/nested-frames.html');
- expect(FrameUtils.dumpFrames(restoredPage.mainFrame())).toBeGolden('reconnect-nested-frames.txt');
+ expect(utils.dumpFrames(restoredPage.mainFrame())).toBeGolden('reconnect-nested-frames.txt');
expect(await restoredPage.evaluate(() => 7 * 8)).toBe(56);
await browser.close();
});
@@ -256,19 +256,4 @@ module.exports.addTests = function(runner, expect, defaultBrowserOptions, puppet
});
});
});
-
- if (process.env.COVERAGE) {
- describe('COVERAGE', function(){
- const coverage = helper.publicAPICoverage();
- const disabled = new Set(['page.bringToFront']);
- if (!defaultBrowserOptions.headless)
- disabled.add('page.pdf');
-
- for (const method of coverage.keys()) {
- (disabled.has(method) ? xit : it)(`public api '${method}' should be called`, async({page, server}) => {
- expect(coverage.get(method)).toBe(true);
- });
- }
- });
- }
};
diff --git a/test/test.js b/test/test.js
index 0a7df7381c9..c236bcb321d 100644
--- a/test/test.js
+++ b/test/test.js
@@ -59,7 +59,7 @@ require('events').defaultMaxListeners *= parallel;
const timeout = slowMo ? 0 : 10 * 1000;
const runner = new TestRunner({timeout, parallel});
new Reporter(runner);
-
+const {beforeAll, beforeEach, afterAll, describe, xit, it} = runner;
const {expect} = new Matchers({
toBeGolden: GoldenUtils.compare.bind(null, GOLDEN_DIR, OUTPUT_DIR)
});
@@ -69,7 +69,7 @@ if (fs.existsSync(OUTPUT_DIR))
console.log('Testing on Node', process.version);
-runner.beforeAll(async state => {
+beforeAll(async state => {
const assetsPath = path.join(__dirname, 'assets');
const cachedPath = path.join(__dirname, 'assets', 'cached');
@@ -88,12 +88,12 @@ runner.beforeAll(async state => {
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
});
-runner.beforeEach(async({server, httpsServer}) => {
+beforeEach(async({server, httpsServer}) => {
server.reset();
httpsServer.reset();
});
-runner.afterAll(async({server, httpsServer}) => {
+afterAll(async({server, httpsServer}) => {
await Promise.all([
server.stop(),
httpsServer.stop(),
@@ -108,15 +108,30 @@ const testFiles = [
testFiles
.map(file => path.join(__dirname, file))
.forEach(file =>
- require(file).addTests(
- runner,
- expect,
- defaultBrowserOptions,
- puppeteer,
- PROJECT_ROOT
- )
+ require(file).addTests({
+ testRunner: runner,
+ expect,
+ defaultBrowserOptions,
+ puppeteer,
+ PROJECT_ROOT
+ })
);
+if (process.env.COVERAGE) {
+ describe('COVERAGE', function(){
+ const coverage = helper.publicAPICoverage();
+ const disabled = new Set(['page.bringToFront']);
+ if (!defaultBrowserOptions.headless)
+ disabled.add('page.pdf');
+
+ for (const method of coverage.keys()) {
+ (disabled.has(method) ? xit : it)(`public api '${method}' should be called`, async({page, server}) => {
+ expect(coverage.get(method)).toBe(true);
+ });
+ }
+ });
+}
+
if (process.env.CI && runner.hasFocusedTestsOrSuites()) {
console.error('ERROR: "focused" tests/suites are prohibitted on bots. Remove any "fit"/"fdescribe" declarations.');
process.exit(1);
diff --git a/test/tracing.spec.js b/test/tracing.spec.js
new file mode 100644
index 00000000000..bcbd849f644
--- /dev/null
+++ b/test/tracing.spec.js
@@ -0,0 +1,56 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+module.exports.addTests = function({testRunner, expect}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ describe('Tracing', function() {
+ beforeEach(function(state) {
+ state.outputFile = path.join(__dirname, 'assets', `trace-${state.parallelIndex}.json`);
+ });
+ afterEach(function(state) {
+ fs.unlinkSync(state.outputFile);
+ state.outputFile = null;
+ });
+ it('should output a trace', async({page, server, outputFile}) => {
+ await page.tracing.start({screenshots: true, path: outputFile});
+ await page.goto(server.PREFIX + '/grid.html');
+ await page.tracing.stop();
+ expect(fs.existsSync(outputFile)).toBe(true);
+ });
+ it('should run with custom categories if provided', async({page, outputFile}) => {
+ await page.tracing.start({path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
+ await page.tracing.stop();
+
+ const traceJson = JSON.parse(fs.readFileSync(outputFile));
+ expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
+ });
+ it('should throw if tracing on two pages', async({page, server, browser, outputFile}) => {
+ await page.tracing.start({path: outputFile});
+ const newPage = await browser.newPage();
+ let error = null;
+ await newPage.tracing.start({path: outputFile}).catch(e => error = e);
+ await newPage.close();
+ expect(error).toBeTruthy();
+ await page.tracing.stop();
+ });
+ });
+};
\ No newline at end of file
diff --git a/test/frame-utils.js b/test/utils.js
similarity index 61%
rename from test/frame-utils.js
rename to test/utils.js
index 2a53c8d9977..cfc980582d9 100644
--- a/test/frame-utils.js
+++ b/test/utils.js
@@ -72,4 +72,58 @@ const utils = module.exports = {
result += '\n' + utils.dumpFrames(child, ' ' + indentation);
return result;
},
+
+ /**
+ * @param {!EventEmitter} emitter
+ * @param {string} eventName
+ * @param {number=} eventCount
+ * @return {!Promise}
+ */
+ waitForEvents: function(emitter, eventName, eventCount = 1) {
+ let fulfill;
+ const promise = new Promise(x => fulfill = x);
+ emitter.on(eventName, onEvent);
+ return promise;
+
+ function onEvent(event) {
+ --eventCount;
+ if (eventCount)
+ return;
+ emitter.removeListener(eventName, onEvent);
+ fulfill(event);
+ }
+ },
+
+ /**
+ * @param {!Buffer} pdfBuffer
+ * @return {!Promise>}
+ */
+ getPDFPages: async function(pdfBuffer) {
+ const PDFJS = require('pdfjs-dist');
+ PDFJS.disableWorker = true;
+ const data = new Uint8Array(pdfBuffer);
+ const doc = await PDFJS.getDocument(data);
+ const pages = [];
+ for (let i = 0; i < doc.numPages; ++i) {
+ const page = await doc.getPage(i + 1);
+ const viewport = page.getViewport(1);
+ // Viewport width and height is in PDF points, which is
+ // 1/72 of an inch.
+ pages.push({
+ width: viewport.width / 72,
+ height: viewport.height / 72,
+ });
+ page.cleanup();
+ }
+ doc.cleanup();
+ return pages;
+ },
+
+ /**
+ * @param {number} px
+ * @return {number}
+ */
+ cssPixelsToInches: function(px) {
+ return px / 96;
+ },
};