Skip to content

Commit 893e7bb

Browse files
authored
chore: add _browserTypes helpers to playwright (#34611)
1 parent 9e43306 commit 893e7bb

File tree

13 files changed

+78
-87
lines changed

13 files changed

+78
-87
lines changed

packages/playwright-core/src/client/browser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
8080
}
8181

8282
async _innerNewContext(options: BrowserContextOptions = {}, forReuse: boolean): Promise<BrowserContext> {
83-
options = { ...this._browserType._defaultContextOptions, ...options };
83+
options = { ...this._browserType._playwright._defaultContextOptions, ...options };
8484
const contextOptions = await prepareBrowserContextParams(options);
8585
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
8686
const context = BrowserContext.from(response.context);

packages/playwright-core/src/client/browserType.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type * as channels from '@protocol/channels';
1818
import { Browser } from './browser';
1919
import { BrowserContext, prepareBrowserContextParams } from './browserContext';
2020
import { ChannelOwner } from './channelOwner';
21-
import type { LaunchOptions, LaunchServerOptions, ConnectOptions, LaunchPersistentContextOptions, BrowserContextOptions, Logger } from './types';
21+
import type { LaunchOptions, LaunchServerOptions, ConnectOptions, LaunchPersistentContextOptions, Logger } from './types';
2222
import { Connection } from './connection';
2323
import { Events } from './events';
2424
import type { ChildProcess } from 'child_process';
@@ -45,12 +45,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
4545
_contexts = new Set<BrowserContext>();
4646
_playwright!: Playwright;
4747

48-
// Instrumentation.
49-
_defaultContextOptions?: BrowserContextOptions;
50-
_defaultContextTimeout?: number;
51-
_defaultContextNavigationTimeout?: number;
52-
private _defaultLaunchOptions?: LaunchOptions;
53-
5448
static from(browserType: channels.BrowserTypeChannel): BrowserType {
5549
return (browserType as any)._object;
5650
}
@@ -69,8 +63,8 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
6963
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
7064
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
7165

72-
const logger = options.logger || this._defaultLaunchOptions?.logger;
73-
options = { ...this._defaultLaunchOptions, ...options };
66+
const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
67+
options = { ...this._playwright._defaultLaunchOptions, ...options };
7468
const launchOptions: channels.BrowserTypeLaunchParams = {
7569
...options,
7670
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
@@ -87,14 +81,14 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
8781
async launchServer(options: LaunchServerOptions = {}): Promise<api.BrowserServer> {
8882
if (!this._serverLauncher)
8983
throw new Error('Launching server is not supported');
90-
options = { ...this._defaultLaunchOptions, ...options };
84+
options = { ...this._playwright._defaultLaunchOptions, ...options };
9185
return await this._serverLauncher.launchServer(options);
9286
}
9387

9488
async launchPersistentContext(userDataDir: string, options: LaunchPersistentContextOptions = {}): Promise<BrowserContext> {
95-
const logger = options.logger || this._defaultLaunchOptions?.logger;
89+
const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
9690
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
97-
options = { ...this._defaultLaunchOptions, ...this._defaultContextOptions, ...options };
91+
options = { ...this._playwright._defaultLaunchOptions, ...this._playwright._defaultContextOptions, ...options };
9892
const contextParams = await prepareBrowserContextParams(options);
9993
const persistentParams: channels.BrowserTypeLaunchPersistentContextParams = {
10094
...contextParams,
@@ -237,10 +231,10 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
237231
context._browserType = this;
238232
this._contexts.add(context);
239233
context._setOptions(contextOptions, browserOptions);
240-
if (this._defaultContextTimeout !== undefined)
241-
context.setDefaultTimeout(this._defaultContextTimeout);
242-
if (this._defaultContextNavigationTimeout !== undefined)
243-
context.setDefaultNavigationTimeout(this._defaultContextNavigationTimeout);
234+
if (this._playwright._defaultContextTimeout !== undefined)
235+
context.setDefaultTimeout(this._playwright._defaultContextTimeout);
236+
if (this._playwright._defaultContextNavigationTimeout !== undefined)
237+
context.setDefaultNavigationTimeout(this._playwright._defaultContextNavigationTimeout);
244238
await this._instrumentation.runAfterCreateBrowserContext(context);
245239
}
246240

packages/playwright-core/src/client/fetch.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { assert, headersObjectToArray, isString } from '../utils';
2525
import { mkdirIfNeeded } from '../utils/fileUtils';
2626
import { ChannelOwner } from './channelOwner';
2727
import { RawHeaders } from './network';
28-
import type { ClientCertificate, FilePayload, Headers, StorageState } from './types';
28+
import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types';
2929
import type { Playwright } from './playwright';
3030
import { Tracing } from './tracing';
3131
import { TargetClosedError, isTargetClosedError } from './errors';
@@ -47,7 +47,7 @@ export type FetchOptions = {
4747

4848
type NewContextOptions = Omit<channels.PlaywrightNewRequestOptions, 'extraHTTPHeaders' | 'clientCertificates' | 'storageState' | 'tracesDir'> & {
4949
extraHTTPHeaders?: Headers,
50-
storageState?: string | StorageState,
50+
storageState?: string | SetStorageState,
5151
clientCertificates?: ClientCertificate[];
5252
};
5353

@@ -57,30 +57,29 @@ export class APIRequest implements api.APIRequest {
5757
private _playwright: Playwright;
5858
readonly _contexts = new Set<APIRequestContext>();
5959

60-
// Instrumentation.
61-
_defaultContextOptions?: NewContextOptions & { tracesDir?: string };
62-
6360
constructor(playwright: Playwright) {
6461
this._playwright = playwright;
6562
}
6663

6764
async newContext(options: NewContextOptions = {}): Promise<APIRequestContext> {
68-
options = { ...this._defaultContextOptions, ...options };
65+
options = {
66+
...this._playwright._defaultContextOptions,
67+
timeout: this._playwright._defaultContextTimeout,
68+
...options,
69+
};
6970
const storageState = typeof options.storageState === 'string' ?
7071
JSON.parse(await fs.promises.readFile(options.storageState, 'utf8')) :
7172
options.storageState;
72-
// We do not expose tracesDir in the API, so do not allow options to accidentally override it.
73-
const tracesDir = this._defaultContextOptions?.tracesDir;
7473
const context = APIRequestContext.from((await this._playwright._channel.newRequest({
7574
...options,
7675
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
7776
storageState,
78-
tracesDir,
77+
tracesDir: this._playwright._defaultLaunchOptions?.tracesDir, // We do not expose tracesDir in the API, so do not allow options to accidentally override it.
7978
clientCertificates: await toClientCertificatesProtocol(options.clientCertificates),
8079
})).request);
8180
this._contexts.add(context);
8281
context._request = this;
83-
context._tracing._tracesDir = tracesDir;
82+
context._tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir;
8483
await context._instrumentation.runAfterCreateRequestContext(context);
8584
return context;
8685
}

packages/playwright-core/src/client/playwright.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { ChannelOwner } from './channelOwner';
2222
import { Electron } from './electron';
2323
import { APIRequest } from './fetch';
2424
import { Selectors, SelectorsOwner } from './selectors';
25+
import type { BrowserContextOptions, LaunchOptions } from 'playwright-core';
2526

2627
export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
2728
readonly _android: Android;
@@ -36,6 +37,12 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
3637
readonly request: APIRequest;
3738
readonly errors: { TimeoutError: typeof TimeoutError };
3839

40+
// Instrumentation.
41+
_defaultLaunchOptions?: LaunchOptions;
42+
_defaultContextOptions?: BrowserContextOptions;
43+
_defaultContextTimeout?: number;
44+
_defaultContextNavigationTimeout?: number;
45+
3946
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PlaywrightInitializer) {
4047
super(parent, type, guid, initializer);
4148
this.request = new APIRequest(this);
@@ -73,4 +80,16 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
7380
static from(channel: channels.PlaywrightChannel): Playwright {
7481
return (channel as any)._object;
7582
}
83+
84+
private _browserTypes(): BrowserType[] {
85+
return [this.chromium, this.firefox, this.webkit, this._bidiChromium, this._bidiFirefox];
86+
}
87+
88+
_allContexts() {
89+
return this._browserTypes().flatMap(type => [...type._contexts]);
90+
}
91+
92+
_allPages() {
93+
return this._allContexts().flatMap(context => context.pages());
94+
}
7695
}

packages/playwright/src/index.ts

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import type { TestInfoImpl, TestStepInternal } from './worker/testInfo';
2424
import { rootTestType } from './common/testType';
2525
import type { ContextReuseMode } from './common/config';
2626
import type { ApiCallData, ClientInstrumentation, ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation';
27+
import type { Playwright as PlaywrightImpl } from '../../playwright-core/src/client/playwright';
2728
import { currentTestInfo } from './common/globals';
2829
export { expect } from './matchers/expect';
2930
export const _baseTest: TestType<{}, {}> = rootTestType.test;
@@ -50,6 +51,7 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
5051
};
5152

5253
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
54+
playwright: PlaywrightImpl;
5355
_browserOptions: LaunchOptions;
5456
_optionContextReuseMode: ContextReuseMode,
5557
_optionConnectOptions: PlaywrightWorkerOptions['connectOptions'],
@@ -78,18 +80,16 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
7880
const options: LaunchOptions = {
7981
handleSIGINT: false,
8082
...launchOptions,
83+
tracesDir: tracing().tracesDir(),
8184
};
8285
if (headless !== undefined)
8386
options.headless = headless;
8487
if (channel !== undefined)
8588
options.channel = channel;
86-
options.tracesDir = tracing().tracesDir();
8789

88-
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit, playwright._bidiChromium, playwright._bidiFirefox])
89-
(browserType as any)._defaultLaunchOptions = options;
90+
playwright._defaultLaunchOptions = options;
9091
await use(options);
91-
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit, playwright._bidiChromium, playwright._bidiFirefox])
92-
(browserType as any)._defaultLaunchOptions = undefined;
92+
playwright._defaultLaunchOptions = undefined;
9393
}, { scope: 'worker', auto: true, box: true }],
9494

9595
browser: [async ({ playwright, browserName, _browserOptions, connectOptions, _reuseContext }, use, testInfo) => {
@@ -232,21 +232,14 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
232232
testInfo.snapshotSuffix = process.platform;
233233
if (debugMode())
234234
(testInfo as TestInfoImpl)._setDebugMode();
235-
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
236-
(browserType as any)._defaultContextOptions = _combinedContextOptions;
237-
(browserType as any)._defaultContextTimeout = actionTimeout || 0;
238-
(browserType as any)._defaultContextNavigationTimeout = navigationTimeout || 0;
239-
}
240-
(playwright.request as any)._defaultContextOptions = { ..._combinedContextOptions };
241-
(playwright.request as any)._defaultContextOptions.tracesDir = tracing().tracesDir();
242-
(playwright.request as any)._defaultContextOptions.timeout = actionTimeout || 0;
235+
236+
playwright._defaultContextOptions = _combinedContextOptions;
237+
playwright._defaultContextTimeout = actionTimeout || 0;
238+
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
243239
await use();
244-
(playwright.request as any)._defaultContextOptions = undefined;
245-
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
246-
(browserType as any)._defaultContextOptions = undefined;
247-
(browserType as any)._defaultContextTimeout = undefined;
248-
(browserType as any)._defaultContextNavigationTimeout = undefined;
249-
}
240+
playwright._defaultContextOptions = undefined;
241+
playwright._defaultContextTimeout = undefined;
242+
playwright._defaultContextNavigationTimeout = undefined;
250243
}, { auto: 'all-hooks-included', title: 'context configuration', box: true } as any],
251244

252245
_setupArtifacts: [async ({ playwright, screenshot, _pageSnapshot }, use, testInfo) => {
@@ -453,7 +446,6 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
453446
});
454447

455448
type ScreenshotOption = PlaywrightWorkerOptions['screenshot'] | undefined;
456-
type Playwright = PlaywrightWorkerArgs['playwright'];
457449
type PageSnapshotOption = 'off' | 'on' | 'only-on-failure';
458450

459451
function normalizeVideoMode(video: VideoMode | 'retry-with-video' | { mode: VideoMode } | undefined): VideoMode {
@@ -556,12 +548,7 @@ class SnapshotRecorder {
556548
if (!this.shouldCaptureUponFinish())
557549
return;
558550

559-
const contexts: BrowserContext[] = [];
560-
const playwright = this._artifactsRecorder._playwright;
561-
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit])
562-
contexts.push(...(browserType as any)._contexts);
563-
564-
await Promise.all(contexts.flatMap(context => context.pages().map(page => this._snapshotPage(page, false))));
551+
await Promise.all(this._artifactsRecorder._playwright._allPages().map(page => this._snapshotPage(page, false)));
565552
}
566553

567554
async persistTemporary() {
@@ -622,15 +609,15 @@ class SnapshotRecorder {
622609

623610
class ArtifactsRecorder {
624611
_testInfo!: TestInfoImpl;
625-
_playwright: Playwright;
612+
_playwright: PlaywrightImpl;
626613
_artifactsDir: string;
627614
private _reusedContexts = new Set<BrowserContext>();
628615
private _startedCollectingArtifacts: symbol;
629616

630617
private _pageSnapshotRecorder: SnapshotRecorder;
631618
private _screenshotRecorder: SnapshotRecorder;
632619

633-
constructor(playwright: Playwright, artifactsDir: string, screenshot: ScreenshotOption, pageSnapshot: PageSnapshotOption) {
620+
constructor(playwright: PlaywrightImpl, artifactsDir: string, screenshot: ScreenshotOption, pageSnapshot: PageSnapshotOption) {
634621
this._playwright = playwright;
635622
this._artifactsDir = artifactsDir;
636623
const screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
@@ -654,17 +641,12 @@ class ArtifactsRecorder {
654641
this._pageSnapshotRecorder.fixOrdinal();
655642

656643
// Process existing contexts.
657-
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) {
658-
const promises: (Promise<void> | undefined)[] = [];
659-
const existingContexts = Array.from((browserType as any)._contexts) as BrowserContext[];
660-
for (const context of existingContexts) {
661-
if ((context as any)[kIsReusedContext])
662-
this._reusedContexts.add(context);
663-
else
664-
promises.push(this.didCreateBrowserContext(context));
665-
}
666-
await Promise.all(promises);
667-
}
644+
await Promise.all(this._playwright._allContexts().map(async context => {
645+
if ((context as any)[kIsReusedContext])
646+
this._reusedContexts.add(context);
647+
else
648+
await this.didCreateBrowserContext(context);
649+
}));
668650
{
669651
const existingApiRequests: APIRequestContext[] = Array.from((this._playwright.request as any)._contexts as Set<APIRequestContext>);
670652
await Promise.all(existingApiRequests.map(c => this.didCreateRequestContext(c)));
@@ -704,10 +686,7 @@ class ArtifactsRecorder {
704686
async didFinishTest() {
705687
await this.didFinishTestFunction();
706688

707-
let leftoverContexts: BrowserContext[] = [];
708-
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit])
709-
leftoverContexts.push(...(browserType as any)._contexts);
710-
leftoverContexts = leftoverContexts.filter(context => !this._reusedContexts.has(context));
689+
const leftoverContexts = this._playwright._allContexts().filter(context => !this._reusedContexts.has(context));
711690
const leftoverApiRequests: APIRequestContext[] = Array.from((this._playwright.request as any)._contexts as Set<APIRequestContext>);
712691

713692
// Collect traces/screenshots for remaining contexts.

tests/config/remoteServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class RemoteServer implements PlaywrightServer {
8282

8383
async _start(childProcess: CommonFixtures['childProcess'], browserType: BrowserType, channel: string, remoteServerOptions: RemoteServerOptions = {}) {
8484
this._browserType = browserType;
85-
const browserOptions = (browserType as any)._defaultLaunchOptions;
85+
const browserOptions = (browserType as any)._playwright._defaultLaunchOptions;
8686
// Copy options to prevent a large JSON string when launching subprocess.
8787
// Otherwise, we get `Error: spawn ENAMETOOLONG` on Windows.
8888
const launchOptions: Parameters<BrowserType['launchServer']>[0] = {

tests/library/browsercontext-reuse.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { BrowserContext, Page } from '@playwright/test';
2020
const test = browserTest.extend<{ reusedContext: () => Promise<BrowserContext> }>({
2121
reusedContext: async ({ browserType, browser }, use) => {
2222
await use(async () => {
23-
const defaultContextOptions = (browserType as any)._defaultContextOptions;
23+
const defaultContextOptions = (browserType as any)._playwright._defaultContextOptions;
2424
const context = await (browser as any)._newContextForReuse(defaultContextOptions);
2525
return context;
2626
});

tests/library/browsertype-basic.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { playwrightTest as test, expect } from '../config/browserTest';
2121
test('browserType.executablePath should work', async ({ browserType, channel, mode }) => {
2222
test.skip(!!channel, 'We skip browser download when testing a channel');
2323
test.skip(mode.startsWith('service'));
24-
test.skip(!!(browserType as any)._defaultLaunchOptions.executablePath, 'Skip with custom executable path');
24+
test.skip(!!(browserType as any)._playwright._defaultLaunchOptions.executablePath, 'Skip with custom executable path');
2525

2626
const executablePath = browserType.executablePath();
2727
expect(fs.existsSync(executablePath)).toBe(true);

0 commit comments

Comments
 (0)