From c356073794ce194831d88545c6e771657ae85a46 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 24 Sep 2021 16:36:10 +0200 Subject: [PATCH 1/6] feat(test-runner): re-use context and page when debug mode is used --- src/test/cli.ts | 1 + src/test/index.ts | 101 ++++++++++++++++++++++- tests/playwright-test/playwright.spec.ts | 87 +++++++++++++++++++ 3 files changed, 186 insertions(+), 3 deletions(-) diff --git a/src/test/cli.ts b/src/test/cli.ts index 52741bda7b7af..3e38d52d274aa 100644 --- a/src/test/cli.ts +++ b/src/test/cli.ts @@ -103,6 +103,7 @@ async function createLoader(opts: { [key: string]: any }): Promise { overrides.timeout = 0; overrides.workers = 1; process.env.PWDEBUG = '1'; + process.env.PWTEST_REUSE_CONTEXT = '1'; } const loader = new Loader(defaultConfig, overrides); diff --git a/src/test/index.ts b/src/test/index.ts index ad7c81b8b0c9b..7d8051bfd71df 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -19,7 +19,7 @@ import * as path from 'path'; import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, BrowserType } from '../../types/types'; import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo } from '../../types/test'; import { rootTestType } from './testType'; -import { createGuid, removeFolders } from '../utils/utils'; +import { assert, createGuid, removeFolders } from '../utils/utils'; import { GridClient } from '../grid/gridClient'; export { expect } from './expect'; export const _baseTest: TestType<{}, {}> = rootTestType.test; @@ -31,8 +31,86 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { type WorkerAndFileFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _browserType: BrowserType; _artifactsDir: () => string, + _reuseBrowerContext: ReuseBrowerContextStorage, }; +class ReuseBrowerContextStorage { + private _browserContext?: BrowserContext; + private _uniqueOrigins = new Set(); + + isEnabled(): boolean { + return !!process.env.PWTEST_REUSE_CONTEXT; + } + + hasContext(): boolean { + return !!this._browserContext; + } + + setContext(context: BrowserContext) { + assert(!this._browserContext); + this._browserContext = context; + this._browserContext.on('page', page => page.on('framenavigated', frame => this._uniqueOrigins.add(new URL(frame.url()).origin))); + } + + async obtainContext(newContextOptions: BrowserContextOptions): Promise { + assert(this._browserContext); + let pages = this._browserContext.pages(); + const page = pages[0]; + if (this._uniqueOrigins.size > 0) { + let pageWorker = null; + if (pages.length > 1) { + await page.bringToFront(); + pageWorker = pages[1]; + } else { + pageWorker = await this._browserContext.newPage(); + pages = this._browserContext.pages(); + } + for (const origin of this._uniqueOrigins) { + await pageWorker.route(origin, route => route.fulfill({body: ``, contentType: 'text/html'}), { times: 1 }); + await pageWorker.goto(origin); + await pageWorker.evaluate(() => window.localStorage.clear()); + await pageWorker.evaluate(() => window.sessionStorage.clear()); + } + } + for (const p of pages.slice(1)) + await p.close(); + await this._browserContext.clearCookies(); + await this._applyNewContextOptions(page, newContextOptions); + await page.goto('about:blank'); + this._uniqueOrigins.clear(); + return this._browserContext; + } + + private async _applyNewContextOptions(page: Page, newOptions: BrowserContextOptions) { + const oldOptions: BrowserContextOptions = (this._browserContext as any)._options; + if ( + ( + oldOptions.viewport?.width !== newOptions.viewport?.width || + oldOptions.viewport?.height !== newOptions.viewport?.height + ) && + (newOptions.viewport?.height && newOptions.viewport?.width) + ) + await page.setViewportSize({width: newOptions.viewport?.width, height: newOptions.viewport?.height }); + const emulateMediaOptions: Partial[0]> = {}; + if (oldOptions.colorScheme !== newOptions.colorScheme) + emulateMediaOptions.colorScheme = newOptions.colorScheme; + if (oldOptions.forcedColors !== newOptions.forcedColors) + emulateMediaOptions.forcedColors = newOptions.forcedColors; + if (oldOptions.reducedMotion !== newOptions.reducedMotion) + emulateMediaOptions.reducedMotion = newOptions.reducedMotion; + if (Object.keys(emulateMediaOptions).length > 0) + await page.emulateMedia(emulateMediaOptions); + (this._browserContext as any)._options = newOptions; + } + + async obtainPage(): Promise { + assert(this._browserContext); + if (this._browserContext.pages().length === 0) + return await this._browserContext.newPage(); + return this._browserContext.pages()[0]; + } +} + export const test = _baseTest.extend({ defaultBrowserType: [ 'chromium', { scope: 'worker' } ], browserName: [ ({ defaultBrowserType }, use) => use(defaultBrowserType), { scope: 'worker' } ], @@ -311,10 +389,17 @@ export const test = _baseTest.extend({ })); }, { auto: true }], - context: async ({ browser, video, _artifactsDir }, use, testInfo) => { + _reuseBrowerContext: [new ReuseBrowerContextStorage(), { scope: 'worker' }], + + context: async ({ browser, video, _artifactsDir, _reuseBrowerContext, _combinedContextOptions }, use, testInfo) => { const hook = hookType(testInfo); if (hook) throw new Error(`"context" and "page" fixtures are not supported in ${hook}. Use browser.newContext() instead.`); + if (_reuseBrowerContext.isEnabled() && _reuseBrowerContext.hasContext()) { + const context = await _reuseBrowerContext.obtainContext(_combinedContextOptions); + await use(context); + return; + } let videoMode = typeof video === 'string' ? video : video.mode; if (videoMode === 'retry-with-video') @@ -332,8 +417,14 @@ export const test = _baseTest.extend({ const allPages: Page[] = []; context.on('page', page => allPages.push(page)); + if (_reuseBrowerContext.isEnabled()) + _reuseBrowerContext.setContext(context); + await use(context); + if (_reuseBrowerContext.isEnabled()) + return; + const prependToError = testInfo.status === 'timedOut' ? formatPendingCalls((context as any)._connection.pendingProtocolCalls()) : ''; await context.close(); @@ -366,7 +457,11 @@ export const test = _baseTest.extend({ } }, - page: async ({ context }, use) => { + page: async ({ context, _reuseBrowerContext }, use) => { + if (_reuseBrowerContext.isEnabled() && _reuseBrowerContext.hasContext()) { + await use(await _reuseBrowerContext.obtainPage()); + return; + } await use(await context.newPage()); }, }); diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts index 2a8a5311cbb13..aa63587899d7e 100644 --- a/tests/playwright-test/playwright.spec.ts +++ b/tests/playwright-test/playwright.spec.ts @@ -456,3 +456,90 @@ test('should work with video size', async ({ runInlineTest }, testInfo) => { expect(videoPlayer.videoWidth).toBe(220); expect(videoPlayer.videoHeight).toBe(110); }); + +test('should be able to re-use the context when debug mode is used', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'a.test.ts': ` + const { test } = pwt; + + test.use({ + colorScheme: 'light', + viewport: { + width: 1920, + height: 1080, + }, + }) + + const host1 = 'http://host1.com/foobar'; + + test.beforeEach(async({page, context}) => { + context.route(host1, route => route.fulfill({body: '', contentType: 'text/html'}, {times: 1})); + console.log(page._guid + '|'); + console.log(context._guid + '|'); + }) + + test('initial setup', async ({ page }) => { + await page.goto(host1); + expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); + await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(page.viewportSize()).toStrictEqual({ + width: 1920, + height: 1080, + }); + }); + + test('second run after persistent data has changed', async ({ page }) => { + await page.goto(host1); + expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); + await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(page.viewportSize()).toStrictEqual({ + width: 1920, + height: 1080, + }); + }); + + test.describe('inside a describe block', () => { + test.use({ + colorScheme: 'dark', + viewport: { + width: 1000, + height: 500, + }, + }); + test('using different options', async ({ page }) => { + await page.goto(host1); + expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false); + expect(page.viewportSize()).toStrictEqual({ + width: 1000, + height: 500, + }); + }); + }); + + test('after the describe block', async ({ page }) => { + await page.goto(host1); + await page.pause(); + expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); + expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); + expect(page.viewportSize()).toStrictEqual({ + width: 1920, + height: 1080, + }); + }); + ` + }, undefined, { + PWTEST_REUSE_CONTEXT: '1', + }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(4); + const pageIds = result.output.match(/page@(.*)\|/g); + const browserContextIds = result.output.match(/browser-context@(.*)\|/g); + expect(pageIds.length).toBe(4); + expect(new Set(pageIds).size).toBe(1); + expect(browserContextIds.length).toBe(4); + expect(new Set(browserContextIds).size).toBe(1); +}); + From eb873cd830099c6226f04c4493f962adf4d88e70 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Sat, 25 Sep 2021 14:40:48 +0200 Subject: [PATCH 2/6] review feedback --- src/test/index.ts | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 7d8051bfd71df..d828bc5395edb 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -37,6 +37,7 @@ type WorkerAndFileFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { class ReuseBrowerContextStorage { private _browserContext?: BrowserContext; private _uniqueOrigins = new Set(); + private _options?: BrowserContextOptions; isEnabled(): boolean { return !!process.env.PWTEST_REUSE_CONTEXT; @@ -49,6 +50,7 @@ class ReuseBrowerContextStorage { setContext(context: BrowserContext) { assert(!this._browserContext); this._browserContext = context; + this._options = (context as any)._options; this._browserContext.on('page', page => page.on('framenavigated', frame => this._uniqueOrigins.add(new URL(frame.url()).origin))); } @@ -65,15 +67,18 @@ class ReuseBrowerContextStorage { pageWorker = await this._browserContext.newPage(); pages = this._browserContext.pages(); } + await Promise.all(pages.slice(2).map(page => page.close())); + await pageWorker.route(url => this._uniqueOrigins.has(url.origin), route => route.fulfill({body: ``, contentType: 'text/html'})); for (const origin of this._uniqueOrigins) { - await pageWorker.route(origin, route => route.fulfill({body: ``, contentType: 'text/html'}), { times: 1 }); await pageWorker.goto(origin); await pageWorker.evaluate(() => window.localStorage.clear()); await pageWorker.evaluate(() => window.sessionStorage.clear()); } + await pageWorker.close(); + } else { + await Promise.all(pages.slice(1).map(page => page.close())); } - for (const p of pages.slice(1)) - await p.close(); + await this._browserContext.clearCookies(); await this._applyNewContextOptions(page, newContextOptions); await page.goto('about:blank'); @@ -82,25 +87,25 @@ class ReuseBrowerContextStorage { } private async _applyNewContextOptions(page: Page, newOptions: BrowserContextOptions) { - const oldOptions: BrowserContextOptions = (this._browserContext as any)._options; + assert(this._options); if ( ( - oldOptions.viewport?.width !== newOptions.viewport?.width || - oldOptions.viewport?.height !== newOptions.viewport?.height + this._options.viewport?.width !== newOptions.viewport?.width || + this._options.viewport?.height !== newOptions.viewport?.height ) && (newOptions.viewport?.height && newOptions.viewport?.width) ) await page.setViewportSize({width: newOptions.viewport?.width, height: newOptions.viewport?.height }); const emulateMediaOptions: Partial[0]> = {}; - if (oldOptions.colorScheme !== newOptions.colorScheme) + if (this._options.colorScheme !== newOptions.colorScheme) emulateMediaOptions.colorScheme = newOptions.colorScheme; - if (oldOptions.forcedColors !== newOptions.forcedColors) + if (this._options.forcedColors !== newOptions.forcedColors) emulateMediaOptions.forcedColors = newOptions.forcedColors; - if (oldOptions.reducedMotion !== newOptions.reducedMotion) + if (this._options.reducedMotion !== newOptions.reducedMotion) emulateMediaOptions.reducedMotion = newOptions.reducedMotion; if (Object.keys(emulateMediaOptions).length > 0) await page.emulateMedia(emulateMediaOptions); - (this._browserContext as any)._options = newOptions; + this._options = newOptions; } async obtainPage(): Promise { @@ -395,8 +400,14 @@ export const test = _baseTest.extend({ const hook = hookType(testInfo); if (hook) throw new Error(`"context" and "page" fixtures are not supported in ${hook}. Use browser.newContext() instead.`); - if (_reuseBrowerContext.isEnabled() && _reuseBrowerContext.hasContext()) { - const context = await _reuseBrowerContext.obtainContext(_combinedContextOptions); + if (_reuseBrowerContext.isEnabled()) { + let context: BrowserContext; + if (_reuseBrowerContext.hasContext()) { + context = await _reuseBrowerContext.obtainContext(_combinedContextOptions); + } else { + context = await browser.newContext(); + _reuseBrowerContext.setContext(context); + } await use(context); return; } @@ -417,14 +428,8 @@ export const test = _baseTest.extend({ const allPages: Page[] = []; context.on('page', page => allPages.push(page)); - if (_reuseBrowerContext.isEnabled()) - _reuseBrowerContext.setContext(context); - await use(context); - if (_reuseBrowerContext.isEnabled()) - return; - const prependToError = testInfo.status === 'timedOut' ? formatPendingCalls((context as any)._connection.pendingProtocolCalls()) : ''; await context.close(); @@ -458,7 +463,7 @@ export const test = _baseTest.extend({ }, page: async ({ context, _reuseBrowerContext }, use) => { - if (_reuseBrowerContext.isEnabled() && _reuseBrowerContext.hasContext()) { + if (_reuseBrowerContext.isEnabled()) { await use(await _reuseBrowerContext.obtainPage()); return; } From f4d7f9e5a0d76d502be0eb6c2baa173b1961d24b Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Tue, 28 Sep 2021 11:58:28 +0200 Subject: [PATCH 3/6] reuse context mode --- src/test/cli.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/cli.ts b/src/test/cli.ts index 3e38d52d274aa..38a76ae4d9a23 100644 --- a/src/test/cli.ts +++ b/src/test/cli.ts @@ -45,6 +45,7 @@ export function addTestCommand(program: Command) { command.option('--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`); command.option('--headed', `Run tests in headed browsers (default: headless)`); command.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --maxFailures=1 --headed --workers=1" options`); + command.option('--reuse-context', `Re-uses the context, useful for running multiple tests after each-other.`); command.option('-c, --config ', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`); command.option('--forbid-only', `Fail if test.only is called (default: false)`); command.option('-g, --grep ', `Only run tests matching this regular expression (default: ".*")`); @@ -96,15 +97,18 @@ async function createLoader(opts: { [key: string]: any }): Promise { } const overrides = overridesFromOptions(opts); - if (opts.headed || opts.debug) + if (opts.headed || opts.debug || opts.reuseContext) overrides.use = { headless: false }; - if (opts.debug) { + if (opts.debug || opts.reuseContext) { overrides.maxFailures = 1; overrides.timeout = 0; overrides.workers = 1; + } + if (opts.debug) process.env.PWDEBUG = '1'; + if (opts.reuseContext) process.env.PWTEST_REUSE_CONTEXT = '1'; - } + const loader = new Loader(defaultConfig, overrides); async function loadConfig(configFile: string) { From 513b96d582c0667689940f6f551147d94e015b26 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 30 Sep 2021 15:07:16 +0200 Subject: [PATCH 4/6] review feedback --- src/test/cli.ts | 4 +- src/test/index.ts | 69 +++++++++++------------- tests/playwright-test/playwright.spec.ts | 5 +- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/test/cli.ts b/src/test/cli.ts index 38a76ae4d9a23..f8b9bd2790616 100644 --- a/src/test/cli.ts +++ b/src/test/cli.ts @@ -16,7 +16,7 @@ /* eslint-disable no-console */ -import { Command } from 'commander'; +import { Command, Option } from 'commander'; import fs from 'fs'; import path from 'path'; import type { Config } from './types'; @@ -45,7 +45,7 @@ export function addTestCommand(program: Command) { command.option('--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`); command.option('--headed', `Run tests in headed browsers (default: headless)`); command.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --maxFailures=1 --headed --workers=1" options`); - command.option('--reuse-context', `Re-uses the context, useful for running multiple tests after each-other.`); + command.addOption(new Option('--reuse-context').hideHelp()); command.option('-c, --config ', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`); command.option('--forbid-only', `Fail if test.only is called (default: false)`); command.option('-g, --grep ', `Only run tests matching this regular expression (default: ".*")`); diff --git a/src/test/index.ts b/src/test/index.ts index d828bc5395edb..36331051fd8d7 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -21,6 +21,7 @@ import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWor import { rootTestType } from './testType'; import { assert, createGuid, removeFolders } from '../utils/utils'; import { GridClient } from '../grid/gridClient'; +import { Browser } from '../..'; export { expect } from './expect'; export const _baseTest: TestType<{}, {}> = rootTestType.test; @@ -38,51 +39,49 @@ class ReuseBrowerContextStorage { private _browserContext?: BrowserContext; private _uniqueOrigins = new Set(); private _options?: BrowserContextOptions; + private _pauseNavigationEventCollection = false; isEnabled(): boolean { return !!process.env.PWTEST_REUSE_CONTEXT; } - hasContext(): boolean { - return !!this._browserContext; + async obtainContext(browser: Browser, newContextOptions: BrowserContextOptions): Promise { + if (!this._browserContext) + return await this._createNewContext(browser); + return await this._refurbishExistingContext(newContextOptions); } - setContext(context: BrowserContext) { - assert(!this._browserContext); - this._browserContext = context; - this._options = (context as any)._options; - this._browserContext.on('page', page => page.on('framenavigated', frame => this._uniqueOrigins.add(new URL(frame.url()).origin))); + private async _createNewContext(browser: Browser): Promise { + this._browserContext = await browser.newContext(); + this._options = (this._browserContext as any)._options; + this._browserContext.on('page', page => page.on('framenavigated', frame => { + if (this._pauseNavigationEventCollection) + return; + this._uniqueOrigins.add(new URL(frame.url()).origin); + })); + return this._browserContext; } - async obtainContext(newContextOptions: BrowserContextOptions): Promise { + async _refurbishExistingContext(newContextOptions: BrowserContextOptions): Promise { assert(this._browserContext); - let pages = this._browserContext.pages(); + const pages = this._browserContext.pages(); const page = pages[0]; - if (this._uniqueOrigins.size > 0) { - let pageWorker = null; - if (pages.length > 1) { - await page.bringToFront(); - pageWorker = pages[1]; - } else { - pageWorker = await this._browserContext.newPage(); - pages = this._browserContext.pages(); - } - await Promise.all(pages.slice(2).map(page => page.close())); - await pageWorker.route(url => this._uniqueOrigins.has(url.origin), route => route.fulfill({body: ``, contentType: 'text/html'})); - for (const origin of this._uniqueOrigins) { - await pageWorker.goto(origin); - await pageWorker.evaluate(() => window.localStorage.clear()); - await pageWorker.evaluate(() => window.sessionStorage.clear()); - } - await pageWorker.close(); - } else { - await Promise.all(pages.slice(1).map(page => page.close())); + this._pauseNavigationEventCollection = true; + const initialOrigin = new URL(page.url()).origin; + while (this._uniqueOrigins.size > 0) { + const nextOrigin = this._uniqueOrigins.has(initialOrigin) ? initialOrigin : this._uniqueOrigins.values().next().value; + this._uniqueOrigins.delete(nextOrigin); + await page.route(nextOrigin, route => route.fulfill({ body: ``, contentType: 'text/html' })); + await page.goto(nextOrigin); + await page.evaluate(() => window.localStorage.clear()); + await page.evaluate(() => window.sessionStorage.clear()); + await page.unroute(nextOrigin); } - + await Promise.all(pages.slice(1).map(page => page.close())); await this._browserContext.clearCookies(); await this._applyNewContextOptions(page, newContextOptions); await page.goto('about:blank'); - this._uniqueOrigins.clear(); + this._pauseNavigationEventCollection = false; return this._browserContext; } @@ -95,7 +94,7 @@ class ReuseBrowerContextStorage { ) && (newOptions.viewport?.height && newOptions.viewport?.width) ) - await page.setViewportSize({width: newOptions.viewport?.width, height: newOptions.viewport?.height }); + await page.setViewportSize({ width: newOptions.viewport?.width, height: newOptions.viewport?.height }); const emulateMediaOptions: Partial[0]> = {}; if (this._options.colorScheme !== newOptions.colorScheme) emulateMediaOptions.colorScheme = newOptions.colorScheme; @@ -401,13 +400,7 @@ export const test = _baseTest.extend({ if (hook) throw new Error(`"context" and "page" fixtures are not supported in ${hook}. Use browser.newContext() instead.`); if (_reuseBrowerContext.isEnabled()) { - let context: BrowserContext; - if (_reuseBrowerContext.hasContext()) { - context = await _reuseBrowerContext.obtainContext(_combinedContextOptions); - } else { - context = await browser.newContext(); - _reuseBrowerContext.setContext(context); - } + const context = await _reuseBrowerContext.obtainContext(browser, _combinedContextOptions); await use(context); return; } diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts index aa63587899d7e..d782006011243 100644 --- a/tests/playwright-test/playwright.spec.ts +++ b/tests/playwright-test/playwright.spec.ts @@ -521,7 +521,6 @@ test('should be able to re-use the context when debug mode is used', async ({ ru test('after the describe block', async ({ page }) => { await page.goto(host1); - await page.pause(); expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); expect(page.viewportSize()).toStrictEqual({ @@ -530,9 +529,7 @@ test('should be able to re-use the context when debug mode is used', async ({ ru }); }); ` - }, undefined, { - PWTEST_REUSE_CONTEXT: '1', - }); + }, { '--reuse-context': true }); expect(result.exitCode).toBe(0); expect(result.passed).toBe(4); const pageIds = result.output.match(/page@(.*)\|/g); From ac359eeb8e14bcecfa9916f9f4019cf1f311da75 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 30 Sep 2021 23:17:04 +0200 Subject: [PATCH 5/6] review --- src/test/index.ts | 40 ++++++++++-------------- tests/playwright-test/playwright.spec.ts | 4 --- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/test/index.ts b/src/test/index.ts index 36331051fd8d7..9235b1bcdac14 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -67,21 +67,24 @@ class ReuseBrowerContextStorage { const pages = this._browserContext.pages(); const page = pages[0]; this._pauseNavigationEventCollection = true; - const initialOrigin = new URL(page.url()).origin; - while (this._uniqueOrigins.size > 0) { - const nextOrigin = this._uniqueOrigins.has(initialOrigin) ? initialOrigin : this._uniqueOrigins.values().next().value; - this._uniqueOrigins.delete(nextOrigin); - await page.route(nextOrigin, route => route.fulfill({ body: ``, contentType: 'text/html' })); - await page.goto(nextOrigin); - await page.evaluate(() => window.localStorage.clear()); - await page.evaluate(() => window.sessionStorage.clear()); - await page.unroute(nextOrigin); + try { + const initialOrigin = new URL(page.url()).origin; + await page.route('**/*', route => route.fulfill({ body: ``, contentType: 'text/html' })); + while (this._uniqueOrigins.size > 0) { + const nextOrigin = this._uniqueOrigins.has(initialOrigin) ? initialOrigin : this._uniqueOrigins.values().next().value; + this._uniqueOrigins.delete(nextOrigin); + await page.goto(nextOrigin); + await page.evaluate(() => window.localStorage.clear()); + await page.evaluate(() => window.sessionStorage.clear()); + } + await page.unroute('**/*'); + await Promise.all(pages.slice(1).map(page => page.close())); + await this._browserContext.clearCookies(); + await this._applyNewContextOptions(page, newContextOptions); + await page.goto('about:blank'); + } finally { + this._pauseNavigationEventCollection = false; } - await Promise.all(pages.slice(1).map(page => page.close())); - await this._browserContext.clearCookies(); - await this._applyNewContextOptions(page, newContextOptions); - await page.goto('about:blank'); - this._pauseNavigationEventCollection = false; return this._browserContext; } @@ -95,15 +98,6 @@ class ReuseBrowerContextStorage { (newOptions.viewport?.height && newOptions.viewport?.width) ) await page.setViewportSize({ width: newOptions.viewport?.width, height: newOptions.viewport?.height }); - const emulateMediaOptions: Partial[0]> = {}; - if (this._options.colorScheme !== newOptions.colorScheme) - emulateMediaOptions.colorScheme = newOptions.colorScheme; - if (this._options.forcedColors !== newOptions.forcedColors) - emulateMediaOptions.forcedColors = newOptions.forcedColors; - if (this._options.reducedMotion !== newOptions.reducedMotion) - emulateMediaOptions.reducedMotion = newOptions.reducedMotion; - if (Object.keys(emulateMediaOptions).length > 0) - await page.emulateMedia(emulateMediaOptions); this._options = newOptions; } diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts index d782006011243..31c1a13a95fb5 100644 --- a/tests/playwright-test/playwright.spec.ts +++ b/tests/playwright-test/playwright.spec.ts @@ -482,7 +482,6 @@ test('should be able to re-use the context when debug mode is used', async ({ ru await page.goto(host1); expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); - expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); expect(page.viewportSize()).toStrictEqual({ width: 1920, height: 1080, @@ -493,7 +492,6 @@ test('should be able to re-use the context when debug mode is used', async ({ ru await page.goto(host1); expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); - expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); expect(page.viewportSize()).toStrictEqual({ width: 1920, height: 1080, @@ -511,7 +509,6 @@ test('should be able to re-use the context when debug mode is used', async ({ ru test('using different options', async ({ page }) => { await page.goto(host1); expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false); expect(page.viewportSize()).toStrictEqual({ width: 1000, height: 500, @@ -522,7 +519,6 @@ test('should be able to re-use the context when debug mode is used', async ({ ru test('after the describe block', async ({ page }) => { await page.goto(host1); expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true); expect(page.viewportSize()).toStrictEqual({ width: 1920, height: 1080, From aedbe9f17ad9208629e2dec40348e883d687e737 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Fri, 1 Oct 2021 08:50:34 +0200 Subject: [PATCH 6/6] fixit --- .github/workflows/tests_primary.yml | 3 +++ src/test/index.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests_primary.yml b/.github/workflows/tests_primary.yml index b223309051849..80a52bc96fb40 100644 --- a/.github/workflows/tests_primary.yml +++ b/.github/workflows/tests_primary.yml @@ -66,3 +66,6 @@ jobs: - run: npm run build - run: node lib/cli/cli install --with-deps - run: npm run ttest + if: matrix.os != 'ubuntu-latest' + - run: xvfb-run npm run ttest + if: matrix.os == 'ubuntu-latest' diff --git a/src/test/index.ts b/src/test/index.ts index 9235b1bcdac14..8d43fc45339f9 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -79,9 +79,9 @@ class ReuseBrowerContextStorage { } await page.unroute('**/*'); await Promise.all(pages.slice(1).map(page => page.close())); + await page.goto('about:blank'); await this._browserContext.clearCookies(); await this._applyNewContextOptions(page, newContextOptions); - await page.goto('about:blank'); } finally { this._pauseNavigationEventCollection = false; }