diff options
Diffstat (limited to 'remote/test/puppeteer/test/src/headful.spec.ts')
-rw-r--r-- | remote/test/puppeteer/test/src/headful.spec.ts | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/remote/test/puppeteer/test/src/headful.spec.ts b/remote/test/puppeteer/test/src/headful.spec.ts new file mode 100644 index 0000000000..f75654f1e1 --- /dev/null +++ b/remote/test/puppeteer/test/src/headful.spec.ts @@ -0,0 +1,438 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {mkdtemp} from 'fs/promises'; +import os from 'os'; +import path from 'path'; + +import expect from 'expect'; +import { + PuppeteerLaunchOptions, + PuppeteerNode, +} from 'puppeteer-core/internal/node/PuppeteerNode.js'; +import {rmSync} from 'puppeteer-core/internal/node/util/fs.js'; + +import {getTestState} from './mocha-utils.js'; + +const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); + +const extensionPath = path.join(__dirname, '../assets', 'simple-extension'); +const serviceWorkerExtensionPath = path.join( + __dirname, + '..', + 'assets', + 'serviceworkers', + 'extension' +); + +describe('headful tests', function () { + /* These tests fire up an actual browser so let's + * allow a higher timeout + */ + this.timeout(20 * 1000); + + let headfulOptions: PuppeteerLaunchOptions | undefined; + let headlessOptions: PuppeteerLaunchOptions & {headless: boolean}; + let extensionOptions: PuppeteerLaunchOptions & { + headless: boolean; + args: string[]; + }; + let forcedOopifOptions: PuppeteerLaunchOptions & { + headless: boolean; + devtools: boolean; + args: string[]; + }; + let devtoolsOptions: PuppeteerLaunchOptions & { + headless: boolean; + devtools: boolean; + }; + const browsers: any[] = []; + + beforeEach(() => { + const {server, defaultBrowserOptions} = getTestState(); + headfulOptions = Object.assign({}, defaultBrowserOptions, { + headless: false, + }); + headlessOptions = Object.assign({}, defaultBrowserOptions, { + headless: true, + }); + + extensionOptions = Object.assign({}, defaultBrowserOptions, { + headless: false, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + ], + }); + + forcedOopifOptions = Object.assign({}, defaultBrowserOptions, { + headless: false, + devtools: true, + args: [ + `--host-rules=MAP oopifdomain 127.0.0.1`, + `--isolate-origins=${server.PREFIX.replace( + 'localhost', + 'oopifdomain' + )}`, + ], + }); + + devtoolsOptions = Object.assign({}, defaultBrowserOptions, { + headless: false, + devtools: true, + }); + }); + + async function launchBrowser(puppeteer: PuppeteerNode, options: any) { + const browser = await puppeteer.launch(options); + browsers.push(browser); + return browser; + } + + afterEach(() => { + for (const i in browsers) { + const browser = browsers[i]; + if (browser.isConnected()) { + browser.close(); + } + delete browsers[i]; + } + }); + + describe('HEADFUL', function () { + it('background_page target type should be available', async () => { + const {puppeteer} = getTestState(); + const browserWithExtension = await launchBrowser( + puppeteer, + extensionOptions + ); + const page = await browserWithExtension.newPage(); + const backgroundPageTarget = await browserWithExtension.waitForTarget( + target => { + return target.type() === 'background_page'; + } + ); + await page.close(); + await browserWithExtension.close(); + expect(backgroundPageTarget).toBeTruthy(); + }); + it('service_worker target type should be available', async () => { + const {puppeteer, defaultBrowserOptions} = getTestState(); + const browserWithExtension = await launchBrowser(puppeteer, { + ...defaultBrowserOptions, + headless: false, + args: [ + `--disable-extensions-except=${serviceWorkerExtensionPath}`, + `--load-extension=${serviceWorkerExtensionPath}`, + ], + }); + const page = await browserWithExtension.newPage(); + const serviceWorkerTarget = await browserWithExtension.waitForTarget( + target => { + return target.type() === 'service_worker'; + } + ); + await page.close(); + await browserWithExtension.close(); + expect(serviceWorkerTarget).toBeTruthy(); + }); + it('target.page() should return a background_page', async function () { + const {puppeteer} = getTestState(); + const browserWithExtension = await launchBrowser( + puppeteer, + extensionOptions + ); + const backgroundPageTarget = await browserWithExtension.waitForTarget( + target => { + return target.type() === 'background_page'; + } + ); + const page = (await backgroundPageTarget.page())!; + expect( + await page.evaluate(() => { + return 2 * 3; + }) + ).toBe(6); + expect( + await page.evaluate(() => { + return (globalThis as any).MAGIC; + }) + ).toBe(42); + await browserWithExtension.close(); + }); + it('target.page() should return a DevTools page if custom isPageTarget is provided', async function () { + const {puppeteer} = getTestState(); + const originalBrowser = await launchBrowser(puppeteer, devtoolsOptions); + + const browserWSEndpoint = originalBrowser.wsEndpoint(); + + const browser = await puppeteer.connect({ + browserWSEndpoint, + _isPageTarget(target) { + return ( + target.type === 'other' && target.url.startsWith('devtools://') + ); + }, + }); + const devtoolsPageTarget = await browser.waitForTarget(target => { + return target.type() === 'other'; + }); + const page = (await devtoolsPageTarget.page())!; + expect( + await page.evaluate(() => { + return 2 * 3; + }) + ).toBe(6); + expect(await browser.pages()).toContainEqual(page); + await browser.close(); + }); + it('should have default url when launching browser', async function () { + const {puppeteer} = getTestState(); + const browser = await launchBrowser(puppeteer, extensionOptions); + const pages = (await browser.pages()).map((page: {url: () => any}) => { + return page.url(); + }); + expect(pages).toEqual(['about:blank']); + await browser.close(); + }); + it('headless should be able to read cookies written by headful', async () => { + /* Needs investigation into why but this fails consistently on Windows CI. */ + const {server, puppeteer} = getTestState(); + + const userDataDir = await mkdtemp(TMP_FOLDER); + // Write a cookie in headful chrome + const headfulBrowser = await launchBrowser( + puppeteer, + Object.assign({userDataDir}, headfulOptions) + ); + const headfulPage = await headfulBrowser.newPage(); + await headfulPage.goto(server.EMPTY_PAGE); + await headfulPage.evaluate(() => { + return (document.cookie = + 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT'); + }); + await headfulBrowser.close(); + // Read the cookie from headless chrome + const headlessBrowser = await launchBrowser( + puppeteer, + Object.assign({userDataDir}, headlessOptions) + ); + const headlessPage = await headlessBrowser.newPage(); + await headlessPage.goto(server.EMPTY_PAGE); + const cookie = await headlessPage.evaluate(() => { + return document.cookie; + }); + await headlessBrowser.close(); + // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 + try { + rmSync(userDataDir); + } catch {} + expect(cookie).toBe('foo=true'); + }); + // TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548 + it.skip('OOPIF: should report google.com frame', async () => { + const {server, puppeteer} = getTestState(); + + // https://google.com is isolated by default in Chromium embedder. + const browser = await launchBrowser(puppeteer, headfulOptions); + const page = await browser.newPage(); + await page.goto(server.EMPTY_PAGE); + await page.setRequestInterception(true); + page.on('request', (r: {respond: (arg0: {body: string}) => any}) => { + return r.respond({body: 'YO, GOOGLE.COM'}); + }); + await page.evaluate(() => { + const frame = document.createElement('iframe'); + frame.setAttribute('src', 'https://google.com/'); + document.body.appendChild(frame); + return new Promise(x => { + return (frame.onload = x); + }); + }); + await page.waitForSelector('iframe[src="https://google.com/"]'); + const urls = page + .frames() + .map((frame: {url: () => any}) => { + return frame.url(); + }) + .sort(); + expect(urls).toEqual([server.EMPTY_PAGE, 'https://google.com/']); + await browser.close(); + }); + it('OOPIF: should expose events within OOPIFs', async () => { + const {server, puppeteer} = getTestState(); + + const browser = await launchBrowser(puppeteer, forcedOopifOptions); + const page = await browser.newPage(); + + // Setup our session listeners to observe OOPIF activity. + const session = await page.target().createCDPSession(); + const networkEvents: any[] = []; + const otherSessions: any[] = []; + await session.send('Target.setAutoAttach', { + autoAttach: true, + flatten: true, + waitForDebuggerOnStart: true, + }); + session.on( + 'sessionattached', + async (session: { + on: (arg0: string, arg1: (params: any) => number) => void; + send: (arg0: string) => any; + }) => { + otherSessions.push(session); + + session.on('Network.requestWillBeSent', (params: any) => { + return networkEvents.push(params); + }); + await session.send('Network.enable'); + await session.send('Runtime.runIfWaitingForDebugger'); + } + ); + + // Navigate to the empty page and add an OOPIF iframe with at least one request. + await page.goto(server.EMPTY_PAGE); + await page.evaluate((frameUrl: string) => { + const frame = document.createElement('iframe'); + frame.setAttribute('src', frameUrl); + document.body.appendChild(frame); + return new Promise((x, y) => { + frame.onload = x; + frame.onerror = y; + }); + }, server.PREFIX.replace('localhost', 'oopifdomain') + '/one-style.html'); + await page.waitForSelector('iframe'); + + // Ensure we found the iframe session. + expect(otherSessions).toHaveLength(1); + + // Resume the iframe and trigger another request. + const iframeSession = otherSessions[0]!; + await iframeSession.send('Runtime.evaluate', { + expression: `fetch('/fetch')`, + awaitPromise: true, + }); + await browser.close(); + + const requests = networkEvents.map(event => { + return event.request.url; + }); + expect(requests).toContain(`http://oopifdomain:${server.PORT}/fetch`); + }); + it('should close browser with beforeunload page', async () => { + const {server, puppeteer} = getTestState(); + + const browser = await launchBrowser(puppeteer, headfulOptions); + const page = await browser.newPage(); + await page.goto(server.PREFIX + '/beforeunload.html'); + // We have to interact with a page so that 'beforeunload' handlers + // fire. + await page.click('body'); + await browser.close(); + }); + it('should open devtools when "devtools: true" option is given', async () => { + const {puppeteer} = getTestState(); + + const browser = await launchBrowser( + puppeteer, + Object.assign({devtools: true}, headfulOptions) + ); + const context = await browser.createIncognitoBrowserContext(); + await Promise.all([ + context.newPage(), + browser.waitForTarget((target: {url: () => string | string[]}) => { + return target.url().includes('devtools://'); + }), + ]); + await browser.close(); + }); + }); + + describe('Page.bringToFront', function () { + it('should work', async () => { + const {puppeteer} = getTestState(); + const browser = await launchBrowser(puppeteer, headfulOptions); + const page1 = await browser.newPage(); + const page2 = await browser.newPage(); + + await page1.bringToFront(); + expect( + await page1.evaluate(() => { + return document.visibilityState; + }) + ).toBe('visible'); + expect( + await page2.evaluate(() => { + return document.visibilityState; + }) + ).toBe('hidden'); + + await page2.bringToFront(); + expect( + await page1.evaluate(() => { + return document.visibilityState; + }) + ).toBe('hidden'); + expect( + await page2.evaluate(() => { + return document.visibilityState; + }) + ).toBe('visible'); + + await page1.close(); + await page2.close(); + await browser.close(); + }); + }); + + describe('Page.screenshot', function () { + it('should run in parallel in multiple pages', async () => { + const {server, puppeteer} = getTestState(); + const browser = await puppeteer.launch(headfulOptions); + const context = await browser.createIncognitoBrowserContext(); + + const N = 2; + const pages = await Promise.all( + Array(N) + .fill(0) + .map(async () => { + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + return page; + }) + ); + const promises = []; + for (let i = 0; i < N; ++i) { + promises.push( + pages[i]!.screenshot({ + clip: {x: 50 * i, y: 0, width: 50, height: 50}, + }) + ); + } + const screenshots = await Promise.all(promises); + for (let i = 0; i < N; ++i) { + expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); + } + await Promise.all( + pages.map(page => { + return page.close(); + }) + ); + + await browser.close(); + }); + }); +}); |