From a5db6d412c99627b8ea06d6c2c904f6b7ae4e900 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Tue, 12 Dec 2017 13:34:21 -0800 Subject: [PATCH] test: migrate test.js to support concurrent test execution (#1531) This patch migrates tests so that they can be run concurrently. - By default, tests still run in one thread. - To run tests in 4 parallel threads, run node test/test.js -j 4 or npm run unit -- -j 4 - Environment variable PPTR_PARALLEL_TESTS could be set to override default parallelization Every test gets passed in a state. State is set up in the beforeAll and beforeEach callbacks, and should be teared down in the afterAll and afterEach callbacks. By default, state has a parallelIndex variable initialized that defines the thread index that runs the execution. --- test/frame-utils.js | 2 +- test/golden/nested-frames.txt | 10 +- test/golden/reconnect-nested-frames.txt | 10 +- test/test.js | 1744 ++++++++++++----------- utils/testrunner/README.md | 4 +- utils/testrunner/TestRunner.js | 2 +- 6 files changed, 888 insertions(+), 884 deletions(-) diff --git a/test/frame-utils.js b/test/frame-utils.js index 2de76772..2a53c8d9 100644 --- a/test/frame-utils.js +++ b/test/frame-utils.js @@ -67,7 +67,7 @@ const utils = module.exports = { */ dumpFrames: function(frame, indentation) { indentation = indentation || ''; - let result = indentation + frame.url(); + let result = indentation + frame.url().replace(/:\d{4}\//, ':/'); for (const child of frame.childFrames()) result += '\n' + utils.dumpFrames(child, ' ' + indentation); return result; diff --git a/test/golden/nested-frames.txt b/test/golden/nested-frames.txt index 008d26ba..e6270080 100644 --- a/test/golden/nested-frames.txt +++ b/test/golden/nested-frames.txt @@ -1,5 +1,5 @@ -http://localhost:8907/frames/nested-frames.html - http://localhost:8907/frames/two-frames.html - http://localhost:8907/frames/frame.html - http://localhost:8907/frames/frame.html - http://localhost:8907/frames/frame.html \ No newline at end of file +http://localhost:/frames/nested-frames.html + http://localhost:/frames/two-frames.html + http://localhost:/frames/frame.html + http://localhost:/frames/frame.html + http://localhost:/frames/frame.html \ No newline at end of file diff --git a/test/golden/reconnect-nested-frames.txt b/test/golden/reconnect-nested-frames.txt index 008d26ba..e6270080 100644 --- a/test/golden/reconnect-nested-frames.txt +++ b/test/golden/reconnect-nested-frames.txt @@ -1,5 +1,5 @@ -http://localhost:8907/frames/nested-frames.html - http://localhost:8907/frames/two-frames.html - http://localhost:8907/frames/frame.html - http://localhost:8907/frames/frame.html - http://localhost:8907/frames/frame.html \ No newline at end of file +http://localhost:/frames/nested-frames.html + http://localhost:/frames/two-frames.html + http://localhost:/frames/frame.html + http://localhost:/frames/frame.html + http://localhost:/frames/frame.html \ No newline at end of file diff --git a/test/test.js b/test/test.js index 5a5deb22..97e2d40b 100644 --- a/test/test.js +++ b/test/test.js @@ -24,6 +24,8 @@ const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? const puppeteer = require(PROJECT_ROOT); const DeviceDescriptors = require(path.join(PROJECT_ROOT, 'DeviceDescriptors')); +const iPhone = DeviceDescriptors['iPhone 6']; +const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; const SimpleServer = require('./server/SimpleServer'); const GoldenUtils = require('./golden-utils'); @@ -34,13 +36,6 @@ const RESET_COLOR = '\x1b[0m'; const GOLDEN_DIR = path.join(__dirname, 'golden'); const OUTPUT_DIR = path.join(__dirname, 'output'); -const PORT = 8907; -const PREFIX = 'http://localhost:' + PORT; -const CROSS_PROCESS_PREFIX = 'http://127.0.0.1:' + PORT; -const EMPTY_PAGE = PREFIX + '/empty.html'; -const HTTPS_PORT = 8908; -const HTTPS_PREFIX = 'https://localhost:' + HTTPS_PORT; - const headless = (process.env.HEADLESS || 'true').trim().toLowerCase() === 'true'; const slowMo = parseInt((process.env.SLOW_MO || '0').trim(), 10); const executablePath = process.env.CHROME; @@ -59,9 +54,15 @@ const defaultBrowserOptions = { }; const timeout = process.env.DEBUG_TEST || slowMo ? 0 : 10 * 1000; +let parallel = 1; +if (process.env.PPTR_PARALLEL_TESTS) + parallel = parseInt(process.env.PPTR_PARALLEL_TESTS.trim(), 10); +const parallelArgIndex = process.argv.indexOf('-j'); +if (parallelArgIndex !== -1) + parallel = parseInt(process.argv[parallelArgIndex + 1], 10); const {TestRunner, Reporter, Matchers} = require('../utils/testrunner/'); -const runner = new TestRunner({timeout}); +const runner = new TestRunner({timeout, parallel}); new Reporter(runner); const {describe, xdescribe, fdescribe} = runner; @@ -72,41 +73,49 @@ const {expect} = new Matchers({ toBeGolden: GoldenUtils.compare.bind(null, GOLDEN_DIR, OUTPUT_DIR) }); -let server; -let httpsServer; -beforeAll(SX(async function() { - const assetsPath = path.join(__dirname, 'assets'); - server = await SimpleServer.create(assetsPath, PORT); - httpsServer = await SimpleServer.createHTTPS(assetsPath, HTTPS_PORT); - if (fs.existsSync(OUTPUT_DIR)) - rm(OUTPUT_DIR); -})); +if (fs.existsSync(OUTPUT_DIR)) + rm(OUTPUT_DIR); -beforeEach(SX(async function() { +beforeAll(async state => { + const assetsPath = path.join(__dirname, 'assets'); + const port = 8907 + state.parallelIndex * 2; + state.server = await SimpleServer.create(assetsPath, port); + state.server.PREFIX = `http://localhost:${port}`; + state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`; + state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`; + + const httpsPort = port + 1; + state.httpsServer = await SimpleServer.createHTTPS(assetsPath, httpsPort); + state.httpsServer.PREFIX = `https://localhost:${httpsPort}`; + state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`; + state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`; +}); + +beforeEach(async({server, httpsServer}) => { server.reset(); httpsServer.reset(); -})); +}); -afterAll(SX(async function() { +afterAll(async({server, httpsServer}) => { await Promise.all([ server.stop(), httpsServer.stop(), ]); -})); +}); describe('Puppeteer', function() { describe('Puppeteer.launch', function() { - it('should support ignoreHTTPSErrors option', SX(async function() { + it('should support ignoreHTTPSErrors option', async({httpsServer}) => { const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions); const browser = await puppeteer.launch(options); const page = await browser.newPage(); let error = null; - const response = await page.goto(HTTPS_PREFIX + '/empty.html').catch(e => error = e); + const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); expect(error).toBe(null); expect(response.ok).toBe(true); browser.close(); - })); - it('should reject all promises when browser is closed', SX(async function() { + }); + it('should reject all promises when browser is closed', async() => { const browser = await puppeteer.launch(defaultBrowserOptions); const page = await browser.newPage(); let error = null; @@ -114,14 +123,14 @@ describe('Puppeteer', function() { browser.close(); await neverResolves; expect(error.message).toContain('Protocol error'); - })); - it('should reject if executable path is invalid', SX(async function() { + }); + it('should reject if executable path is invalid', async({server}) => { let waitError = null; const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'}); await puppeteer.launch(options).catch(e => waitError = e); expect(waitError.message.startsWith('Failed to launch chrome! spawn random-invalid-path ENOENT')).toBe(true); - })); - it('userDataDir option', SX(async function() { + }); + it('userDataDir option', async({server}) => { const userDataDir = fs.mkdtempSync(path.join(__dirname, 'test-user-data-dir')); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); @@ -129,8 +138,8 @@ describe('Puppeteer', function() { await browser.close(); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); rm(userDataDir); - })); - it('userDataDir argument', SX(async function() { + }); + it('userDataDir argument', async({server}) => { const userDataDir = fs.mkdtempSync(path.join(__dirname, 'test-user-data-dir')); const options = Object.assign({}, defaultBrowserOptions); options.args = [`--user-data-dir=${userDataDir}`].concat(options.args); @@ -139,63 +148,63 @@ describe('Puppeteer', function() { await browser.close(); expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0); rm(userDataDir); - })); - it('userDataDir option should restore state', SX(async function() { + }); + it('userDataDir option should restore state', async({server}) => { const userDataDir = fs.mkdtempSync(path.join(__dirname, 'test-user-data-dir')); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); const page = await browser.newPage(); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => localStorage.hey = 'hello'); await browser.close(); const browser2 = await puppeteer.launch(options); const page2 = await browser2.newPage(); - await page2.goto(EMPTY_PAGE); + await page2.goto(server.EMPTY_PAGE); expect(await page2.evaluate(() => localStorage.hey)).toBe('hello'); await browser2.close(); rm(userDataDir); - })); + }); // @see https://github.com/GoogleChrome/puppeteer/issues/1537 - xit('userDataDir option should restore cookies', SX(async function() { + xit('userDataDir option should restore cookies', async({server}) => { const userDataDir = fs.mkdtempSync(path.join(__dirname, 'test-user-data-dir')); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); const page = await browser.newPage(); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); await browser.close(); const browser2 = await puppeteer.launch(options); const page2 = await browser2.newPage(); - await page2.goto(EMPTY_PAGE); + await page2.goto(server.EMPTY_PAGE); expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true'); await browser2.close(); rm(userDataDir); - })); - xit('headless should be able to read cookies written by headful', SX(async function() { + }); + xit('headless should be able to read cookies written by headful', async({server}) => { const userDataDir = fs.mkdtempSync(path.join(__dirname, 'test-user-data-dir')); const options = Object.assign({userDataDir}, defaultBrowserOptions); // Write a cookie in headful chrome options.headless = false; const headfulBrowser = await puppeteer.launch(options); const headfulPage = await headfulBrowser.newPage(); - await headfulPage.goto(EMPTY_PAGE); + await headfulPage.goto(server.EMPTY_PAGE); await headfulPage.evaluate(() => document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); await headfulBrowser.close(); // Read the cookie from headless chrome options.headless = true; const headlessBrowser = await puppeteer.launch(options); const headlessPage = await headlessBrowser.newPage(); - await headlessPage.goto(EMPTY_PAGE); + await headlessPage.goto(server.EMPTY_PAGE); const cookie = await headlessPage.evaluate(() => document.cookie); await headlessBrowser.close(); rm(userDataDir); expect(cookie).toBe('foo=true'); - })); + }); }); describe('Puppeteer.connect', function() { - it('should be able to connect multiple times to the same browser', SX(async function() { + it('should be able to connect multiple times to the same browser', async({server}) => { const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.connect({ browserWSEndpoint: originalBrowser.wsEndpoint() @@ -207,74 +216,71 @@ describe('Puppeteer', function() { const secondPage = await originalBrowser.newPage(); expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work'); await originalBrowser.close(); - })); - it('should be able to reconnect to a disconnected browser', SX(async function() { + }); + it('should be able to reconnect to a disconnected browser', async({server}) => { const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const browserWSEndpoint = originalBrowser.wsEndpoint(); const page = await originalBrowser.newPage(); - await page.goto(PREFIX + '/frames/nested-frames.html'); + await page.goto(server.PREFIX + '/frames/nested-frames.html'); originalBrowser.disconnect(); const FrameUtils = require('./frame-utils'); const browser = await puppeteer.connect({browserWSEndpoint}); const pages = await browser.pages(); - const restoredPage = pages.find(page => page.url() === PREFIX + '/frames/nested-frames.html'); + const restoredPage = pages.find(page => page.url() === server.PREFIX + '/frames/nested-frames.html'); expect(FrameUtils.dumpFrames(restoredPage.mainFrame())).toBeGolden('reconnect-nested-frames.txt'); expect(await restoredPage.evaluate(() => 7 * 8)).toBe(56); await browser.close(); - })); + }); }); describe('Puppeteer.executablePath', function() { - it('should work', SX(async function() { + it('should work', async({server}) => { const executablePath = puppeteer.executablePath(); expect(fs.existsSync(executablePath)).toBe(true); - })); + }); }); }); describe('Page', function() { - const iPhone = DeviceDescriptors['iPhone 6']; - const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; + beforeAll(async state => { + state.browser = await puppeteer.launch(defaultBrowserOptions); + }); - let browser; - let page; + afterAll(async state => { + await state.browser.close(); + state.browser = null; + }); - beforeAll(SX(async function() { - browser = await puppeteer.launch(defaultBrowserOptions); - })); + beforeEach(async state => { + state.page = await state.browser.newPage(); + }); - afterAll(SX(async function() { - browser.close(); - })); - - beforeEach(SX(async function() { - page = await browser.newPage(); - })); - - afterEach(SX(async function() { - await page.close(); - })); + afterEach(async state => { + await state.page.close(); + state.page = null; + }); describe('Browser.version', function() { - it('should return whether we are in headless', SX(async 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.process', function() { - it('should return child_process instance', SX(async 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', SX(async 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}); @@ -296,48 +302,48 @@ describe('Page', function() { expect(disconnectedOriginal).toBe(1); expect(disconnectedRemote1).toBe(1); expect(disconnectedRemote2).toBe(1); - })); + }); }); describe('Page.close', function() { - it('should reject all promises when page is closed', SX(async function() { + it('should reject all promises when page is closed', async({browser}) => { const newPage = await browser.newPage(); const neverResolves = newPage.evaluate(() => new Promise(r => {})); newPage.close(); let error = null; await neverResolves.catch(e => error = e); expect(error.message).toContain('Protocol error'); - })); + }); }); describe('Page.Events.error', function() { - it('should throw when page crashes', SX(async function() { + it('should throw when page crashes', async({page}) => { let error = null; page.on('error', err => error = err); page.goto('chrome://crash').catch(e => {}); await waitForEvents(page, 'error'); expect(error.message).toBe('Page crashed!'); - })); + }); }); describe('Page.evaluate', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { const result = await page.evaluate(() => 7 * 3); expect(result).toBe(21); - })); - it('should await promise', SX(async function() { + }); + it('should await promise', async({page, server}) => { const result = await page.evaluate(() => Promise.resolve(8 * 7)); expect(result).toBe(56); - })); - it('should work right after framenavigated', SX(async function() { + }); + it('should work right after framenavigated', async({page, server}) => { let frameEvaluation = null; page.on('framenavigated', async frame => { frameEvaluation = frame.evaluate(() => 6 * 7); }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(await frameEvaluation).toBe(42); - })); - it('should work from-inside an exposed function', SX(async function() { + }); + it('should work from-inside an exposed function', async({page, server}) => { // Setup inpage callback, which calls Page.evaluate await page.exposeFunction('callController', async function(a, b) { return await page.evaluate((a, b) => a * b, a, b); @@ -346,62 +352,62 @@ describe('Page', function() { return await callController(9, 3); }); expect(result).toBe(27); - })); - it('should reject promise with exception', SX(async function() { + }); + it('should reject promise with exception', async({page, server}) => { let error = null; await page.evaluate(() => not.existing.object.property).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('not is not defined'); - })); - it('should return complex objects', SX(async function() { + }); + it('should return complex objects', async({page, server}) => { const object = {foo: 'bar!'}; const result = await page.evaluate(a => a, object); expect(result).not.toBe(object); expect(result).toEqual(object); - })); - it('should return NaN', SX(async function() { + }); + it('should return NaN', async({page, server}) => { const result = await page.evaluate(() => NaN); expect(Object.is(result, NaN)).toBe(true); - })); - it('should return -0', SX(async function() { + }); + it('should return -0', async({page, server}) => { const result = await page.evaluate(() => -0); expect(Object.is(result, -0)).toBe(true); - })); - it('should return Infinity', SX(async function() { + }); + it('should return Infinity', async({page, server}) => { const result = await page.evaluate(() => Infinity); expect(Object.is(result, Infinity)).toBe(true); - })); - it('should return -Infinity', SX(async function() { + }); + it('should return -Infinity', async({page, server}) => { const result = await page.evaluate(() => -Infinity); expect(Object.is(result, -Infinity)).toBe(true); - })); - it('should accept "undefined" as one of multiple parameters', SX(async function() { + }); + it('should accept "undefined" as one of multiple parameters', async({page, server}) => { const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo'); expect(result).toBe(true); - })); - it('should fail for window object', SX(async function() { + }); + it('should fail for window object', async({page, server}) => { const result = await page.evaluate(() => window); expect(result).toBe(undefined); - })); - it('should accept a string', SX(async function() { + }); + it('should accept a string', async({page, server}) => { const result = await page.evaluate('1 + 2'); expect(result).toBe(3); - })); - it('should accept a string with semi colons', SX(async function() { + }); + it('should accept a string with semi colons', async({page, server}) => { const result = await page.evaluate('1 + 5;'); expect(result).toBe(6); - })); - it('should accept a string with comments', SX(async function() { + }); + it('should accept a string with comments', async({page, server}) => { const result = await page.evaluate('2 + 5;\n// do some math!'); expect(result).toBe(7); - })); - it('should accept element handle as an argument', SX(async function() { + }); + it('should accept element handle as an argument', async({page, server}) => { await page.setContent('
42
'); const element = await page.$('section'); const text = await page.evaluate(e => e.textContent, element); expect(text).toBe('42'); - })); - it('should throw if underlying element was disposed', SX(async function() { + }); + it('should throw if underlying element was disposed', async({page, server}) => { await page.setContent('
39
'); const element = await page.$('section'); expect(element).toBeTruthy(); @@ -409,56 +415,56 @@ describe('Page', function() { let error = null; await page.evaluate(e => e.textContent, element).catch(e => error = e); expect(error.message).toContain('JSHandle is disposed'); - })); - it('should throw if elementHandles are from other frames', SX(async function() { + }); + it('should throw if elementHandles are from other frames', async({page, server}) => { const FrameUtils = require('./frame-utils'); - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); + await FrameUtils.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); expect(error).toBeTruthy(); expect(error.message).toContain('JSHandles can be evaluated only in the context they were created'); - })); - it('should accept object handle as an argument', SX(async function() { + }); + it('should accept object handle as an argument', async({page, server}) => { const navigatorHandle = await page.evaluateHandle(() => navigator); const text = await page.evaluate(e => e.userAgent, navigatorHandle); expect(text).toContain('Mozilla'); - })); - it('should accept object handle to primitive types', SX(async function() { + }); + it('should accept object handle to primitive types', async({page, server}) => { const aHandle = await page.evaluateHandle(() => 5); const isFive = await page.evaluate(e => Object.is(e, 5), aHandle); expect(isFive).toBeTruthy(); - })); + }); }); describe('Page.setOfflineMode', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setOfflineMode(true); let error = null; - await page.goto(EMPTY_PAGE).catch(e => error = e); + await page.goto(server.EMPTY_PAGE).catch(e => error = e); expect(error).toBeTruthy(); await page.setOfflineMode(false); const response = await page.reload(); expect(response.status).toBe(200); - })); - it('should emulate navigator.onLine', SX(async function() { + }); + it('should emulate navigator.onLine', async({page, server}) => { expect(await page.evaluate(() => window.navigator.onLine)).toBe(true); await page.setOfflineMode(true); expect(await page.evaluate(() => window.navigator.onLine)).toBe(false); await page.setOfflineMode(false); expect(await page.evaluate(() => window.navigator.onLine)).toBe(true); - })); + }); }); describe('Page.evaluateHandle', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { const windowHandle = await page.evaluateHandle(() => window); expect(windowHandle).toBeTruthy(); - })); + }); }); describe('ExecutionContext.queryObjects', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { // Instantiate an object await page.evaluate(() => window.set = new Set(['hello', 'world'])); const prototypeHandle = await page.evaluateHandle(() => Set.prototype); @@ -467,24 +473,24 @@ describe('Page', function() { expect(count).toBe(1); const values = await page.evaluate(objects => Array.from(objects[0].values()), objectsHandle); expect(values).toEqual(['hello', 'world']); - })); - it('should fail for disposed handles', SX(async function() { + }); + it('should fail for disposed handles', async({page, server}) => { const prototypeHandle = await page.evaluateHandle(() => HTMLBodyElement.prototype); await prototypeHandle.dispose(); let error = null; await page.queryObjects(prototypeHandle).catch(e => error = e); expect(error.message).toBe('Prototype JSHandle is disposed!'); - })); - it('should fail primitive values as prototypes', SX(async function() { + }); + it('should fail primitive values as prototypes', async({page, server}) => { const prototypeHandle = await page.evaluateHandle(() => 42); let error = null; await page.queryObjects(prototypeHandle).catch(e => error = e); expect(error.message).toBe('Prototype JSHandle must not be referencing primitive value'); - })); + }); }); describe('JSHandle.getProperty', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { const aHandle = await page.evaluateHandle(() => ({ one: 1, two: 2, @@ -492,30 +498,30 @@ describe('Page', function() { })); const twoHandle = await aHandle.getProperty('two'); expect(await twoHandle.jsonValue()).toEqual(2); - })); + }); }); describe('JSHandle.jsonValue', function() { - it('should work', SX(async 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', SX(async function() { + }); + 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', SX(async function() { + }); + 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', SX(async function() { + it('should work', async({page, server}) => { const aHandle = await page.evaluateHandle(() => ({ foo: 'bar' })); @@ -523,8 +529,8 @@ describe('Page', function() { const foo = properties.get('foo'); expect(foo).toBeTruthy(); expect(await foo.jsonValue()).toBe('bar'); - })); - it('should return even non-own properties', SX(async function() { + }); + it('should return even non-own properties', async({page, server}) => { const aHandle = await page.evaluateHandle(() => { class A { constructor() { @@ -542,47 +548,47 @@ describe('Page', function() { 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', SX(async 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', SX(async function() { + }); + 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', SX(async function() { + }); + 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', SX(async 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', SX(async function() { + }); + 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() { const FrameUtils = require('./frame-utils'); - it('should work', SX(async function() { - await page.goto(EMPTY_PAGE); - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); + 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(); @@ -601,14 +607,14 @@ describe('Page', function() { ]); expect(a1).toBe(1); expect(a2).toBe(2); - })); + }); }); describe('Frame.evaluate', function() { const FrameUtils = require('./frame-utils'); - it('should have different execution contexts', SX(async function() { - await page.goto(EMPTY_PAGE); - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); + 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]; @@ -616,23 +622,23 @@ describe('Page', function() { 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', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + 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(CROSS_PROCESS_PREFIX + '/empty.html'); + 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', SX(async 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', SX(async function() { + }); + it('should poll on interval', async({page, server}) => { let success = false; const startTime = Date.now(); const polling = 100; @@ -643,8 +649,8 @@ describe('Page', function() { await page.evaluate(() => document.body.appendChild(document.createElement('div'))); await watchdog; expect(Date.now() - startTime).not.toBeLessThan(polling / 2); - })); - it('should poll on mutation', SX(async function() { + }); + it('should poll on mutation', async({page, server}) => { let success = false; const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'}) .then(() => success = true); @@ -652,13 +658,13 @@ describe('Page', function() { expect(success).toBe(false); await page.evaluate(() => document.body.appendChild(document.createElement('div'))); await watchdog; - })); - it('should poll on raf', SX(async function() { + }); + 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', SX(async function() { + }); + it('should throw on bad polling value', async({page, server}) => { let error = null; try { await page.waitForFunction(() => !!document.body, {polling: 'unknown'}); @@ -667,8 +673,8 @@ describe('Page', function() { } expect(error).toBeTruthy(); expect(error.message).toContain('polling'); - })); - it('should throw negative polling interval', SX(async function() { + }); + it('should throw negative polling interval', async({page, server}) => { let error = null; try { await page.waitForFunction(() => !!document.body, {polling: -10}); @@ -677,15 +683,15 @@ describe('Page', function() { } expect(error).toBeTruthy(); expect(error.message).toContain('Cannot poll with non-positive interval'); - })); + }); }); describe('Frame.waitForSelector', function() { const FrameUtils = require('./frame-utils'); const addElement = tag => document.body.appendChild(document.createElement(tag)); - it('should immediately resolve promise if node exists', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should immediately resolve promise if node exists', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const frame = page.mainFrame(); let added = false; await frame.waitForSelector('*').then(() => added = true); @@ -695,10 +701,10 @@ describe('Page', function() { await frame.evaluate(addElement, 'div'); await frame.waitForSelector('div').then(() => added = true); expect(added).toBe(true); - })); + }); - it('should resolve promise when node is added', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should resolve promise when node is added', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const frame = page.mainFrame(); let added = false; const watchdog = frame.waitForSelector('div').then(() => added = true); @@ -711,19 +717,19 @@ describe('Page', function() { await frame.evaluate(addElement, 'div'); await watchdog; expect(added).toBe(true); - })); + }); - it('should work when node is added through innerHTML', SX(async function() { - await page.goto(EMPTY_PAGE); + 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', SX(async function() { - await page.goto(EMPTY_PAGE); - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); + 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]; let added = false; page.waitForSelector('div').then(() => added = true); @@ -731,11 +737,11 @@ describe('Page', function() { expect(added).toBe(false); await page.evaluate(addElement, 'div'); expect(added).toBe(true); - })); + }); - it('should run in specified frame', SX(async function() { - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); - await FrameUtils.attachFrame(page, 'frame2', EMPTY_PAGE); + 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]; let added = false; @@ -745,19 +751,19 @@ describe('Page', function() { expect(added).toBe(false); await frame2.evaluate(addElement, 'div'); expect(added).toBe(true); - })); + }); - it('should throw if evaluation failed', SX(async function() { + it('should throw if evaluation failed', async({page, server}) => { await page.evaluateOnNewDocument(function() { document.querySelector = null; }); - await page.goto(EMPTY_PAGE); + 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', SX(async function() { - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); + }); + 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); @@ -765,19 +771,19 @@ describe('Page', function() { await waitPromise; expect(waitError).toBeTruthy(); expect(waitError.message).toContain('waitForSelector failed: frame got detached.'); - })); - it('should survive cross-process navigation', SX(async function() { + }); + it('should survive cross-process navigation', async({page, server}) => { let boxFound = false; const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(boxFound).toBe(false); await page.reload(); expect(boxFound).toBe(false); - await page.goto(CROSS_PROCESS_PREFIX + '/grid.html'); + await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html'); await waitForSelector; expect(boxFound).toBe(true); - })); - it('should wait for visible', SX(async function() { + }); + 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
`); @@ -787,8 +793,8 @@ describe('Page', function() { await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility')); expect(await waitForSelector).toBe(true); expect(divFound).toBe(true); - })); - it('should wait for visible recursively', SX(async function() { + }); + 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(`
hi
`); @@ -798,8 +804,8 @@ describe('Page', function() { 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', SX(async function() { + }); + 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); @@ -808,8 +814,8 @@ describe('Page', function() { 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', SX(async function() { + }); + 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); @@ -818,8 +824,8 @@ describe('Page', function() { 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', SX(async function() { + }); + 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); @@ -828,57 +834,57 @@ describe('Page', function() { await page.evaluate(() => document.querySelector('div').remove()); expect(await waitForSelector).toBe(true); expect(divRemoved).toBe(true); - })); - it('should respect timeout', SX(async function() { + }); + 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', SX(async function() { + 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); - })); + }); }); describe('Page.waitFor', function() { - it('should wait for selector', SX(async function() { + it('should wait for selector', async({page, server}) => { let found = false; const waitFor = page.waitFor('div').then(() => found = true); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(found).toBe(false); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); await waitFor; expect(found).toBe(true); - })); - it('should timeout', SX(async function() { + }); + it('should timeout', async({page, server}) => { const startTime = Date.now(); const timeout = 42; await page.waitFor(timeout); expect(Date.now() - startTime).not.toBeLessThan(timeout / 2); - })); - it('should wait for predicate', SX(async function() { + }); + it('should wait for predicate', async({page, server}) => { const watchdog = page.waitFor(() => window.innerWidth < 100); page.setViewport({width: 10, height: 10}); await watchdog; - })); - it('should throw when unknown type', SX(async function() { + }); + it('should throw when unknown type', async({page, server}) => { let error = null; await page.waitFor({foo: 'bar'}).catch(e => error = e); expect(error.message).toContain('Unsupported target type'); - })); - it('should wait for predicate with arguments', SX(async function() { + }); + it('should wait for predicate with arguments', async({page, server}) => { await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2); - })); + }); }); describe('Page.Events.Console', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { let message = null; page.once('console', m => message = m); await Promise.all([ @@ -890,8 +896,8 @@ describe('Page', function() { expect(await message.args[0].jsonValue()).toEqual('hello'); expect(await message.args[1].jsonValue()).toEqual(5); expect(await message.args[2].jsonValue()).toEqual({foo: 'bar'}); - })); - it('should work for different console API calls', SX(async function() { + }); + it('should work for different console API calls', async({page, server}) => { const messages = []; page.on('console', msg => messages.push(msg)); await Promise.all([ @@ -919,8 +925,8 @@ describe('Page', function() { 'calling console.error', 'JSHandle@promise', ]); - })); - it('should not fail for window object', SX(async function() { + }); + it('should not fail for window object', async({page, server}) => { let message = null; page.once('console', msg => message = msg); await Promise.all([ @@ -928,22 +934,22 @@ describe('Page', function() { waitForEvents(page, 'console') ]); expect(message.text).toBe('JSHandle@object'); - })); + }); }); describe('Page.metrics', function() { - it('should get metrics from a page', SX(async function() { + it('should get metrics from a page', async({page, server}) => { await page.goto('about:blank'); const metrics = await page.metrics(); checkMetrics(metrics); - })); - it('metrics event fired on console.timeStamp', SX(async function() { + }); + it('metrics event fired on console.timeStamp', async({page, server}) => { const metricsPromise = new Promise(fulfill => page.once('metrics', fulfill)); await page.evaluate(() => console.timeStamp('test42')); const metrics = await metricsPromise; expect(metrics.title).toBe('test42'); checkMetrics(metrics.metrics); - })); + }); function checkMetrics(metrics) { const metricsToCheck = new Set([ 'Timestamp', @@ -970,91 +976,91 @@ describe('Page', function() { }); describe('Page.goto', function() { - it('should navigate to about:blank', SX(async function() { + it('should navigate to about:blank', async({page, server}) => { const response = await page.goto('about:blank'); expect(response).toBe(null); - })); - it('should navigate to empty page with domcontentloaded', SX(async function() { - const response = await page.goto(EMPTY_PAGE, {waitUntil: 'domcontentloaded'}); + }); + it('should navigate to empty page with domcontentloaded', async({page, server}) => { + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'}); expect(response.status).toBe(200); - })); - it('should navigate to empty page with networkidle0', SX(async function() { - const response = await page.goto(EMPTY_PAGE, {waitUntil: 'networkidle0'}); + }); + it('should navigate to empty page with networkidle0', async({page, server}) => { + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'}); expect(response.status).toBe(200); - })); - it('should navigate to empty page with networkidle2', SX(async function() { - const response = await page.goto(EMPTY_PAGE, {waitUntil: 'networkidle2'}); + }); + it('should navigate to empty page with networkidle2', async({page, server}) => { + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'}); expect(response.status).toBe(200); - })); - it('should fail when navigating to bad url', SX(async function() { + }); + it('should fail when navigating to bad url', async({page, server}) => { let error = null; await page.goto('asdfasdf').catch(e => error = e); expect(error.message).toContain('Cannot navigate to invalid URL'); - })); - it('should fail when navigating to bad SSL', SX(async function() { + }); + it('should fail when navigating to bad SSL', async({page, httpsServer}) => { // Make sure that network events do not emit 'undefined'. // @see https://crbug.com/750469 page.on('request', request => expect(request).toBeTruthy()); page.on('requestfinished', request => expect(request).toBeTruthy()); page.on('requestfailed', request => expect(request).toBeTruthy()); let error = null; - await page.goto(HTTPS_PREFIX + '/empty.html').catch(e => error = e); + await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID'); - })); - it('should fail when navigating to bad SSL after redirects', SX(async function() { + }); + it('should fail when navigating to bad SSL after redirects', async({page, server, httpsServer}) => { server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html'); let error = null; - await page.goto(HTTPS_PREFIX + '/redirect/1.html').catch(e => error = e); + await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e); expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID'); - })); - it('should throw if networkidle is passed as an option', SX(async function() { + }); + it('should throw if networkidle is passed as an option', async({page, server}) => { let error = null; - await page.goto(EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err); + await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err); expect(error.message).toContain('"networkidle" option is no longer supported'); - })); - it('should fail when main resources failed to load', SX(async function() { + }); + it('should fail when main resources failed to load', async({page, server}) => { let error = null; await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e); expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); - })); - it('should fail when exceeding maximum navigation timeout', SX(async function() { + }); + it('should fail when exceeding maximum navigation timeout', async({page, server}) => { // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; - await page.goto(PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); + await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); - })); - it('should disable timeout when its set to 0', SX(async function() { + }); + it('should disable timeout when its set to 0', async({page, server}) => { let error = null; let loaded = false; page.once('load', () => loaded = true); - await page.goto(PREFIX + '/grid.html', {timeout: 0, waitUntil: ['load']}).catch(e => error = e); + await page.goto(server.PREFIX + '/grid.html', {timeout: 0, waitUntil: ['load']}).catch(e => error = e); expect(error).toBe(null); expect(loaded).toBe(true); - })); - it('should work when navigating to valid url', SX(async function() { - const response = await page.goto(EMPTY_PAGE); + }); + it('should work when navigating to valid url', async({page, server}) => { + const response = await page.goto(server.EMPTY_PAGE); expect(response.ok).toBe(true); - })); - it('should work when navigating to data url', SX(async function() { + }); + it('should work when navigating to data url', async({page, server}) => { const response = await page.goto('data:text/html,hello'); expect(response.ok).toBe(true); - })); - it('should work when navigating to 404', SX(async function() { - const response = await page.goto(PREFIX + '/not-found'); + }); + it('should work when navigating to 404', async({page, server}) => { + const response = await page.goto(server.PREFIX + '/not-found'); expect(response.ok).toBe(false); expect(response.status).toBe(404); - })); - it('should return last response in redirect chain', SX(async function() { + }); + it('should return last response in redirect chain', async({page, server}) => { server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/redirect/3.html'); - server.setRedirect('/redirect/3.html', EMPTY_PAGE); - const response = await page.goto(PREFIX + '/redirect/1.html'); + server.setRedirect('/redirect/3.html', server.EMPTY_PAGE); + const response = await page.goto(server.PREFIX + '/redirect/1.html'); expect(response.ok).toBe(true); - expect(response.url).toBe(EMPTY_PAGE); - })); - it('should wait for network idle to succeed navigation', SX(async function() { + expect(response.url).toBe(server.EMPTY_PAGE); + }); + it('should wait for network idle to succeed navigation', async({page, server}) => { let responses = []; // Hold on to a bunch of requests without answering. server.setRoute('/fetch-request-a.js', (req, res) => responses.push(res)); @@ -1070,7 +1076,7 @@ describe('Page', function() { // Navigate to a page which loads immediately and then does a bunch of // requests via javascript's fetch method. - const navigationPromise = page.goto(PREFIX + '/networkidle.html', { + const navigationPromise = page.goto(server.PREFIX + '/networkidle.html', { waitUntil: 'networkidle0', }); // Track when the navigation gets completed. @@ -1110,17 +1116,17 @@ describe('Page', function() { const response = await navigationPromise; // Expect navigation to succeed. expect(response.ok).toBe(true); - })); - it('should not leak listeners during navigation', SX(async function() { + }); + it('should not leak listeners during navigation', async({page, server}) => { let warning = null; const warningHandler = w => warning = w; process.on('warning', warningHandler); for (let i = 0; i < 20; ++i) - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); process.removeListener('warning', warningHandler); expect(warning).toBe(null); - })); - it('should not leak listeners during bad navigation', SX(async function() { + }); + it('should not leak listeners during bad navigation', async({page, server}) => { let warning = null; const warningHandler = w => warning = w; process.on('warning', warningHandler); @@ -1128,8 +1134,8 @@ describe('Page', function() { await page.goto('asdf').catch(e => {/* swallow navigation error */}); process.removeListener('warning', warningHandler); expect(warning).toBe(null); - })); - it('should navigate to dataURL and fire dataURL requests', SX(async function() { + }); + it('should navigate to dataURL and fire dataURL requests', async({page, server}) => { const requests = []; page.on('request', request => requests.push(request)); const dataURL = 'data:text/html,
yo
'; @@ -1137,33 +1143,33 @@ describe('Page', function() { expect(response.status).toBe(200); expect(requests.length).toBe(1); expect(requests[0].url).toBe(dataURL); - })); - it('should navigate to URL with hash and fire requests without hash', SX(async function() { + }); + it('should navigate to URL with hash and fire requests without hash', async({page, server}) => { const requests = []; page.on('request', request => requests.push(request)); - const response = await page.goto(EMPTY_PAGE + '#hash'); + const response = await page.goto(server.EMPTY_PAGE + '#hash'); expect(response.status).toBe(200); - expect(response.url).toBe(EMPTY_PAGE); + expect(response.url).toBe(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url).toBe(EMPTY_PAGE); - })); + expect(requests[0].url).toBe(server.EMPTY_PAGE); + }); }); describe('Page.waitForNavigation', function() { - it('should work', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const [result] = await Promise.all([ page.waitForNavigation(), - page.evaluate(url => window.location.href = url, PREFIX + '/grid.html') + page.evaluate(url => window.location.href = url, server.PREFIX + '/grid.html') ]); const response = await result; expect(response.ok).toBe(true); expect(response.url).toContain('grid.html'); - })); - it('should work with both domcontentloaded and load', SX(async function() { + }); + it('should work with both domcontentloaded and load', async({page, server}) => { let response = null; server.setRoute('/one-style.css', (req, res) => response = res); - page.goto(PREFIX + '/one-style.html'); + page.goto(server.PREFIX + '/one-style.html'); const domContentLoadedPromise = page.waitForNavigation({ waitUntil: 'domcontentloaded' }); @@ -1178,17 +1184,17 @@ describe('Page', function() { expect(bothFired).toBe(false); response.end(); await bothFiredPromise; - })); + }); }); describe('Page.goBack', function() { - it('should work', SX(async function() { - await page.goto(EMPTY_PAGE); - await page.goto(PREFIX + '/grid.html'); + it('should work', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await page.goto(server.PREFIX + '/grid.html'); let response = await page.goBack(); expect(response.ok).toBe(true); - expect(response.url).toContain(EMPTY_PAGE); + expect(response.url).toContain(server.EMPTY_PAGE); response = await page.goForward(); expect(response.ok).toBe(true); @@ -1196,11 +1202,11 @@ describe('Page', function() { response = await page.goForward(); expect(response).toBe(null); - })); + }); }); describe('Page.exposeFunction', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.exposeFunction('compute', function(a, b) { return a * b; }); @@ -1208,19 +1214,19 @@ describe('Page', function() { return await compute(9, 4); }); expect(result).toBe(36); - })); - it('should survive navigation', SX(async function() { + }); + it('should survive navigation', async({page, server}) => { await page.exposeFunction('compute', function(a, b) { return a * b; }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async function() { return await compute(9, 4); }); expect(result).toBe(36); - })); - it('should await returned promise', SX(async function() { + }); + it('should await returned promise', async({page, server}) => { await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); }); @@ -1229,21 +1235,21 @@ describe('Page', function() { return await compute(3, 5); }); expect(result).toBe(15); - })); - it('should work on frames', SX(async function() { + }); + it('should work on frames', async({page, server}) => { await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); }); - await page.goto(PREFIX + '/frames/nested-frames.html'); + await page.goto(server.PREFIX + '/frames/nested-frames.html'); const frame = page.frames()[1]; const result = await frame.evaluate(async function() { return await compute(3, 5); }); expect(result).toBe(15); - })); - it('should work on frames before navigation', SX(async function() { - await page.goto(PREFIX + '/frames/nested-frames.html'); + }); + it('should work on frames before navigation', async({page, server}) => { + await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); }); @@ -1253,11 +1259,11 @@ describe('Page', function() { return await compute(3, 5); }); expect(result).toBe(15); - })); + }); }); describe('Page.setRequestInterception', function() { - it('should intercept', SX(async function() { + it('should intercept', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { expect(request.url).toContain('empty.html'); @@ -1267,17 +1273,17 @@ describe('Page', function() { expect(request.resourceType).toBe('document'); request.continue(); }); - const response = await page.goto(EMPTY_PAGE); + const response = await page.goto(server.EMPTY_PAGE); expect(response.ok).toBe(true); - })); - it('should stop intercepting', SX(async function() { + }); + it('should stop intercepting', async({page, server}) => { await page.setRequestInterception(true); page.once('request', request => request.continue()); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); await page.setRequestInterception(false); - await page.goto(EMPTY_PAGE); - })); - it('should show custom HTTP headers', SX(async function() { + await page.goto(server.EMPTY_PAGE); + }); + it('should show custom HTTP headers', async({page, server}) => { await page.setExtraHTTPHeaders({ foo: 'bar' }); @@ -1286,10 +1292,10 @@ describe('Page', function() { expect(request.headers['foo']).toBe('bar'); request.continue(); }); - const response = await page.goto(EMPTY_PAGE); + const response = await page.goto(server.EMPTY_PAGE); expect(response.ok).toBe(true); - })); - it('should be abortable', SX(async function() { + }); + it('should be abortable', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { if (request.url.endsWith('.css')) @@ -1299,45 +1305,45 @@ describe('Page', function() { }); let failedRequests = 0; page.on('requestfailed', event => ++failedRequests); - const response = await page.goto(PREFIX + '/one-style.html'); + const response = await page.goto(server.PREFIX + '/one-style.html'); expect(response.ok).toBe(true); expect(response.request().failure()).toBe(null); expect(failedRequests).toBe(1); - })); - it('should be abortable with custom error codes', SX(async function() { + }); + it('should be abortable with custom error codes', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { request.abort('internetdisconnected'); }); let failedRequest = null; page.on('requestfailed', request => failedRequest = request); - await page.goto(EMPTY_PAGE).catch(e => {}); + await page.goto(server.EMPTY_PAGE).catch(e => {}); expect(failedRequest).toBeTruthy(); expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED'); - })); - it('should amend HTTP headers', SX(async function() { + }); + it('should amend HTTP headers', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { const headers = Object.assign({}, request.headers); headers['FOO'] = 'bar'; request.continue({ headers }); }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ server.waitForRequest('/sleep.zzz'), page.evaluate(() => fetch('/sleep.zzz')) ]); expect(request.headers['foo']).toBe('bar'); - })); - it('should fail navigation when aborting main resource', SX(async function() { + }); + it('should fail navigation when aborting main resource', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => request.abort()); let error = null; - await page.goto(EMPTY_PAGE).catch(e => error = e); + await page.goto(server.EMPTY_PAGE).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('net::ERR_FAILED'); - })); - it('should work with redirects', SX(async function() { + }); + it('should work with redirects', async({page, server}) => { await page.setRequestInterception(true); const requests = []; page.on('request', request => { @@ -1348,13 +1354,13 @@ describe('Page', function() { server.setRedirect('/non-existing-page-2.html', '/non-existing-page-3.html'); server.setRedirect('/non-existing-page-3.html', '/non-existing-page-4.html'); server.setRedirect('/non-existing-page-4.html', '/empty.html'); - const response = await page.goto(PREFIX + '/non-existing-page.html'); + const response = await page.goto(server.PREFIX + '/non-existing-page.html'); expect(response.status).toBe(200); expect(response.url).toContain('empty.html'); expect(requests.length).toBe(5); expect(requests[2].resourceType).toBe('document'); - })); - it('should be able to abort redirects', SX(async function() { + }); + it('should be able to abort redirects', async({page, server}) => { await page.setRequestInterception(true); server.setRedirect('/non-existing.json', '/non-existing-2.json'); server.setRedirect('/non-existing-2.json', '/simple.html'); @@ -1364,7 +1370,7 @@ describe('Page', function() { else request.continue(); }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(async() => { try { await fetch('/non-existing.json'); @@ -1373,9 +1379,9 @@ describe('Page', function() { } }); expect(result).toContain('Failed to fetch'); - })); - it('should work with equal requests', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + it('should work with equal requests', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); let responseCount = 1; server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + '')); await page.setRequestInterception(true); @@ -1392,8 +1398,8 @@ describe('Page', function() { fetch('/zzz').then(response => response.text()).catch(e => 'FAILED'), ])); expect(results).toEqual(['11', 'FAILED', '22']); - })); - it('should navigate to dataURL and fire dataURL requests', SX(async function() { + }); + it('should navigate to dataURL and fire dataURL requests', async({page, server}) => { await page.setRequestInterception(true); const requests = []; page.on('request', request => { @@ -1405,8 +1411,8 @@ describe('Page', function() { expect(response.status).toBe(200); expect(requests.length).toBe(1); expect(requests[0].url).toBe(dataURL); - })); - it('should abort data URLs', SX(async function() { + }); + it('should abort data server', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { request.abort(); @@ -1414,36 +1420,36 @@ describe('Page', function() { let error = null; await page.goto('data:text/html,No way!').catch(err => error = err); expect(error.message).toContain('net::ERR_FAILED'); - })); - it('should navigate to URL with hash and and fire requests without hash', SX(async function() { + }); + it('should navigate to URL with hash and and fire requests without hash', async({page, server}) => { await page.setRequestInterception(true); const requests = []; page.on('request', request => { requests.push(request); request.continue(); }); - const response = await page.goto(EMPTY_PAGE + '#hash'); + const response = await page.goto(server.EMPTY_PAGE + '#hash'); expect(response.status).toBe(200); - expect(response.url).toBe(EMPTY_PAGE); + expect(response.url).toBe(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url).toBe(EMPTY_PAGE); - })); - it('should work with encoded URLs', SX(async function() { + expect(requests[0].url).toBe(server.EMPTY_PAGE); + }); + it('should work with encoded server', async({page, server}) => { // The requestWillBeSent will report encoded URL, whereas interception will // report URL as-is. @see crbug.com/759388 await page.setRequestInterception(true); page.on('request', request => request.continue()); - const response = await page.goto(PREFIX + '/some nonexisting page'); + const response = await page.goto(server.PREFIX + '/some nonexisting page'); expect(response.status).toBe(404); - })); - it('should work with badly encoded URLs', SX(async function() { + }); + it('should work with badly encoded server', async({page, server}) => { await page.setRequestInterception(true); server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); page.on('request', request => request.continue()); - const response = await page.goto(PREFIX + '/malformed?rnd=%911'); + const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); expect(response.status).toBe(200); - })); - it('should work with encoded URLs - 2', SX(async function() { + }); + it('should work with encoded server - 2', async({page, server}) => { // The requestWillBeSent will report URL as-is, whereas interception will // report encoded URL for stylesheet. @see crbug.com/759388 await page.setRequestInterception(true); @@ -1452,17 +1458,17 @@ describe('Page', function() { request.continue(); requests.push(request); }); - const response = await page.goto(`data:text/html,`); + const response = await page.goto(`data:text/html,`); expect(response.status).toBe(200); expect(requests.length).toBe(2); expect(requests[1].response().status).toBe(404); - })); - it('should not throw "Invalid Interception Id" if the request was cancelled', SX(async function() { + }); + it('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => { await page.setContent(''); await page.setRequestInterception(true); let request = null; page.on('request', async r => request = r); - page.$eval('iframe', (frame, url) => frame.src = url, EMPTY_PAGE), + page.$eval('iframe', (frame, url) => frame.src = url, server.EMPTY_PAGE), // Wait for request interception. await waitForEvents(page, 'request'); // Delete frame to cause request to be canceled. @@ -1470,8 +1476,8 @@ describe('Page', function() { let error = null; await request.continue().catch(e => error = e); expect(error).toBe(null); - })); - it('should throw if interception is not enabled', SX(async function() { + }); + it('should throw if interception is not enabled', async({page, server}) => { let error = null; page.on('request', async request => { try { @@ -1480,13 +1486,13 @@ describe('Page', function() { error = e; } }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(error.message).toContain('Request Interception is not enabled'); - })); + }); }); describe('Request.respond', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { request.respond({ @@ -1497,12 +1503,12 @@ describe('Page', function() { body: 'Yo, page!' }); }); - const response = await page.goto(EMPTY_PAGE); + const response = await page.goto(server.EMPTY_PAGE); expect(response.status).toBe(201); expect(response.headers.foo).toBe('bar'); expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); - })); - it('should allow mocking binary responses', SX(async function() { + }); + it('should allow mocking binary responses', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); @@ -1516,14 +1522,14 @@ describe('Page', function() { img.src = PREFIX + '/does-not-exist.png'; document.body.appendChild(img); return new Promise(fulfill => img.onload = fulfill); - }, PREFIX); + }, server.PREFIX); const img = await page.$('img'); expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); - })); + }); }); describe('Page.Events.Dialog', function() { - it('should fire', SX(async function() { + it('should fire', async({page, server}) => { page.on('dialog', dialog => { expect(dialog.type).toBe('alert'); expect(dialog.defaultValue()).toBe(''); @@ -1531,8 +1537,8 @@ describe('Page', function() { dialog.accept(); }); await page.evaluate(() => alert('yo')); - })); - it('should allow accepting prompts', SX(async function() { + }); + it('should allow accepting prompts', async({page, server}) => { page.on('dialog', dialog => { expect(dialog.type).toBe('prompt'); expect(dialog.defaultValue()).toBe('yes.'); @@ -1541,44 +1547,44 @@ describe('Page', function() { }); const result = await page.evaluate(() => prompt('question?', 'yes.')); expect(result).toBe('answer!'); - })); - it('should dismiss the prompt', SX(async function() { + }); + it('should dismiss the prompt', async({page, server}) => { page.on('dialog', dialog => { dialog.dismiss(); }); const result = await page.evaluate(() => prompt('question?')); expect(result).toBe(null); - })); + }); }); describe('Page.Events.PageError', function() { - it('should fire', SX(async function() { + it('should fire', async({page, server}) => { let error = null; page.once('pageerror', e => error = e); - page.goto(PREFIX + '/error.html'); + page.goto(server.PREFIX + '/error.html'); await waitForEvents(page, 'pageerror'); expect(error.message).toContain('Fancy'); - })); + }); }); describe('Page.Events.Request', function() { - it('should fire', SX(async function() { + it('should fire', async({page, server}) => { const requests = []; page.on('request', request => requests.push(request)); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(requests.length).toBe(1); expect(requests[0].url).toContain('empty.html'); - })); + }); }); describe('Frame Management', function() { const FrameUtils = require('./frame-utils'); - it('should handle nested frames', SX(async function() { - await page.goto(PREFIX + '/frames/nested-frames.html'); + 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'); - })); - it('should send events when frames are manipulated dynamically', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + 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)); @@ -1599,28 +1605,28 @@ describe('Page', function() { await FrameUtils.detachFrame(page, 'frame1'); expect(detachedFrames.length).toBe(1); expect(detachedFrames[0].isDetached()).toBe(true); - })); - it('should persist mainFrame on cross-process navigation', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + it('should persist mainFrame on cross-process navigation', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); - await page.goto('http://127.0.0.1:' + PORT + '/empty.html'); + await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); expect(page.mainFrame() === mainFrame).toBeTruthy(); - })); - it('should not send attach/detach events for main frame', SX(async function() { + }); + it('should not send attach/detach events for main frame', async({page, server}) => { let hasEvents = false; page.on('frameattached', frame => hasEvents = true); page.on('framedetached', frame => hasEvents = true); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(hasEvents).toBe(false); - })); - it('should detach child frames on navigation', SX(async function() { + }); + it('should detach child frames on navigation', async({page, server}) => { let attachedFrames = []; let detachedFrames = []; let navigatedFrames = []; page.on('frameattached', frame => attachedFrames.push(frame)); page.on('framedetached', frame => detachedFrames.push(frame)); page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.goto(PREFIX + '/frames/nested-frames.html'); + await page.goto(server.PREFIX + '/frames/nested-frames.html'); expect(attachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(0); expect(navigatedFrames.length).toBe(5); @@ -1628,184 +1634,184 @@ describe('Page', function() { attachedFrames = []; detachedFrames = []; navigatedFrames = []; - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(attachedFrames.length).toBe(0); expect(detachedFrames.length).toBe(4); expect(navigatedFrames.length).toBe(1); - })); - it('should report frame.name()', SX(async function() { - await FrameUtils.attachFrame(page, 'theFrameId', EMPTY_PAGE); + }); + it('should report frame.name()', async({page, server}) => { + await FrameUtils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE); await page.evaluate(url => { const frame = document.createElement('iframe'); frame.name = 'theFrameName'; frame.src = url; document.body.appendChild(frame); return new Promise(x => frame.onload = x); - }, EMPTY_PAGE); + }, server.EMPTY_PAGE); expect(page.frames()[0].name()).toBe(''); expect(page.frames()[1].name()).toBe('theFrameId'); expect(page.frames()[2].name()).toBe('theFrameName'); - })); - it('should report frame.parent()', SX(async function() { - await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE); - await FrameUtils.attachFrame(page, 'frame2', EMPTY_PAGE); + }); + it('should report frame.parent()', async({page, server}) => { + await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + await FrameUtils.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()); - })); + }); }); describe('Page.$eval', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setContent('
43543
'); const idAttribute = await page.$eval('section', e => e.id); expect(idAttribute).toBe('testAttribute'); - })); - it('should accept arguments', SX(async function() { + }); + it('should accept arguments', async({page, server}) => { await page.setContent('
hello
'); const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' world!'); expect(text).toBe('hello world!'); - })); - it('should accept ElementHandles as arguments', SX(async function() { + }); + it('should accept ElementHandles as arguments', async({page, server}) => { await page.setContent('
hello
world
'); const divHandle = await page.$('div'); const text = await page.$eval('section', (e, div) => e.textContent + div.textContent, divHandle); expect(text).toBe('hello world'); - })); - it('should throw error if no element is found', SX(async function() { + }); + it('should throw error if no element is found', async({page, server}) => { let error = null; await page.$eval('section', e => e.id).catch(e => error = e); expect(error.message).toContain('failed to find element matching selector "section"'); - })); + }); }); describe('Page.$$eval', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setContent('
hello
beautiful
world!
'); const divsCount = await page.$$eval('div', divs => divs.length); expect(divsCount).toBe(3); - })); + }); }); describe('Page.$', function() { - it('should query existing element', SX(async function() { + it('should query existing element', async({page, server}) => { await page.setContent('
test
'); const element = await page.$('section'); expect(element).toBeTruthy(); - })); - it('should return null for non-existing element', SX(async function() { + }); + it('should return null for non-existing element', async({page, server}) => { const element = await page.$('non-existing-element'); expect(element).toBe(null); - })); + }); }); describe('Page.$$', function() { - it('should query existing elements', SX(async function() { + it('should query existing elements', async({page, server}) => { await page.setContent('
A

B
'); const elements = await page.$$('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 if nothing is found', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + it('should return empty array if nothing is found', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const elements = await page.$$('div'); expect(elements.length).toBe(0); - })); + }); }); describe('ElementHandle.boundingBox', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + 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', SX(async function() { + }); + it('should handle nested frames', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/frames/nested-frames.html'); + 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', SX(async function() { + }); + 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); - })); + }); }); describe('ElementHandle.click', function() { - it('should work', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + 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', SX(async function() { - await page.goto(PREFIX + '/shadow.html'); + }); + 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', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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 not visible'); - })); - it('should throw for recursively hidden nodes', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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 not visible'); - })); - it('should throw for
elements', SX(async function() { + }); + 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 not visible'); - })); + }); }); describe('ElementHandle.hover', function() { - it('should work', SX(async function() { - await page.goto(PREFIX + '/input/scrollable.html'); + 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', SX(async function() { + it('should work', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + 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', SX(async function() { + }); + it('should take into account padding and border', async({page, server}) => { await page.setViewport({width: 500, height: 500}); await page.setContent(` something above @@ -1821,8 +1827,8 @@ describe('Page', function() { const elementHandle = await page.$('div'); const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); - })); - it('should scroll element into view', SX(async function() { + }); + it('should scroll element into view', async({page, server}) => { await page.setViewport({width: 500, height: 500}); await page.setContent(` something above @@ -1844,8 +1850,8 @@ describe('Page', function() { 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', SX(async function() { + }); + it('should work with a rotated element', async({page, server}) => { await page.setViewport({width: 500, height: 500}); await page.setContent(`
A
'); 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', SX(async function() { + it('should return null for non-existing element', async({page, server}) => { await page.setContent('
B
'); const html = await page.$('html'); const second = await html.$('.third'); expect(second).toBe(null); - })); + }); }); describe('ElementHandle.$$', function() { - it('should query existing elements', SX(async 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', SX(async function() { + 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('input', function() { - it('should click the button', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + 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', SX(async function() { - await page.goto(PREFIX + '/input/checkbox.html'); + 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); @@ -1927,10 +1933,10 @@ describe('Page', function() { ]); await page.click('input#agree'); expect(await page.evaluate(() => result.check)).toBe(false); - })); + }); - it('should click on checkbox label and toggle', SX(async function() { - await page.goto(PREFIX + '/input/checkbox.html'); + 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); @@ -1940,37 +1946,37 @@ describe('Page', function() { ]); await page.click('label[for="agree"]'); expect(await page.evaluate(() => result.check)).toBe(false); - })); + }); - it('should fail to click a missing button', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + 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', SX(async function() { + 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', SX(async function() { - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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 ', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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(PREFIX + '/input/button.html'); + await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); expect(await page.evaluate(() => result)).toBe('Clicked'); - })); - it('should upload the file', SX(async function(){ - await page.goto(PREFIX + '/input/fileupload.html'); + }); + 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); @@ -1981,9 +1987,9 @@ describe('Page', function() { reader.readAsText(e.files[0]); return promise.then(() => reader.result); }, input)).toBe('contents of the file'); - })); - it('should move with the arrow keys', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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++) @@ -1996,9 +2002,9 @@ describe('Page', function() { 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', SX(async function() { - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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'); @@ -2007,18 +2013,18 @@ describe('Page', function() { await textarea.press('a', {text: 'y'}); expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('f'); - })); - it('should send a character with sendCharacter', SX(async function() { - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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', SX(async function(){ - await page.goto(PREFIX + '/input/keyboard.html'); + }); + 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) { @@ -2036,9 +2042,9 @@ describe('Page', function() { await keyboard.up(modifierKey); expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []'); } - })); - it('should report multiple modifiers', SX(async function(){ - await page.goto(PREFIX + '/input/keyboard.html'); + }); + 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]'); @@ -2052,9 +2058,9 @@ describe('Page', function() { 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', SX(async function(){ - await page.goto(PREFIX + '/input/keyboard.html'); + }); + 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 []', @@ -2065,9 +2071,9 @@ describe('Page', function() { [ 'Keydown: ^ Digit6 54 []', 'Keypress: ^ Digit6 94 94 94 []', 'Keyup: ^ Digit6 54 []'].join('\n')); - })); - it('should send proper codes while typing with shift', SX(async function(){ - await page.goto(PREFIX + '/input/keyboard.html'); + }); + 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('~'); @@ -2077,9 +2083,9 @@ describe('Page', function() { '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', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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 => { @@ -2093,8 +2099,8 @@ describe('Page', function() { }); await page.keyboard.type('Hello World!'); expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!'); - })); - it('keyboard.modifiers()', SX(async function(){ + }); + it('keyboard.modifiers()', async({page, server}) => { const keyboard = page.keyboard; expect(keyboard._modifiers).toBe(0); await keyboard.down('Shift'); @@ -2104,9 +2110,9 @@ describe('Page', function() { await keyboard.up('Shift'); await keyboard.up('Alt'); expect(keyboard._modifiers).toBe(0); - })); - it('should resize the textarea', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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); @@ -2116,16 +2122,16 @@ describe('Page', function() { 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', SX(async function(){ - await page.goto(PREFIX + '/input/scrollable.html'); + }); + 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 click a partially obscured button', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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'; @@ -2134,9 +2140,9 @@ describe('Page', function() { }); await page.click('button'); expect(await page.evaluate(() => window.result)).toBe('Clicked'); - })); - it('should select the text with mouse', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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); @@ -2147,9 +2153,9 @@ describe('Page', function() { 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', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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); @@ -2157,23 +2163,23 @@ describe('Page', function() { 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', SX(async function(){ - await page.goto(PREFIX + '/input/scrollable.html'); + }); + 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', SX(async function(){ - await page.goto(PREFIX + '/input/scrollable.html'); + }); + 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', SX(async function(){ - await page.goto(PREFIX + '/input/scrollable.html'); + }); + 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) { @@ -2188,23 +2194,23 @@ describe('Page', function() { if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) fail(modifiers[modifier] + ' should be false'); } - })); - it('should specify repeat property', SX(async function(){ - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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', {text: 'a'}); expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); await page.keyboard.press('a'); expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); - })); + }); // @see https://github.com/GoogleChrome/puppeteer/issues/206 - it('should click links which cause navigation', SX(async function() { - await page.setContent(`empty.html`); + 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', SX(async function() { + }); + it('should tween mouse movement', async({page, server}) => { await page.mouse.move(100, 100); await page.evaluate(() => { window.result = []; @@ -2220,48 +2226,48 @@ describe('Page', function() { [180, 260], [200, 300] ]); - })); - it('should tap the button', SX(async function() { - await page.goto(PREFIX + '/input/button.html'); + }); + 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', SX(async function() { - await page.goto(PREFIX + '/input/touches.html'); + }); + 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', SX(async function() { - await page.goto(EMPTY_PAGE); + }); + it('should click the button inside an iframe', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); await page.setContent('
spacer
'); const FrameUtils = require('./frame-utils'); - await FrameUtils.attachFrame(page, 'button-test', PREFIX + '/input/button.html'); + 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', SX(async function() { + }); + 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
'); const FrameUtils = require('./frame-utils'); - await FrameUtils.attachFrame(page, 'button-test', PREFIX + '/input/button.html'); + 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', SX(async function() { - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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', SX(async function() { - await page.goto(PREFIX + '/input/textarea.html'); + }); + 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); }); @@ -2278,8 +2284,8 @@ describe('Page', function() { await textarea.press('NumpadSubtract'); expect(await page.evaluate('keyLocation')).toBe(3); - })); - it('should throw on unknown keys', SX(async function() { + }); + 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"'); @@ -2288,7 +2294,7 @@ describe('Page', function() { error = await page.keyboard.press('😊').catch(e => e); expect(error && error.message).toBe('Unknown key: "😊"'); - })); + }); function dimensions() { const rect = document.querySelector('textarea').getBoundingClientRect(); return { @@ -2301,35 +2307,35 @@ describe('Page', function() { }); describe('Page.setUserAgent', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla'); page.setUserAgent('foobar'); const [request] = await Promise.all([ server.waitForRequest('/empty.html'), - page.goto(EMPTY_PAGE), + page.goto(server.EMPTY_PAGE), ]); expect(request.headers['user-agent']).toBe('foobar'); - })); - it('should emulate device user-agent', SX(async function() { - await page.goto(PREFIX + '/mobile.html'); + }); + it('should emulate device user-agent', async({page, server}) => { + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => navigator.userAgent)).toContain('Chrome'); await page.setUserAgent(iPhone.userAgent); expect(await page.evaluate(() => navigator.userAgent)).toContain('Safari'); - })); + }); }); describe('Page.setExtraHTTPHeaders', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setExtraHTTPHeaders({ foo: 'bar' }); const [request] = await Promise.all([ server.waitForRequest('/empty.html'), - page.goto(EMPTY_PAGE), + page.goto(server.EMPTY_PAGE), ]); expect(request.headers['foo']).toBe('bar'); - })); - it('should throw for non-string header values', SX(async function() { + }); + it('should throw for non-string header values', async({page, server}) => { let error = null; try { await page.setExtraHTTPHeaders({ 'foo': 1 }); @@ -2337,13 +2343,13 @@ describe('Page', function() { error = e; } expect(error.message).toBe('Expected value of header "foo" to be String, but "number" is found.'); - })); + }); }); describe('Page.authenticate', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { server.setAuth('/empty.html', 'user', 'pass'); - let response = await page.goto(EMPTY_PAGE); + let response = await page.goto(server.EMPTY_PAGE); expect(response.status).toBe(401); await page.authenticate({ username: 'user', @@ -2351,95 +2357,95 @@ describe('Page', function() { }); response = await page.reload(); expect(response.status).toBe(200); - })); - it('should fail if wrong credentials', SX(async function() { + }); + it('should fail if wrong credentials', async({page, server}) => { // Use unique user/password since Chrome caches credentials per origin. server.setAuth('/empty.html', 'user2', 'pass2'); await page.authenticate({ username: 'foo', password: 'bar' }); - const response = await page.goto(EMPTY_PAGE); + const response = await page.goto(server.EMPTY_PAGE); expect(response.status).toBe(401); - })); - it('should allow disable authentication', SX(async function() { + }); + it('should allow disable authentication', async({page, server}) => { // Use unique user/password since Chrome caches credentials per origin. server.setAuth('/empty.html', 'user3', 'pass3'); await page.authenticate({ username: 'user3', password: 'pass3' }); - let response = await page.goto(EMPTY_PAGE); + let response = await page.goto(server.EMPTY_PAGE); expect(response.status).toBe(200); await page.authenticate(null); // Navigate to a different origin to bust Chrome's credential caching. - response = await page.goto(CROSS_PROCESS_PREFIX + '/empty.html'); + response = await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); expect(response.status).toBe(401); - })); + }); }); describe('Page.setContent', function() { const expectedOutput = '
hello
'; - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setContent('
hello
'); const result = await page.content(); expect(result).toBe(expectedOutput); - })); - it('should work with doctype', SX(async function() { + }); + it('should work with doctype', async({page, server}) => { const doctype = ''; await page.setContent(`${doctype}
hello
`); const result = await page.content(); expect(result).toBe(`${doctype}${expectedOutput}`); - })); - it('should work with HTML 4 doctype', SX(async function() { + }); + it('should work with HTML 4 doctype', async({page, server}) => { const doctype = ''; await page.setContent(`${doctype}
hello
`); const result = await page.content(); expect(result).toBe(`${doctype}${expectedOutput}`); - })); + }); }); describe('Network Events', function() { - it('Page.Events.Request', SX(async function() { + it('Page.Events.Request', async({page, server}) => { const requests = []; page.on('request', request => requests.push(request)); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url).toBe(EMPTY_PAGE); + 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(); - })); - it('Page.Events.Request should report post data', SX(async function() { - await page.goto(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', SX(async function() { + }); + it('Page.Events.Response', async({page, server}) => { const responses = []; page.on('response', response => responses.push(response)); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(responses.length).toBe(1); - expect(responses[0].url).toBe(EMPTY_PAGE); + expect(responses[0].url).toBe(server.EMPTY_PAGE); expect(responses[0].status).toBe(200); expect(responses[0].ok).toBe(true); expect(responses[0].request()).toBeTruthy(); - })); - it('Page.Events.Response should provide body', SX(async function() { + }); + it('Page.Events.Response should provide body', async({page, server}) => { let response = null; page.on('response', r => response = r); - await page.goto(PREFIX + '/simple.json'); + 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', SX(async() => { - await page.goto(EMPTY_PAGE); + }); + 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) => { @@ -2468,8 +2474,8 @@ describe('Page', function() { // Finish response. await new Promise(x => serverResponse.end('ld!', x)); expect(await responseText).toBe('hello world!'); - })); - it('Page.Events.RequestFailed', SX(async function() { + }); + it('Page.Events.RequestFailed', async({page, server}) => { await page.setRequestInterception(true); page.on('request', request => { if (request.url.endsWith('css')) @@ -2479,51 +2485,51 @@ describe('Page', function() { }); const failedRequests = []; page.on('requestfailed', request => failedRequests.push(request)); - await page.goto(PREFIX + '/one-style.html'); + 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'); - })); - it('Page.Events.RequestFinished', SX(async function() { + }); + it('Page.Events.RequestFinished', async({page, server}) => { const requests = []; page.on('requestfinished', request => requests.push(request)); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(requests.length).toBe(1); - expect(requests[0].url).toBe(EMPTY_PAGE); + expect(requests[0].url).toBe(server.EMPTY_PAGE); expect(requests[0].response()).toBeTruthy(); - })); - it('should fire events in proper order', SX(async function() { + }); + 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(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); expect(events).toEqual(['request', 'response', 'requestfinished']); - })); - it('should support redirects', SX(async function() { + }); + 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 = PREFIX + '/foo.html'; + const FOO_URL = server.PREFIX + '/foo.html'; await page.goto(FOO_URL); expect(events).toEqual([ `GET ${FOO_URL}`, `302 ${FOO_URL}`, `DONE ${FOO_URL}`, - `GET ${EMPTY_PAGE}`, - `200 ${EMPTY_PAGE}`, - `DONE ${EMPTY_PAGE}` + `GET ${server.EMPTY_PAGE}`, + `200 ${server.EMPTY_PAGE}`, + `DONE ${server.EMPTY_PAGE}` ]); - })); + }); }); describe('Page.addScriptTag', function() { - it('should throw an error if no options are provided', SX(async function() { + it('should throw an error if no options are provided', async({page, server}) => { let error = null; try { await page.addScriptTag('/injectedfile.js'); @@ -2531,17 +2537,17 @@ describe('Page', function() { error = e; } expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); - })); + }); - it('should work with a url', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with a url', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ url: '/injectedfile.js' }); expect(scriptHandle.asElement()).not.toBeNull(); expect(await page.evaluate(() => __injected)).toBe(42); - })); + }); - it('should throw an error if loading from url fail', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should throw an error if loading from url fail', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); let error = null; try { await page.addScriptTag({ url: '/nonexistfile.js' }); @@ -2549,32 +2555,32 @@ describe('Page', function() { error = e; } expect(error.message).toBe('Loading script from /nonexistfile.js failed'); - })); + }); - it('should work with a path', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with a path', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); expect(scriptHandle.asElement()).not.toBeNull(); expect(await page.evaluate(() => __injected)).toBe(42); - })); + }); - it('should include sourcemap when path is provided', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should include sourcemap when path is provided', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); const result = await page.evaluate(() => __injectedError.stack); expect(result).toContain(path.join('assets', 'injectedfile.js')); - })); + }); - it('should work with content', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with content', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ content: 'window.__injected = 35;' }); expect(scriptHandle.asElement()).not.toBeNull(); expect(await page.evaluate(() => __injected)).toBe(35); - })); + }); }); describe('Page.addStyleTag', function() { - it('should throw an error if no options are provided', SX(async function() { + it('should throw an error if no options are provided', async({page, server}) => { let error = null; try { await page.addStyleTag('/injectedstyle.css'); @@ -2582,17 +2588,17 @@ describe('Page', function() { error = e; } expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); - })); + }); - it('should work with a url', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with a url', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ url: '/injectedstyle.css' }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); - })); + }); - it('should throw an error if loading from url fail', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should throw an error if loading from url fail', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); let error = null; try { await page.addStyleTag({ url: '/nonexistfile.js' }); @@ -2600,55 +2606,55 @@ describe('Page', function() { error = e; } expect(error.message).toBe('Loading style from /nonexistfile.js failed'); - })); + }); - it('should work with a path', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with a path', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); - })); + }); - it('should include sourcemap when path is provided', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should include sourcemap when path is provided', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); const styleHandle = await page.$('style'); const styleContent = await page.evaluate(style => style.innerHTML, styleHandle); expect(styleContent).toContain(path.join('assets', 'injectedstyle.css')); - })); + }); - it('should work with content', SX(async function() { - await page.goto(EMPTY_PAGE); + it('should work with content', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ content: 'body { background-color: green; }' }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(0, 128, 0)'); - })); + }); }); describe('Page.url', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { expect(page.url()).toBe('about:blank'); - await page.goto(EMPTY_PAGE); - expect(page.url()).toBe(EMPTY_PAGE); - })); + await page.goto(server.EMPTY_PAGE); + expect(page.url()).toBe(server.EMPTY_PAGE); + }); }); describe('Page.viewport', function() { - it('should get the proper viewport size', SX(async function() { + it('should get the proper viewport size', async({page, server}) => { expect(page.viewport()).toEqual({width: 800, height: 600}); await page.setViewport({width: 123, height: 456}); expect(page.viewport()).toEqual({width: 123, height: 456}); - })); - it('should support mobile emulation', SX(async function() { - await page.goto(PREFIX + '/mobile.html'); + }); + it('should support mobile emulation', async({page, server}) => { + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => window.innerWidth)).toBe(800); await page.setViewport(iPhone.viewport); expect(await page.evaluate(() => window.innerWidth)).toBe(375); await page.setViewport({width: 400, height: 300}); expect(await page.evaluate(() => window.innerWidth)).toBe(400); - })); - it('should support touch emulation', SX(async function() { - await page.goto(PREFIX + '/mobile.html'); + }); + it('should support touch emulation', async({page, server}) => { + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); await page.setViewport(iPhone.viewport); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); @@ -2668,43 +2674,43 @@ describe('Page', function() { return promise; } - })); - it('should be detectable by Modernizr', SX(async function(){ - await page.goto(PREFIX + '/detect-touch.html'); + }); + it('should be detectable by Modernizr', async({page, server}) => { + await page.goto(server.PREFIX + '/detect-touch.html'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO'); await page.setViewport(iPhone.viewport); - await page.goto(PREFIX + '/detect-touch.html'); + await page.goto(server.PREFIX + '/detect-touch.html'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); - })); - it('should support landscape emulation', SX(async function() { - await page.goto(PREFIX + '/mobile.html'); + }); + it('should support landscape emulation', async({page, server}) => { + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); await page.setViewport(iPhoneLandscape.viewport); expect(await page.evaluate(() => screen.orientation.type)).toBe('landscape-primary'); await page.setViewport({width: 100, height: 100}); expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); - })); + }); }); describe('Page.emulate', function() { - it('should work', SX(async function() { - await page.goto(PREFIX + '/mobile.html'); + it('should work', async({page, server}) => { + await page.goto(server.PREFIX + '/mobile.html'); await page.emulate(iPhone); expect(await page.evaluate(() => window.innerWidth)).toBe(375); expect(await page.evaluate(() => navigator.userAgent)).toContain('Safari'); - })); - it('should support clicking', SX(async function() { + }); + it('should support clicking', async({page, server}) => { await page.emulate(iPhone); - await page.goto(PREFIX + '/input/button.html'); + await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate(button => button.style.marginTop = '200px', button); await button.click(); expect(await page.evaluate(() => result)).toBe('Clicked'); - })); + }); }); describe('Page.emulateMedia', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { expect(await page.evaluate(() => window.matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => window.matchMedia('print').matches)).toBe(false); await page.emulateMedia('print'); @@ -2713,16 +2719,16 @@ describe('Page', function() { await page.emulateMedia(null); expect(await page.evaluate(() => window.matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => window.matchMedia('print').matches)).toBe(false); - })); - it('should throw in case of bad argument', SX(async function() { + }); + it('should throw in case of bad argument', async({page, server}) => { let error = null; await page.emulateMedia('bad').catch(e => error = e); expect(error.message).toBe('Unsupported media type: bad'); - })); + }); }); describe('Page.setJavaScriptEnabled', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setJavaScriptEnabled(false); await page.goto('data:text/html, '); let error = null; @@ -2732,42 +2738,42 @@ describe('Page', function() { await page.setJavaScriptEnabled(true); await page.goto('data:text/html, '); expect(await page.evaluate('something')).toBe('forbidden'); - })); + }); }); describe('Page.evaluateOnNewDocument', function() { - it('should evaluate before anything else on the page', SX(async function() { + it('should evaluate before anything else on the page', async({page, server}) => { await page.evaluateOnNewDocument(function(){ window.injected = 123; }); - await page.goto(PREFIX + '/tamperable.html'); + await page.goto(server.PREFIX + '/tamperable.html'); expect(await page.evaluate(() => window.result)).toBe(123); - })); + }); }); // Printing to pdf is currently only supported in headless (headless ? describe : xdescribe)('Page.pdf', function() { - it('should be able to save file', SX(async function() { + it('should be able to save file', async({page, server}) => { const outputFile = __dirname + '/assets/output.pdf'; await page.pdf({path: outputFile}); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); fs.unlinkSync(outputFile); - })); - it('should default to printing in Letter format', SX(async function() { + }); + it('should default to printing in Letter format', async({page, server}) => { const pages = await getPDFPages(await page.pdf()); expect(pages.length).toBe(1); expect(pages[0].width).toBeCloseTo(8.5, 2); expect(pages[0].height).toBeCloseTo(11, 2); - })); - it('should support setting custom format', SX(async function() { + }); + it('should support setting custom format', async({page, server}) => { const pages = await getPDFPages(await page.pdf({ format: 'a4' })); expect(pages.length).toBe(1); expect(pages[0].width).toBeCloseTo(8.27, 1); expect(pages[0].height).toBeCloseTo(11.7, 1); - })); - it('should support setting paper width and height', SX(async function() { + }); + it('should support setting paper width and height', async({page, server}) => { const pages = await getPDFPages(await page.pdf({ width: '10in', height: '10in', @@ -2775,9 +2781,9 @@ describe('Page', function() { expect(pages.length).toBe(1); expect(pages[0].width).toBeCloseTo(10, 2); expect(pages[0].height).toBeCloseTo(10, 2); - })); - it('should print multiple pages', SX(async function() { - await page.goto(PREFIX + '/grid.html'); + }); + it('should print multiple pages', async({page, server}) => { + await page.goto(server.PREFIX + '/grid.html'); // Define width and height in CSS pixels. const width = 50 * 5 + 1; const height = 50 * 5 + 1; @@ -2785,16 +2791,16 @@ describe('Page', function() { expect(pages.length).toBe(8); expect(pages[0].width).toBeCloseTo(cssPixelsToInches(width), 2); expect(pages[0].height).toBeCloseTo(cssPixelsToInches(height), 2); - })); - it('should support page ranges', SX(async function() { - await page.goto(PREFIX + '/grid.html'); + }); + it('should support page ranges', async({page, server}) => { + await page.goto(server.PREFIX + '/grid.html'); // Define width and height in CSS pixels. const width = 50 * 5 + 1; const height = 50 * 5 + 1; const pages = await getPDFPages(await page.pdf({width, height, pageRanges: '1,4-7'})); expect(pages.length).toBe(5); - })); - it('should throw if format is unknown', SX(async function() { + }); + it('should throw if format is unknown', async({page, server}) => { let error = null; try { await getPDFPages(await page.pdf({ @@ -2805,8 +2811,8 @@ describe('Page', function() { } expect(error).toBeTruthy(); expect(error.message).toContain('Unknown paper format'); - })); - it('should throw if units are unknown', SX(async function() { + }); + it('should throw if units are unknown', async({page, server}) => { let error = null; try { await getPDFPages(await page.pdf({ @@ -2818,26 +2824,26 @@ describe('Page', function() { } expect(error).toBeTruthy(); expect(error.message).toContain('Failed to parse parameter value'); - })); + }); }); describe('Page.title', function() { - it('should return the page title', SX(async function(){ - await page.goto(PREFIX + '/input/button.html'); + it('should return the page title', async({page, server}) => { + await page.goto(server.PREFIX + '/input/button.html'); expect(await page.title()).toBe('Button test'); - })); + }); }); describe('Page.screenshot', function() { - it('should work', SX(async function() { + it('should work', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); const screenshot = await page.screenshot(); expect(screenshot).toBeGolden('screenshot-sanity.png'); - })); - it('should clip rect', SX(async function() { + }); + it('should clip rect', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); const screenshot = await page.screenshot({ clip: { x: 50, @@ -2847,10 +2853,10 @@ describe('Page', function() { } }); expect(screenshot).toBeGolden('screenshot-clip-rect.png'); - })); - it('should work for offscreen clip', SX(async function() { + }); + it('should work for offscreen clip', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); const screenshot = await page.screenshot({ clip: { x: 50, @@ -2860,10 +2866,10 @@ describe('Page', function() { } }); expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); - })); - it('should run in parallel', SX(async function() { + }); + it('should run in parallel', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); const promises = []; for (let i = 0; i < 3; ++i) { promises.push(page.screenshot({ @@ -2877,20 +2883,20 @@ describe('Page', function() { } const screenshots = await Promise.all(promises); expect(screenshots[1]).toBeGolden('grid-cell-1.png'); - })); - it('should take fullPage screenshots', SX(async function() { + }); + it('should take fullPage screenshots', async({page, server}) => { await page.setViewport({width: 500, height: 500}); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); const screenshot = await page.screenshot({ fullPage: true }); expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); - })); - it('should run in parallel in multiple pages', SX(async function() { + }); + it('should run in parallel in multiple pages', async({page, server, browser}) => { const N = 2; const pages = await Promise.all(Array(N).fill(0).map(async() => { const page = await browser.newPage(); - await page.goto(PREFIX + '/grid.html'); + await page.goto(server.PREFIX + '/grid.html'); return page; })); const promises = []; @@ -2900,14 +2906,14 @@ describe('Page', function() { for (let i = 0; i < N; ++i) expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); await Promise.all(pages.map(page => page.close())); - })); - it('should allow transparency', SX(async function() { + }); + it('should allow transparency', async({page, server}) => { await page.setViewport({ width: 100, height: 100 }); - await page.goto(EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE); const screenshot = await page.screenshot({omitBackground: true}); expect(screenshot).toBeGolden('transparent.png'); - })); - it('should work with odd clip size on Retina displays', SX(async function() { + }); + it('should work with odd clip size on Retina displays', async({page, server}) => { const screenshot = await page.screenshot({ clip: { x: 0, @@ -2917,70 +2923,70 @@ describe('Page', function() { } }); expect(screenshot).toBeGolden('screenshot-clip-odd-size.png'); - })); + }); }); describe('Page.select', function() { - it('should select single option', SX(async function() { - await page.goto(PREFIX + '/input/select.html'); + it('should select single option', async({page, server}) => { + await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); - })); - it('should select multiple options', SX(async function() { - await page.goto(PREFIX + '/input/select.html'); + }); + it('should select multiple options', async({page, server}) => { + await page.goto(server.PREFIX + '/input/select.html'); await page.evaluate(() => makeMultiple()); await page.select('select', 'blue', 'green', 'red'); expect(await page.evaluate(() => result.onInput)).toEqual(['blue', 'green', 'red']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue', 'green', 'red']); - })); - it('should respect event bubbling', SX(async function() { - await page.goto(PREFIX + '/input/select.html'); + }); + it('should respect event bubbling', async({page, server}) => { + await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); expect(await page.evaluate(() => result.onBubblingInput)).toEqual(['blue']); expect(await page.evaluate(() => result.onBubblingChange)).toEqual(['blue']); - })); - it('should throw when element is not a ', async({page, server}) => { let error = null; - await page.goto(PREFIX + '/input/select.html'); + await page.goto(server.PREFIX + '/input/select.html'); await page.select('body', '').catch(e => error = e); expect(error.message).toContain('Element is not a