From 3939b9f36e2fa0305e431e50d94fd32364b5df36 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 12 Jul 2022 13:30:24 -0800 Subject: [PATCH] chore: migrate component testing to server-side page reuse (#15477) --- .../src/client/browserContext.ts | 23 ------- packages/playwright-core/src/client/page.ts | 23 ------- .../playwright-core/src/protocol/channels.ts | 16 ----- .../playwright-core/src/protocol/protocol.yml | 8 --- .../playwright-core/src/protocol/validator.ts | 8 --- .../src/server/browserContext.ts | 36 +++++++--- .../dispatchers/browserContextDispatcher.ts | 8 --- .../server/dispatchers/browserDispatcher.ts | 4 +- .../src/server/dispatchers/pageDispatcher.ts | 8 --- packages/playwright-core/src/server/page.ts | 14 ++-- packages/playwright-test/src/index.ts | 15 ++-- packages/playwright-test/src/mount.ts | 69 +++---------------- .../browsercontext-expose-function.spec.ts | 19 +++-- tests/page/page-expose-function.spec.ts | 23 ------- .../playwright.ct-reuse.spec.ts | 10 +-- 15 files changed, 69 insertions(+), 215 deletions(-) diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index c3d633aa16..1de5976956 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -244,23 +244,11 @@ export class BrowserContext extends ChannelOwner await this._channel.addInitScript({ source }); } - async _removeInitScripts() { - await this._channel.removeInitScripts(); - } - async exposeBinding(name: string, callback: (source: structs.BindingSource, ...args: any[]) => any, options: { handle?: boolean } = {}): Promise { await this._channel.exposeBinding({ name, needsHandle: options.handle }); this._bindings.set(name, callback); } - async _removeExposedBindings() { - for (const key of this._bindings.keys()) { - if (!key.startsWith('__pw_')) - this._bindings.delete(key); - } - await this._channel.removeExposedBindings(); - } - async exposeFunction(name: string, callback: Function): Promise { await this._channel.exposeBinding({ name }); const binding = (source: structs.BindingSource, ...args: any[]) => callback(...args); @@ -301,11 +289,6 @@ export class BrowserContext extends ChannelOwner await this._disableInterception(); } - async _unrouteAll() { - this._routes = []; - await this._disableInterception(); - } - private async _disableInterception() { await this._channel.setNetworkInterceptionEnabled({ enabled: false }); } @@ -395,12 +378,6 @@ export class BrowserContext extends ChannelOwner }) { await this._channel.recorderSupplementEnable(params); } - - async _resetForReuse() { - await this._unrouteAll(); - await this._removeInitScripts(); - await this._removeExposedBindings(); - } } async function prepareStorageState(options: BrowserContextOptions): Promise { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 822b7c9b0a..45a917eb76 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -329,14 +329,6 @@ export class Page extends ChannelOwner implements api.Page this._bindings.set(name, callback); } - async _removeExposedBindings() { - for (const key of this._bindings.keys()) { - if (!key.startsWith('__pw_')) - this._bindings.delete(key); - } - await this._channel.removeExposedBindings(); - } - async setExtraHTTPHeaders(headers: Headers) { validateHeaders(headers); await this._channel.setExtraHTTPHeaders({ headers: headersObjectToArray(headers) }); @@ -457,10 +449,6 @@ export class Page extends ChannelOwner implements api.Page await this._channel.addInitScript({ source }); } - async _removeInitScripts() { - await this._channel.removeInitScripts(); - } - async route(url: URLMatch, handler: RouteHandlerCallback, options: { times?: number } = {}): Promise { this._routes.unshift(new RouteHandler(this._browserContext._options.baseURL, url, handler, options.times)); if (this._routes.length === 1) @@ -482,11 +470,6 @@ export class Page extends ChannelOwner implements api.Page await this._disableInterception(); } - async _unrouteAll() { - this._routes = []; - await this._disableInterception(); - } - private async _disableInterception() { await this._channel.setNetworkInterceptionEnabled({ enabled: false }); } @@ -736,12 +719,6 @@ export class Page extends ChannelOwner implements api.Page } return result.pdf; } - - async _resetForReuse() { - await this._unrouteAll(); - await this._removeInitScripts(); - await this._removeExposedBindings(); - } } export class BindingCall extends ChannelOwner { diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index 04201906b6..9c884dac5d 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -1227,13 +1227,11 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT _type_BrowserContext: boolean; addCookies(params: BrowserContextAddCookiesParams, metadata?: Metadata): Promise; addInitScript(params: BrowserContextAddInitScriptParams, metadata?: Metadata): Promise; - removeInitScripts(params?: BrowserContextRemoveInitScriptsParams, metadata?: Metadata): Promise; clearCookies(params?: BrowserContextClearCookiesParams, metadata?: Metadata): Promise; clearPermissions(params?: BrowserContextClearPermissionsParams, metadata?: Metadata): Promise; close(params?: BrowserContextCloseParams, metadata?: Metadata): Promise; cookies(params: BrowserContextCookiesParams, metadata?: Metadata): Promise; exposeBinding(params: BrowserContextExposeBindingParams, metadata?: Metadata): Promise; - removeExposedBindings(params?: BrowserContextRemoveExposedBindingsParams, metadata?: Metadata): Promise; grantPermissions(params: BrowserContextGrantPermissionsParams, metadata?: Metadata): Promise; newPage(params?: BrowserContextNewPageParams, metadata?: Metadata): Promise; setDefaultNavigationTimeoutNoReply(params: BrowserContextSetDefaultNavigationTimeoutNoReplyParams, metadata?: Metadata): Promise; @@ -1305,9 +1303,6 @@ export type BrowserContextAddInitScriptOptions = { }; export type BrowserContextAddInitScriptResult = void; -export type BrowserContextRemoveInitScriptsParams = {}; -export type BrowserContextRemoveInitScriptsOptions = {}; -export type BrowserContextRemoveInitScriptsResult = void; export type BrowserContextClearCookiesParams = {}; export type BrowserContextClearCookiesOptions = {}; export type BrowserContextClearCookiesResult = void; @@ -1334,9 +1329,6 @@ export type BrowserContextExposeBindingOptions = { needsHandle?: boolean, }; export type BrowserContextExposeBindingResult = void; -export type BrowserContextRemoveExposedBindingsParams = {}; -export type BrowserContextRemoveExposedBindingsOptions = {}; -export type BrowserContextRemoveExposedBindingsResult = void; export type BrowserContextGrantPermissionsParams = { permissions: string[], origin?: string, @@ -1529,11 +1521,9 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { setDefaultTimeoutNoReply(params: PageSetDefaultTimeoutNoReplyParams, metadata?: Metadata): Promise; setFileChooserInterceptedNoReply(params: PageSetFileChooserInterceptedNoReplyParams, metadata?: Metadata): Promise; addInitScript(params: PageAddInitScriptParams, metadata?: Metadata): Promise; - removeInitScripts(params?: PageRemoveInitScriptsParams, metadata?: Metadata): Promise; close(params: PageCloseParams, metadata?: Metadata): Promise; emulateMedia(params: PageEmulateMediaParams, metadata?: Metadata): Promise; exposeBinding(params: PageExposeBindingParams, metadata?: Metadata): Promise; - removeExposedBindings(params?: PageRemoveExposedBindingsParams, metadata?: Metadata): Promise; goBack(params: PageGoBackParams, metadata?: Metadata): Promise; goForward(params: PageGoForwardParams, metadata?: Metadata): Promise; reload(params: PageReloadParams, metadata?: Metadata): Promise; @@ -1631,9 +1621,6 @@ export type PageAddInitScriptOptions = { }; export type PageAddInitScriptResult = void; -export type PageRemoveInitScriptsParams = {}; -export type PageRemoveInitScriptsOptions = {}; -export type PageRemoveInitScriptsResult = void; export type PageCloseParams = { runBeforeUnload?: boolean, }; @@ -1662,9 +1649,6 @@ export type PageExposeBindingOptions = { needsHandle?: boolean, }; export type PageExposeBindingResult = void; -export type PageRemoveExposedBindingsParams = {}; -export type PageRemoveExposedBindingsOptions = {}; -export type PageRemoveExposedBindingsResult = void; export type PageGoBackParams = { timeout?: number, waitUntil?: LifecycleEvent, diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index f31e8c6071..6d4324a8e9 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -850,8 +850,6 @@ BrowserContext: parameters: source: string - removeInitScripts: - clearCookies: clearPermissions: @@ -873,8 +871,6 @@ BrowserContext: name: string needsHandle: boolean? - removeExposedBindings: - grantPermissions: parameters: permissions: @@ -1061,8 +1057,6 @@ Page: parameters: source: string - removeInitScripts: - close: parameters: runBeforeUnload: boolean? @@ -1104,8 +1098,6 @@ Page: name: string needsHandle: boolean? - removeExposedBindings: - goBack: parameters: timeout: number? diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index ab56a76845..03edadd0ed 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -695,8 +695,6 @@ scheme.BrowserContextAddInitScriptParams = tObject({ source: tString, }); scheme.BrowserContextAddInitScriptResult = tOptional(tObject({})); -scheme.BrowserContextRemoveInitScriptsParams = tOptional(tObject({})); -scheme.BrowserContextRemoveInitScriptsResult = tOptional(tObject({})); scheme.BrowserContextClearCookiesParams = tOptional(tObject({})); scheme.BrowserContextClearCookiesResult = tOptional(tObject({})); scheme.BrowserContextClearPermissionsParams = tOptional(tObject({})); @@ -714,8 +712,6 @@ scheme.BrowserContextExposeBindingParams = tObject({ needsHandle: tOptional(tBoolean), }); scheme.BrowserContextExposeBindingResult = tOptional(tObject({})); -scheme.BrowserContextRemoveExposedBindingsParams = tOptional(tObject({})); -scheme.BrowserContextRemoveExposedBindingsResult = tOptional(tObject({})); scheme.BrowserContextGrantPermissionsParams = tObject({ permissions: tArray(tString), origin: tOptional(tString), @@ -871,8 +867,6 @@ scheme.PageAddInitScriptParams = tObject({ source: tString, }); scheme.PageAddInitScriptResult = tOptional(tObject({})); -scheme.PageRemoveInitScriptsParams = tOptional(tObject({})); -scheme.PageRemoveInitScriptsResult = tOptional(tObject({})); scheme.PageCloseParams = tObject({ runBeforeUnload: tOptional(tBoolean), }); @@ -889,8 +883,6 @@ scheme.PageExposeBindingParams = tObject({ needsHandle: tOptional(tBoolean), }); scheme.PageExposeBindingResult = tOptional(tObject({})); -scheme.PageRemoveExposedBindingsParams = tOptional(tObject({})); -scheme.PageRemoveExposedBindingsResult = tOptional(tObject({})); scheme.PageGoBackParams = tObject({ timeout: tOptional(tNumber), waitUntil: tOptional(tType('LifecycleEvent')), diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 04220a194f..2a88e641bc 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -140,25 +140,33 @@ export abstract class BrowserContext extends SdkObject { canResetForReuse(): boolean { if (this._closedStatus !== 'open') return false; - if (this.pages().length < 1) - return false; return true; } - async resetForReuse(metadata: CallMetadata) { + static reusableContextHash(params: channels.BrowserNewContextForReuseParams): string { + const paramsCopy = { ...params }; + for (const key of paramsThatAllowContextReuse) + delete paramsCopy[key]; + return JSON.stringify(paramsCopy); + } + + async resetForReuse(metadata: CallMetadata, params: channels.BrowserNewContextForReuseParams) { this.setDefaultNavigationTimeout(undefined); this.setDefaultTimeout(undefined); + for (const key of paramsThatAllowContextReuse) + (this._options as any)[key] = params[key]; + await this._cancelAllRoutesInFlight(); const [page, ...otherPages] = this.pages(); for (const page of otherPages) await page.close(metadata); // Unless I do this early, setting extra http headers below does not respond. - await page._frameManager.closeOpenDialogs(); - await page.mainFrame().goto(metadata, 'about:blank', { timeout: 0 }); - await this.removeExposedBindings(); - await this.removeInitScripts(); + await page?._frameManager.closeOpenDialogs(); + await page?.mainFrame().goto(metadata, 'about:blank', { timeout: 0 }); + await this._removeExposedBindings(); + await this._removeInitScripts(); // TODO: following can be optimized to not perform noops. if (this._options.permissions) await this.grantPermissions(this._options.permissions); @@ -168,7 +176,7 @@ export abstract class BrowserContext extends SdkObject { await this.setGeolocation(this._options.geolocation); await this.setOffline(!!this._options.offline); - await page.resetForReuse(metadata); + await page?.resetForReuse(metadata); } _browserClosed() { @@ -236,7 +244,7 @@ export abstract class BrowserContext extends SdkObject { await this.doExposeBinding(binding); } - async removeExposedBindings() { + async _removeExposedBindings() { for (const key of this._pageBindings.keys()) { if (!key.startsWith('__pw')) this._pageBindings.delete(key); @@ -324,7 +332,7 @@ export abstract class BrowserContext extends SdkObject { await this.doAddInitScript(script); } - async removeInitScripts(): Promise { + async _removeInitScripts(): Promise { this.initScripts.splice(0, this.initScripts.length); await this.doRemoveInitScripts(); } @@ -586,3 +594,11 @@ export function normalizeProxySettings(proxy: types.ProxySettings): types.ProxyS bypass = bypass.split(',').map(t => t.trim()).join(','); return { ...proxy, server, bypass }; } + +const paramsThatAllowContextReuse: (keyof channels.BrowserNewContextForReuseParams)[] = [ + 'colorScheme', + 'forcedColors', + 'reducedMotion', + 'screen', + 'viewport' +]; diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 454bab126c..20464d7cdf 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -126,10 +126,6 @@ export class BrowserContextDispatcher extends Dispatcher { return { page: lookupDispatcher(await this._context.newPage(metadata)) }; } @@ -174,10 +170,6 @@ export class BrowserContextDispatcher extends Dispatcher { - await this._context.removeInitScripts(); - } - async setNetworkInterceptionEnabled(params: channels.BrowserContextSetNetworkInterceptionEnabledParams): Promise { if (!params.enabled) { await this._context.setRequestInterceptor(undefined); diff --git a/packages/playwright-core/src/server/dispatchers/browserDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserDispatcher.ts index ec86889307..86b80fcd4e 100644 --- a/packages/playwright-core/src/server/dispatchers/browserDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserDispatcher.ts @@ -51,7 +51,7 @@ export class BrowserDispatcher extends Dispatcher { - const hash = JSON.stringify(params); + const hash = BrowserContext.reusableContextHash(params); if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) { if (this._contextForReuse) await this._contextForReuse.context.close(metadata); @@ -59,7 +59,7 @@ export class BrowserDispatcher extends Dispatcher(this._contextForReuse.context); oldContextDispatcher._dispose(); - await this._contextForReuse.context.resetForReuse(metadata); + await this._contextForReuse.context.resetForReuse(metadata, params); } const context = new BrowserContextDispatcher(this._scope, this._contextForReuse.context); return { context }; diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index ecade2e89a..0cc9e46a91 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -107,10 +107,6 @@ export class PageDispatcher extends Dispatcher imple }); } - async removeExposedBindings() { - await this._page.removeExposedBindings(); - } - async setExtraHTTPHeaders(params: channels.PageSetExtraHTTPHeadersParams, metadata: CallMetadata): Promise { await this._page.setExtraHTTPHeaders(params.headers); } @@ -144,10 +140,6 @@ export class PageDispatcher extends Dispatcher imple await this._page.addInitScript(params.source); } - async removeInitScripts(): Promise { - await this._page.removeInitScripts(); - } - async setNetworkInterceptionEnabled(params: channels.PageSetNetworkInterceptionEnabledParams, metadata: CallMetadata): Promise { if (!params.enabled) { await this._page.setClientRequestInterceptor(undefined); diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 516f39e00e..4fbdd25bec 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -231,11 +231,14 @@ export class Page extends SdkObject { this.setDefaultNavigationTimeout(undefined); this.setDefaultTimeout(undefined); - // To this first in order to unfreeze evaluates. + // Do this first in order to unfreeze evaluates. await this._frameManager.closeOpenDialogs(); - await this.removeExposedBindings(); - await this.removeInitScripts(); + await this._removeExposedBindings(); + await this._removeInitScripts(); + + // TODO: handle pending routes. + await this.setClientRequestInterceptor(undefined); await this._setServerRequestInterceptor(undefined); await this.setFileChooserIntercepted(false); await this.mainFrame().goto(metadata, 'about:blank'); @@ -334,7 +337,7 @@ export class Page extends SdkObject { await this._delegate.exposeBinding(binding); } - async removeExposedBindings() { + async _removeExposedBindings() { for (const key of this._pageBindings.keys()) { if (!key.startsWith('__pw')) this._pageBindings.delete(key); @@ -431,6 +434,7 @@ export class Page extends SdkObject { this._emulatedMedia.reducedMotion = options.reducedMotion; if (options.forcedColors !== undefined) this._emulatedMedia.forcedColors = options.forcedColors; + await this._delegate.updateEmulateMedia(); await this._doSlowMo(); } @@ -471,7 +475,7 @@ export class Page extends SdkObject { await this._delegate.addInitScript(source); } - async removeInitScripts() { + async _removeInitScripts() { this.initScripts.splice(0, this.initScripts.length); await this._delegate.removeInitScripts(); } diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index 30752f6409..586b433871 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -42,6 +42,7 @@ if ((process as any)['__pw_initiator__']) { type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { _combinedContextOptions: BrowserContextOptions, _contextReuseEnabled: boolean, + _reuseContext: boolean, _setupContextOptionsAndArtifacts: void; _contextFactory: (options?: BrowserContextOptions) => Promise; }; @@ -506,13 +507,15 @@ export const test = _baseTest.extend({ testInfo.errors.push({ message: prependToError }); }, { scope: 'test', _title: 'context' } as any], - _contextReuseEnabled: async ({ video, trace }, use, testInfo) => { - const reuse = !!process.env.PW_REUSE_CONTEXT && !shouldCaptureVideo(normalizeVideoMode(video), testInfo) && !shouldCaptureTrace(normalizeTraceMode(trace), testInfo); + _contextReuseEnabled: !!process.env.PW_REUSE_CONTEXT, + + _reuseContext: async ({ video, trace, _contextReuseEnabled }, use, testInfo) => { + const reuse = _contextReuseEnabled && !shouldCaptureVideo(normalizeVideoMode(video), testInfo) && !shouldCaptureTrace(normalizeTraceMode(trace), testInfo); await use(reuse); }, - context: async ({ playwright, browser, _contextReuseEnabled, _contextFactory }, use, testInfo) => { - if (!_contextReuseEnabled) { + context: async ({ playwright, browser, _reuseContext, _contextFactory }, use, testInfo) => { + if (!_reuseContext) { await use(await _contextFactory()); return; } @@ -522,8 +525,8 @@ export const test = _baseTest.extend({ await use(context); }, - page: async ({ context, _contextReuseEnabled }, use) => { - if (!_contextReuseEnabled) { + page: async ({ context, _reuseContext }, use) => { + if (!_reuseContext) { await use(await context.newPage()); return; } diff --git a/packages/playwright-test/src/mount.ts b/packages/playwright-test/src/mount.ts index 0f6ba6749b..46c2301e16 100644 --- a/packages/playwright-test/src/mount.ts +++ b/packages/playwright-test/src/mount.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { normalizeTraceMode, normalizeVideoMode, shouldCaptureTrace, shouldCaptureVideo } from './index'; import type { Fixtures, Locator, Page, BrowserContextOptions, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, BrowserContext } from './types'; import type { Component, JsxComponent, ObjectComponentOptions } from '../types/component'; @@ -23,43 +22,19 @@ let boundCallbacksForMount: Function[] = []; export const fixtures: Fixtures< PlaywrightTestArgs & PlaywrightTestOptions & { mount: (component: any, options: any) => Promise }, PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker: { context: BrowserContext | undefined, hash: string } }, - { _contextFactory: (options?: BrowserContextOptions) => Promise }> = { + { _contextFactory: (options?: BrowserContextOptions) => Promise, _contextReuseEnabled: boolean }> = { + + _contextReuseEnabled: true, + + serviceWorkers: 'block', _ctWorker: [{ context: undefined, hash: '' }, { scope: 'worker' }], - context: async ({ playwright, browser, _ctWorker, _contextFactory, video, trace }, use, testInfo) => { - const isolateTests = shouldCaptureVideo(normalizeVideoMode(video), testInfo) || shouldCaptureTrace(normalizeTraceMode(trace), testInfo); - const defaultContextOptions = (playwright.chromium as any)._defaultContextOptions as BrowserContextOptions; - const hash = contextHash(defaultContextOptions); - - if (!_ctWorker.context || _ctWorker.hash !== hash || isolateTests) { - if (_ctWorker.context) - await _ctWorker.context.close(); - // Context factory sets up video so we want to use that for isolated contexts. - // However, it closes the context after the test, so we don't want to use it - // for shared contexts. - _ctWorker.context = isolateTests ? await _contextFactory() : await browser.newContext(); - _ctWorker.hash = hash; - await _ctWorker.context.addInitScript('navigator.serviceWorker.register = () => {}'); - await _ctWorker.context.exposeFunction('__pw_dispatch', (ordinal: number, args: any[]) => { + page: async ({ page }, use) => { + await (page as any)._wrapApiCall(async () => { + await page.exposeFunction('__ct_dispatch', (ordinal: number, args: any[]) => { boundCallbacksForMount[ordinal](...args); }); - } else { - await (_ctWorker.context as any)._resetForReuse(); - } - await use(_ctWorker.context); - }, - - page: async ({ context, viewport }, use) => { - let page = context.pages()[0]; - await (context as any)._wrapApiCall(async () => { - if (!page) { - page = await context.newPage(); - } else { - await (page as any)._resetForReuse(); - await page.goto('about:blank'); - await page.setViewportSize(viewport || { width: 1280, height: 800 }); - } await page.goto(process.env.PLAYWRIGHT_VITE_COMPONENTS_BASE_URL!); }, true); await use(page); @@ -94,7 +69,7 @@ async function innerMount(page: Page, jsxOrType: JsxComponent | string, options: if (typeof value === 'string' && (value as string).startsWith('__pw_func_')) { const ordinal = +value.substring('__pw_func_'.length); object[key] = (...args: any[]) => { - (window as any)['__pw_dispatch'](ordinal, args); + (window as any)['__ct_dispatch'](ordinal, args); }; } else if (typeof value === 'object' && value) { unwrapFunctions(value); @@ -130,29 +105,3 @@ function wrapFunctions(object: any, page: Page, callbacks: Function[]) { } } } - -function contextHash(context: BrowserContextOptions): string { - const hash = { - acceptDownloads: context.acceptDownloads, - bypassCSP: context.bypassCSP, - colorScheme: context.colorScheme, - extraHTTPHeaders: context.extraHTTPHeaders, - forcedColors: context.forcedColors, - geolocation: context.geolocation, - hasTouch: context.hasTouch, - httpCredentials: context.httpCredentials, - ignoreHTTPSErrors: context.ignoreHTTPSErrors, - isMobile: context.isMobile, - javaScriptEnabled: context.javaScriptEnabled, - locale: context.locale, - offline: context.offline, - permissions: context.permissions, - proxy: context.proxy, - storageState: context.storageState, - timezoneId: context.timezoneId, - userAgent: context.userAgent, - deviceScaleFactor: context.deviceScaleFactor, - serviceWorkers: context.serviceWorkers, - }; - return JSON.stringify(hash); -} diff --git a/tests/library/browsercontext-expose-function.spec.ts b/tests/library/browsercontext-expose-function.spec.ts index ee364357eb..fbc146ebba 100644 --- a/tests/library/browsercontext-expose-function.spec.ts +++ b/tests/library/browsercontext-expose-function.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import type { BrowserContext } from '@playwright/test'; import { contextTest as it, expect } from '../config/browserTest'; it('expose binding should work', async ({ context }) => { @@ -95,25 +96,23 @@ it('should work with CSP', async ({ page, context, server }) => { expect(called).toBe(true); }); -it('should re-add binding after reset', async ({ page, context }) => { +it('should re-add binding after reset', async ({ browserType, browser }) => { + const defaultContextOptions = (browserType as any)._defaultContextOptions; + let context: BrowserContext = await (browser as any)._newContextForReuse(defaultContextOptions); + await context.exposeFunction('add', function(a, b) { return Promise.resolve(a - b); }); + let page = await context.newPage(); expect(await page.evaluate('add(7, 6)')).toBe(1); - await (context as any)._removeExposedBindings(); + context = await (browser as any)._newContextForReuse(defaultContextOptions); await context.exposeFunction('add', function(a, b) { return Promise.resolve(a + b); }); + + page = context.pages()[0]; expect(await page.evaluate('add(5, 6)')).toBe(11); await page.reload(); expect(await page.evaluate('add(5, 6)')).toBe(11); }); - -it('should retain internal binding after reset', async ({ page, context }) => { - await context.exposeFunction('__pw_add', function(a, b) { - return Promise.resolve(a + b); - }); - await (context as any)._removeExposedBindings(); - expect(await page.evaluate('__pw_add(5, 6)')).toBe(11); -}); diff --git a/tests/page/page-expose-function.spec.ts b/tests/page/page-expose-function.spec.ts index 39115e7a31..b84800ab43 100644 --- a/tests/page/page-expose-function.spec.ts +++ b/tests/page/page-expose-function.spec.ts @@ -262,29 +262,6 @@ it('should work with setContent', async ({ page, server }) => { expect(await page.evaluate('window.result')).toBe(6); }); -it('should re-add binding after reset', async ({ page }) => { - await page.exposeFunction('add', function(a, b) { - return Promise.resolve(a - b); - }); - expect(await page.evaluate('add(7, 6)')).toBe(1); - - await (page as any)._removeExposedBindings(); - await page.exposeFunction('add', function(a, b) { - return Promise.resolve(a + b); - }); - expect(await page.evaluate('add(5, 6)')).toBe(11); - await page.reload(); - expect(await page.evaluate('add(5, 6)')).toBe(11); -}); - -it('should retain internal binding after reset', async ({ page }) => { - await page.exposeFunction('__pw_add', function(a, b) { - return Promise.resolve(a + b); - }); - await (page as any)._removeExposedBindings(); - expect(await page.evaluate('__pw_add(5, 6)')).toBe(11); -}); - it('should alias Window, Document and Node', async ({ page }) => { let object: any; await page.exposeBinding('log', (source, obj) => object = obj); diff --git a/tests/playwright-test/playwright.ct-reuse.spec.ts b/tests/playwright-test/playwright.ct-reuse.spec.ts index e477851a07..8b39584acc 100644 --- a/tests/playwright-test/playwright.ct-reuse.spec.ts +++ b/tests/playwright-test/playwright.ct-reuse.spec.ts @@ -26,21 +26,21 @@ test('should reuse context', async ({ runInlineTest }) => { 'src/reuse.test.tsx': ` //@no-header import { test, expect } from '@playwright/experimental-ct-react'; - let lastContext; + let lastContextGuid; test('one', async ({ context }) => { - lastContext = context; + lastContextGuid = context._guid; }); test('two', async ({ context }) => { - expect(context).toBe(lastContext); + expect(context._guid).toBe(lastContextGuid); }); test.describe('Dark', () => { - test.use({ colorScheme: 'dark' }); + test.use({ userAgent: 'dark' }); test('three', async ({ context }) => { - expect(context).not.toBe(lastContext); + expect(context._guid).not.toBe(lastContextGuid); }); }); `,