From edfff8cd8c1d27e719dc18389459684d8370871f Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 7 Feb 2023 18:25:33 -0800 Subject: [PATCH] test: run more tests in 'service' mode (#20734) --- .github/workflows/tests_secondary.yml | 5 - .../playwright-core/src/client/browserType.ts | 5 +- tests/library/browsertype-launch.spec.ts | 15 +- tests/library/chromium/chromium.spec.ts | 399 ----------------- .../library/chromium/connect-over-cdp.spec.ts | 420 ++++++++++++++++++ tests/library/debug-controller.spec.ts | 1 + tests/library/launcher.spec.ts | 4 +- tests/library/logger.spec.ts | 2 +- tests/library/playwright.config.ts | 4 +- tests/page/locator-misc-2.spec.ts | 1 - tests/page/page-click-scroll.spec.ts | 1 - tests/page/page-goto.spec.ts | 1 - tests/page/page-request-fulfill.spec.ts | 4 +- 13 files changed, 441 insertions(+), 421 deletions(-) create mode 100644 tests/library/chromium/connect-over-cdp.spec.ts diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index 426c0a754c..fb6e558539 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -195,11 +195,6 @@ jobs: - run: npm run build - run: npx playwright install --with-deps chromium - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - if: matrix.mode == 'service' - env: - PWTEST_MODE: ${{ matrix.mode }} - - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - if: matrix.mode != 'service' env: PWTEST_MODE: ${{ matrix.mode }} - run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index d0c3e99d2a..184bbf0089 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -68,12 +68,13 @@ export class BrowserType extends ChannelOwner imple } async launch(options: LaunchOptions = {}): Promise { + assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); + assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); + if (this._defaultConnectOptions) return await this._connectInsteadOfLaunching(this._defaultConnectOptions); const logger = options.logger || this._defaultLaunchOptions?.logger; - assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); - assert(!(options as any).port, 'Cannot specify a port without launching as a server.'); options = { ...this._defaultLaunchOptions, ...options }; const launchOptions: channels.BrowserTypeLaunchParams = { ...options, diff --git a/tests/library/browsertype-launch.spec.ts b/tests/library/browsertype-launch.spec.ts index aab8170679..fe0f16b738 100644 --- a/tests/library/browsertype-launch.spec.ts +++ b/tests/library/browsertype-launch.spec.ts @@ -35,7 +35,9 @@ it('should throw if userDataDir option is passed', async ({ browserType }) => { expect(waitError.message).toContain('userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); }); -it('should throw if userDataDir is passed as an argument', async ({ browserType }) => { +it('should throw if userDataDir is passed as an argument', async ({ mode, browserType }) => { + it.skip(mode === 'service'); + let waitError = null; await browserType.launch({ args: ['--user-data-dir=random-path', '--profile=random-path'] } as any).catch(e => waitError = e); expect(waitError.message).toContain('Pass userDataDir parameter to `browserType.launchPersistentContext'); @@ -51,7 +53,8 @@ it('should throw if port option is passed for persistent context', async ({ brow expect(error.message).toContain('Cannot specify a port without launching as a server.'); }); -it('should throw if page argument is passed', async ({ browserType, browserName }) => { +it('should throw if page argument is passed', async ({ mode, browserType, browserName }) => { + it.skip(mode === 'service'); it.skip(browserName === 'firefox'); let waitError = null; @@ -59,13 +62,17 @@ it('should throw if page argument is passed', async ({ browserType, browserName expect(waitError.message).toContain('can not specify page'); }); -it('should reject if launched browser fails immediately', async ({ browserType, asset }) => { +it('should reject if launched browser fails immediately', async ({ mode, browserType, asset }) => { + it.skip(mode === 'service'); + let waitError = null; await browserType.launch({ executablePath: asset('dummy_bad_browser_executable.js') }).catch(e => waitError = e); expect(waitError.message).toContain('== logs =='); }); -it('should reject if executable path is invalid', async ({ browserType }) => { +it('should reject if executable path is invalid', async ({ browserType, mode }) => { + it.skip(mode === 'service'); + let waitError = null; await browserType.launch({ executablePath: 'random-invalid-path' }).catch(e => waitError = e); expect(waitError.message).toContain('Failed to launch'); diff --git a/tests/library/chromium/chromium.spec.ts b/tests/library/chromium/chromium.spec.ts index c3e1ab4939..ca88d866d7 100644 --- a/tests/library/chromium/chromium.spec.ts +++ b/tests/library/chromium/chromium.spec.ts @@ -17,10 +17,6 @@ import { contextTest as test, expect } from '../../config/browserTest'; import { playwrightTest } from '../../config/browserTest'; -import http from 'http'; -import fs from 'fs'; -import { getUserAgent } from '../../../packages/playwright-core/lib/utils/userAgent'; -import { suppressCertificateWarning } from '../../config/utils'; test('should create a worker from a service worker', async ({ page, server }) => { const [worker] = await Promise.all([ @@ -168,401 +164,6 @@ playwrightTest('should close service worker together with the context', async ({ await browser.close(); }); -playwrightTest('should connect to an existing cdp session', async ({ browserType }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should cleanup artifacts dir after connectOverCDP disconnects due to ws close', async ({ browserType, toImpl, mode }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const dir = toImpl(cdpBrowser).options.artifactsDir; - const exists1 = fs.existsSync(dir); - await Promise.all([ - new Promise(f => cdpBrowser.on('disconnected', f)), - browserServer.close() - ]); - const exists2 = fs.existsSync(dir); - expect(exists1).toBe(true); - expect(exists2).toBe(false); -}); - -playwrightTest('should connectOverCDP and manage downloads in default context', async ({ browserType, toImpl, mode, server }, testInfo) => { - server.setRoute('/downloadWithFilename', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); - res.end(`Hello world`); - }); - - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - - try { - const browser = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const page = await browser.contexts()[0].newPage(); - await page.setContent(`download`); - - const [download] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - expect(download.page()).toBe(page); - expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`); - expect(download.suggestedFilename()).toBe(`file.txt`); - - const userPath = testInfo.outputPath('download.txt'); - await download.saveAs(userPath); - expect(fs.existsSync(userPath)).toBeTruthy(); - expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should connect to an existing cdp session twice', async ({ browserType, server }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const contexts1 = cdpBrowser1.contexts(); - expect(contexts1.length).toBe(1); - const [context1] = contexts1; - const page1 = await context1.newPage(); - await page1.goto(server.EMPTY_PAGE); - - const contexts2 = cdpBrowser2.contexts(); - expect(contexts2.length).toBe(1); - const [context2] = contexts2; - const page2 = await context2.newPage(); - await page2.goto(server.EMPTY_PAGE); - - expect(context1.pages().length).toBe(2); - expect(context2.pages().length).toBe(2); - - await cdpBrowser1.close(); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should connect to existing page with iframe and navigate', async ({ browserType, server }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - { - const context1 = await browserServer.newContext(); - const page = await context1.newPage(); - await page.goto(server.PREFIX + '/frames/one-frame.html'); - } - const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await contexts[0].pages()[0].goto(server.EMPTY_PAGE); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should connect to existing service workers', async ({ browserType, server }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}`, - }); - const context = cdpBrowser1.contexts()[0]; - const page = await cdpBrowser1.contexts()[0].newPage(); - const [worker] = await Promise.all([ - context.waitForEvent('serviceworker'), - page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') - ]); - expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); - await cdpBrowser1.close(); - - const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}`, - }); - const context2 = cdpBrowser2.contexts()[0]; - expect(context2.serviceWorkers().length).toBe(1); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should connect over a ws endpoint', async ({ browserType, server }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const json = await new Promise((resolve, reject) => { - http.get(`http://127.0.0.1:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: JSON.parse(json).webSocketDebuggerUrl, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - - // also connect with the depercreated wsEndpoint option - const cdpBrowser2 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, - }); - const contexts2 = cdpBrowser2.contexts(); - expect(contexts2.length).toBe(1); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should send extra headers with connect request', async ({ browserType, server }, testInfo) => { - { - const [request] = await Promise.all([ - server.waitForWebSocketConnectionRequest(), - browserType.connectOverCDP({ - wsEndpoint: `ws://localhost:${server.PORT}/ws`, - headers: { - 'User-Agent': 'Playwright', - 'foo': 'bar', - }, - timeout: 100, - }).catch(() => {}) - ]); - expect(request.headers['user-agent']).toBe('Playwright'); - expect(request.headers['foo']).toBe('bar'); - } - { - const [request] = await Promise.all([ - server.waitForWebSocketConnectionRequest(), - browserType.connectOverCDP({ - endpointURL: `ws://localhost:${server.PORT}/ws`, - headers: { - 'User-Agent': 'Playwright', - 'foo': 'bar', - }, - timeout: 100, - }).catch(() => {}) - ]); - expect(request.headers['user-agent']).toBe('Playwright'); - expect(request.headers['foo']).toBe('bar'); - } -}); - -playwrightTest('should send default User-Agent header with connect request', async ({ browserType, server }, testInfo) => { - { - const [request] = await Promise.all([ - server.waitForWebSocketConnectionRequest(), - browserType.connectOverCDP({ - wsEndpoint: `ws://localhost:${server.PORT}/ws`, - headers: { - 'foo': 'bar', - }, - timeout: 100, - }).catch(() => {}) - ]); - expect(request.headers['user-agent']).toBe(getUserAgent()); - expect(request.headers['foo']).toBe('bar'); - } -}); - -playwrightTest('should report all pages in an existing browser', async ({ browserType }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - for (let i = 0; i < 3; i++) - await contexts[0].newPage(); - await cdpBrowser.close(); - - const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - expect(cdpBrowser2.contexts()[0].pages().length).toBe(3); - - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should connect via https', async ({ browserType, httpsServer, mode }, testInfo) => { - test.skip(mode !== 'default'); // Out of process transport does not allow us to set env vars dynamically. - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - const json = await new Promise((resolve, reject) => { - http.get(`http://127.0.0.1:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); - httpsServer.setRoute('/json/version/', (req, res) => { - res.writeHead(200); - res.end(json); - }); - const oldValue = process.env['NODE_TLS_REJECT_UNAUTHORIZED']; - // https://stackoverflow.com/a/21961005/552185 - process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; - suppressCertificateWarning(); - try { - const cdpBrowser = await browserType.connectOverCDP(`https://localhost:${httpsServer.PORT}/`); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - for (let i = 0; i < 3; i++) - await contexts[0].newPage(); - await cdpBrowser.close(); - } finally { - process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue; - await browserServer.close(); - } -}); - -playwrightTest('should return valid browser from context.browser()', async ({ browserType }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://127.0.0.1:${port}/`, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - expect(contexts[0].browser()).toBe(cdpBrowser); - - const context2 = await cdpBrowser.newContext(); - expect(context2.browser()).toBe(cdpBrowser); - - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - -test('should report an expected error when the endpointURL returns a non-expected status code', async ({ browserType, server }) => { - server.setRoute('/json/version/', (req, resp) => { - resp.statusCode = 404; - resp.end(JSON.stringify({ - webSocketDebuggerUrl: 'dont-use-me', - })); - }); - await expect(browserType.connectOverCDP({ - endpointURL: server.PREFIX, - })).rejects.toThrowError(`browserType.connectOverCDP: Unexpected status 404 when connecting to ${server.PREFIX}/json/version/`); -}); - -test('should report an expected error when the endpoint URL JSON webSocketDebuggerUrl is undefined', async ({ browserType, server }) => { - server.setRoute('/json/version/', (req, resp) => { - resp.end(JSON.stringify({ - webSocketDebuggerUrl: undefined, - })); - }); - await expect(browserType.connectOverCDP({ - endpointURL: server.PREFIX, - })).rejects.toThrowError('browserType.connectOverCDP: Invalid URL'); -}); - -playwrightTest('should connect to an existing cdp session when passed as a first argument', async ({ browserType }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should use proxy with connectOverCDP', async ({ browserType, server }, testInfo) => { - server.setRoute('/target.html', async (req, res) => { - res.end('Served by the proxy'); - }); - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port, ...(process.platform === 'win32' ? ['--proxy-server=some-value'] : [])] - }); - try { - const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); - const context = await cdpBrowser.newContext({ - proxy: { server: `localhost:${server.PORT}` } - }); - const page = await context.newPage(); - await page.goto('http://non-existent.com/target.html'); - expect(await page.title()).toBe('Served by the proxy'); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - -playwrightTest('should be able to connect via localhost', async ({ browserType }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - args: ['--remote-debugging-port=' + port] - }); - try { - const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}`); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } -}); - playwrightTest('should pass args with spaces', async ({ browserType, createUserDataDir }, testInfo) => { const browser = await browserType.launchPersistentContext(await createUserDataDir(), { args: ['--user-agent=I am Foo'] diff --git a/tests/library/chromium/connect-over-cdp.spec.ts b/tests/library/chromium/connect-over-cdp.spec.ts new file mode 100644 index 0000000000..45ecb4c2c4 --- /dev/null +++ b/tests/library/chromium/connect-over-cdp.spec.ts @@ -0,0 +1,420 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * 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 { playwrightTest as test, expect } from '../../config/browserTest'; +import http from 'http'; +import fs from 'fs'; +import { getUserAgent } from '../../../packages/playwright-core/lib/utils/userAgent'; +import { suppressCertificateWarning } from '../../config/utils'; + +test.skip(({ mode }) => mode === 'service'); + +test('should connect to an existing cdp session', async ({ browserType, mode }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +test('should cleanup artifacts dir after connectOverCDP disconnects due to ws close', async ({ browserType, toImpl, mode }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const dir = toImpl(cdpBrowser).options.artifactsDir; + const exists1 = fs.existsSync(dir); + await Promise.all([ + new Promise(f => cdpBrowser.on('disconnected', f)), + browserServer.close() + ]); + const exists2 = fs.existsSync(dir); + expect(exists1).toBe(true); + expect(exists2).toBe(false); +}); + +test('should connectOverCDP and manage downloads in default context', async ({ browserType, mode, server }, testInfo) => { + server.setRoute('/downloadWithFilename', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); + res.end(`Hello world`); + }); + + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + + try { + const browser = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const page = await browser.contexts()[0].newPage(); + await page.setContent(`download`); + + const [download] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + expect(download.page()).toBe(page); + expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`); + expect(download.suggestedFilename()).toBe(`file.txt`); + + const userPath = testInfo.outputPath('download.txt'); + await download.saveAs(userPath); + expect(fs.existsSync(userPath)).toBeTruthy(); + expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); + } finally { + await browserServer.close(); + } +}); + +test('should connect to an existing cdp session twice', async ({ browserType, mode, server }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser1 = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const cdpBrowser2 = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const contexts1 = cdpBrowser1.contexts(); + expect(contexts1.length).toBe(1); + const [context1] = contexts1; + const page1 = await context1.newPage(); + await page1.goto(server.EMPTY_PAGE); + + const contexts2 = cdpBrowser2.contexts(); + expect(contexts2.length).toBe(1); + const [context2] = contexts2; + const page2 = await context2.newPage(); + await page2.goto(server.EMPTY_PAGE); + + expect(context1.pages().length).toBe(2); + expect(context2.pages().length).toBe(2); + + await cdpBrowser1.close(); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +test('should connect to existing page with iframe and navigate', async ({ browserType, mode, server }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + { + const context1 = await browserServer.newContext(); + const page = await context1.newPage(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); + } + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await contexts[0].pages()[0].goto(server.EMPTY_PAGE); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +test('should connect to existing service workers', async ({ browserType, mode, server }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser1 = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}`, + }); + const context = cdpBrowser1.contexts()[0]; + const page = await cdpBrowser1.contexts()[0].newPage(); + const [worker] = await Promise.all([ + context.waitForEvent('serviceworker'), + page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') + ]); + expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); + await cdpBrowser1.close(); + + const cdpBrowser2 = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}`, + }); + const context2 = cdpBrowser2.contexts()[0]; + expect(context2.serviceWorkers().length).toBe(1); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +test('should connect over a ws endpoint', async ({ browserType, server }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const json = await new Promise((resolve, reject) => { + http.get(`http://127.0.0.1:${port}/json/version/`, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); + }); + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: JSON.parse(json).webSocketDebuggerUrl, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + + // also connect with the depercreated wsEndpoint option + const cdpBrowser2 = await browserType.connectOverCDP({ + wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + }); + const contexts2 = cdpBrowser2.contexts(); + expect(contexts2.length).toBe(1); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +test('should send extra headers with connect request', async ({ browserType, server }, testInfo) => { + { + const [request] = await Promise.all([ + server.waitForWebSocketConnectionRequest(), + browserType.connectOverCDP({ + wsEndpoint: `ws://localhost:${server.PORT}/ws`, + headers: { + 'User-Agent': 'Playwright', + 'foo': 'bar', + }, + timeout: 100, + }).catch(() => {}) + ]); + expect(request.headers['user-agent']).toBe('Playwright'); + expect(request.headers['foo']).toBe('bar'); + } + { + const [request] = await Promise.all([ + server.waitForWebSocketConnectionRequest(), + browserType.connectOverCDP({ + endpointURL: `ws://localhost:${server.PORT}/ws`, + headers: { + 'User-Agent': 'Playwright', + 'foo': 'bar', + }, + timeout: 100, + }).catch(() => {}) + ]); + expect(request.headers['user-agent']).toBe('Playwright'); + expect(request.headers['foo']).toBe('bar'); + } +}); + +test('should send default User-Agent header with connect request', async ({ browserType, server }, testInfo) => { + { + const [request] = await Promise.all([ + server.waitForWebSocketConnectionRequest(), + browserType.connectOverCDP({ + wsEndpoint: `ws://localhost:${server.PORT}/ws`, + headers: { + 'foo': 'bar', + }, + timeout: 100, + }).catch(() => {}) + ]); + expect(request.headers['user-agent']).toBe(getUserAgent()); + expect(request.headers['foo']).toBe('bar'); + } +}); + +test('should report all pages in an existing browser', async ({ browserType }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + for (let i = 0; i < 3; i++) + await contexts[0].newPage(); + await cdpBrowser.close(); + + const cdpBrowser2 = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + expect(cdpBrowser2.contexts()[0].pages().length).toBe(3); + + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +test('should connect via https', async ({ browserType, httpsServer, mode }, testInfo) => { + test.skip(mode !== 'default'); // Out of process transport does not allow us to set env vars dynamically. + + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + const json = await new Promise((resolve, reject) => { + http.get(`http://127.0.0.1:${port}/json/version/`, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); + }); + httpsServer.setRoute('/json/version/', (req, res) => { + res.writeHead(200); + res.end(json); + }); + const oldValue = process.env['NODE_TLS_REJECT_UNAUTHORIZED']; + // https://stackoverflow.com/a/21961005/552185 + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; + suppressCertificateWarning(); + try { + const cdpBrowser = await browserType.connectOverCDP(`https://localhost:${httpsServer.PORT}/`); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + for (let i = 0; i < 3; i++) + await contexts[0].newPage(); + await cdpBrowser.close(); + } finally { + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue; + await browserServer.close(); + } +}); + +test('should return valid browser from context.browser()', async ({ browserType }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: `http://127.0.0.1:${port}/`, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + expect(contexts[0].browser()).toBe(cdpBrowser); + + const context2 = await cdpBrowser.newContext(); + expect(context2.browser()).toBe(cdpBrowser); + + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +test('should report an expected error when the endpointURL returns a non-expected status code', async ({ browserType, server }) => { + server.setRoute('/json/version/', (req, resp) => { + resp.statusCode = 404; + resp.end(JSON.stringify({ + webSocketDebuggerUrl: 'dont-use-me', + })); + }); + await expect(browserType.connectOverCDP({ + endpointURL: server.PREFIX, + })).rejects.toThrowError(`browserType.connectOverCDP: Unexpected status 404 when connecting to ${server.PREFIX}/json/version/`); +}); + +test('should report an expected error when the endpoint URL JSON webSocketDebuggerUrl is undefined', async ({ browserType, server }) => { + server.setRoute('/json/version/', (req, resp) => { + resp.end(JSON.stringify({ + webSocketDebuggerUrl: undefined, + })); + }); + await expect(browserType.connectOverCDP({ + endpointURL: server.PREFIX, + })).rejects.toThrowError('browserType.connectOverCDP: Invalid URL'); +}); + +test('should connect to an existing cdp session when passed as a first argument', async ({ browserType }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +test('should use proxy with connectOverCDP', async ({ browserType, server, mode }, testInfo) => { + server.setRoute('/target.html', async (req, res) => { + res.end('Served by the proxy'); + }); + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port, ...(process.platform === 'win32' ? ['--proxy-server=some-value'] : [])] + }); + try { + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); + const context = await cdpBrowser.newContext({ + proxy: { server: `localhost:${server.PORT}` } + }); + const page = await context.newPage(); + await page.goto('http://non-existent.com/target.html'); + expect(await page.title()).toBe('Served by the proxy'); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +test('should be able to connect via localhost', async ({ browserType }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}`); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); diff --git a/tests/library/debug-controller.spec.ts b/tests/library/debug-controller.spec.ts index b794c8ffd1..91442a4d9d 100644 --- a/tests/library/debug-controller.spec.ts +++ b/tests/library/debug-controller.spec.ts @@ -65,6 +65,7 @@ const test = baseTest.extend({ }); test.slow(true, 'All controller tests are slow'); +test.skip(({ mode }) => mode === 'service'); test('should pick element', async ({ backend, connectedBrowser }) => { const events = []; diff --git a/tests/library/launcher.spec.ts b/tests/library/launcher.spec.ts index 068f217aba..9f8a861263 100644 --- a/tests/library/launcher.spec.ts +++ b/tests/library/launcher.spec.ts @@ -41,8 +41,10 @@ it('should kill browser process on timeout after close', async ({ browserType, m expect(stalled).toBeTruthy(); }); -it('should throw a friendly error if its headed and there is no xserver on linux running', async ({ browserType, platform }) => { +it('should throw a friendly error if its headed and there is no xserver on linux running', async ({ mode, browserType, platform }) => { it.skip(platform !== 'linux'); + it.skip(mode === 'service'); + const error: Error = await browserType.launch({ headless: false, env: { diff --git a/tests/library/logger.spec.ts b/tests/library/logger.spec.ts index eeb18e068f..6ec9ae1b3f 100644 --- a/tests/library/logger.spec.ts +++ b/tests/library/logger.spec.ts @@ -17,7 +17,7 @@ import { playwrightTest as it, expect } from '../config/browserTest'; it('should log @smoke', async ({ browserType, mode }) => { - it.skip(mode === 'docker_remote', 'logger is not plumbed into the remote connection'); + it.skip(mode !== 'default', 'logger is not plumbed into the remote connection'); const log = []; const browser = await browserType.launch({ logger: { diff --git a/tests/library/playwright.config.ts b/tests/library/playwright.config.ts index bcd7eba9c1..d40ed8428d 100644 --- a/tests/library/playwright.config.ts +++ b/tests/library/playwright.config.ts @@ -72,7 +72,7 @@ const config: Config b !== browserName).map(b => new RegExp(b)); for (const folder of ['library', 'page']) { - if (mode === 'service' && folder === 'library') - continue; config.projects.push({ name: browserName, testDir: path.join(testDir, folder), diff --git a/tests/page/locator-misc-2.spec.ts b/tests/page/locator-misc-2.spec.ts index bfaefd69d6..d47c8368d8 100644 --- a/tests/page/locator-misc-2.spec.ts +++ b/tests/page/locator-misc-2.spec.ts @@ -95,7 +95,6 @@ it('should type', async ({ page }) => { }); it('should take screenshot', async ({ page, server, browserName, headless, isAndroid, mode }) => { - it.skip(mode === 'service'); it.skip(browserName === 'firefox' && !headless); it.skip(isAndroid, 'Different dpr. Remove after using 1x scale for screenshots.'); await page.setViewportSize({ width: 500, height: 500 }); diff --git a/tests/page/page-click-scroll.spec.ts b/tests/page/page-click-scroll.spec.ts index 2199391d61..f37ac4739d 100644 --- a/tests/page/page-click-scroll.spec.ts +++ b/tests/page/page-click-scroll.spec.ts @@ -94,6 +94,5 @@ it('should scroll into view span element', async ({ page }) => { foo `); await page.locator('#small').scrollIntoViewIfNeeded(); - console.log(await page.evaluate(() => window.scrollY)); expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(9000); }); \ No newline at end of file diff --git a/tests/page/page-goto.spec.ts b/tests/page/page-goto.spec.ts index efdf1fd48d..b8d99787d6 100644 --- a/tests/page/page-goto.spec.ts +++ b/tests/page/page-goto.spec.ts @@ -253,7 +253,6 @@ it('should work when page calls history API in beforeunload', async ({ page, ser }); it('should fail when navigating to bad url', async ({ mode, page, browserName }) => { - it.skip(mode === 'service', 'baseURL is inherited from webServer in config'); let error = null; await page.goto('asdfasdf').catch(e => error = e); if (browserName === 'chromium' || browserName === 'webkit') diff --git a/tests/page/page-request-fulfill.spec.ts b/tests/page/page-request-fulfill.spec.ts index 85b985026b..b82e399770 100644 --- a/tests/page/page-request-fulfill.spec.ts +++ b/tests/page/page-request-fulfill.spec.ts @@ -78,7 +78,6 @@ it('should work with status code 422', async ({ page, server }) => { }); it('should allow mocking binary responses', async ({ page, server, browserName, headless, asset, isAndroid, mode }) => { - it.skip(mode === 'service'); it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image.'); it.skip(isAndroid); @@ -100,7 +99,6 @@ it('should allow mocking binary responses', async ({ page, server, browserName, }); it('should allow mocking svg with charset', async ({ page, server, browserName, headless, isAndroid, isElectron, mode }) => { - it.skip(mode === 'service'); it.skip(browserName === 'firefox' && !headless, 'Firefox headed produces a different image.'); it.skip(isAndroid); it.skip(isElectron, 'Protocol error (Storage.getCookies): Browser context management is not supported'); @@ -122,7 +120,7 @@ it('should allow mocking svg with charset', async ({ page, server, browserName, }); it('should work with file path', async ({ page, server, asset, mode, isAndroid }) => { - it.skip(mode === 'service' || isAndroid); + it.skip(isAndroid); await page.route('**/*', route => route.fulfill({ contentType: 'shouldBeIgnored', path: asset('pptr.png') })); await page.evaluate(PREFIX => {