Skip to content

Commit

Permalink
feat(vscode): allow running codegen with selected browser/options
Browse files Browse the repository at this point in the history
  • Loading branch information
mxschmitt committed Mar 22, 2024
1 parent 4a67437 commit 4717e1d
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 53 deletions.
3 changes: 3 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Expand Up @@ -400,6 +400,9 @@ scheme.DebugControllerNavigateResult = tOptional(tObject({}));
scheme.DebugControllerSetRecorderModeParams = tObject({
mode: tEnum(['inspecting', 'recording', 'none']),
testIdAttributeName: tOptional(tString),
browserName: tOptional(tEnum(['chromium', 'firefox', 'webkit'])),
contextOptions: tOptional(tType('BrowserNewContextForReuseParams')),
launchOptions: tOptional(tType('BrowserTypeLaunchParams')),
});
scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({}));
scheme.DebugControllerHighlightParams = tObject({
Expand Down
34 changes: 1 addition & 33 deletions packages/playwright-core/src/remote/playwrightConnection.ts
Expand Up @@ -181,13 +181,7 @@ export class PlaywrightConnection {
debugLogger.log('server', `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
const playwright = this._preLaunched.playwright!;

const requestedOptions = launchOptionsHash(this._options.launchOptions);
let browser = playwright.allBrowsers().find(b => {
if (b.options.name !== this._options.browserName)
return false;
const existingOptions = launchOptionsHash(b.options.originalLaunchOptions);
return existingOptions === requestedOptions;
});
let browser = playwright.findBrowserWithMatchingOptions(this._options.browserName, this._options.launchOptions)

Check failure on line 184 in packages/playwright-core/src/remote/playwrightConnection.ts

View workflow job for this annotation

GitHub Actions / docs & lint

Missing semicolon

// Close remaining browsers of this type+channel. Keep different browser types for the speed.
for (const b of playwright.allBrowsers()) {
Expand Down Expand Up @@ -272,18 +266,6 @@ export class PlaywrightConnection {
}
}

function launchOptionsHash(options: LaunchOptions) {
const copy = { ...options };
for (const k of Object.keys(copy)) {
const key = k as keyof LaunchOptions;
if (copy[key] === defaultLaunchOptions[key])
delete copy[key];
}
for (const key of optionsThatAllowBrowserReuse)
delete copy[key];
return JSON.stringify(copy);
}

function filterLaunchOptions(options: LaunchOptions): LaunchOptions {
return {
channel: options.channel,
Expand All @@ -299,17 +281,3 @@ function filterLaunchOptions(options: LaunchOptions): LaunchOptions {
executablePath: isUnderTest() ? options.executablePath : undefined,
};
}

const defaultLaunchOptions: LaunchOptions = {
ignoreAllDefaultArgs: false,
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
headless: true,
devtools: false,
};

const optionsThatAllowBrowserReuse: (keyof LaunchOptions)[] = [
'headless',
'tracesDir',
];
23 changes: 23 additions & 0 deletions packages/playwright-core/src/server/browserContext.ts
Expand Up @@ -228,6 +228,29 @@ export abstract class BrowserContext extends SdkObject {
await page?.resetForReuse(metadata);
}

async reapplyContextOptionsIfNeeded(options: channels.BrowserNewContextForReuseParams = {}) {
const promises: Promise<any>[] = [];
const hash = (obj: any) => JSON.stringify(obj);
if (options.viewport && hash(options.viewport) !== hash(this._options.viewport))
promises.push(...this.pages().map(page => page.setViewportSize(options.viewport!)));
if (options.extraHTTPHeaders && hash(options.extraHTTPHeaders) !== hash(this._options.extraHTTPHeaders))
promises.push(this.setExtraHTTPHeaders(options.extraHTTPHeaders));
if (options.geolocation && hash(options.geolocation) !== hash(this._options.geolocation))
promises.push(this.setGeolocation(options.geolocation));
if (options.offline !== undefined && options.offline !== this._options.offline)
promises.push(this.setOffline(!!options.offline));
if (options.userAgent && options.userAgent !== this._options.userAgent)
promises.push(this.setUserAgent(options.userAgent));
if (options.storageState && hash(options.storageState) !== hash(this._options.storageState))
promises.push(this.setStorageState(serverSideCallMetadata(), options.storageState));
if (options.permissions && hash(options.permissions) !== hash(this._options.permissions))
promises.push(this.grantPermissions(options.permissions));
const hashMedia = (colorScheme?: types.ColorScheme, reducedMotion?: types.ReducedMotion, forcedColors?: types.ForcedColors) => hash({ colorScheme, reducedMotion, forcedColors });
if (hashMedia(options.colorScheme, options.reducedMotion, options.forcedColors) !== hashMedia(this._options.colorScheme, this._options.reducedMotion, this._options.forcedColors))
promises.push(...this.pages().map(page => page.emulateMedia({ colorScheme: options.colorScheme, reducedMotion: options.reducedMotion, forcedColors: options.forcedColors })));
await Promise.all(promises);
}

_browserClosed() {
for (const page of this.pages())
page._didClose();
Expand Down
35 changes: 25 additions & 10 deletions packages/playwright-core/src/server/debugController.ts
Expand Up @@ -25,6 +25,7 @@ import { Recorder } from './recorder';
import { EmptyRecorderApp } from './recorder/recorderApp';
import { asLocator } from '../utils/isomorphic/locatorGenerators';
import type { Language } from '../utils/isomorphic/locatorGenerators';
import type { BrowserNewContextForReuseParams, BrowserTypeLaunchParams } from '@protocol/channels';

const internalMetadata = serverSideCallMetadata();

Expand Down Expand Up @@ -79,20 +80,27 @@ export class DebugController extends SdkObject {
}
}

async resetForReuse() {
async resetForReuse(params: BrowserNewContextForReuseParams | null = null) {
const contexts = new Set<BrowserContext>();
for (const page of this._playwright.allPages())
contexts.add(page.context());
for (const context of contexts)
await context.resetForReuse(internalMetadata, null);
await context.resetForReuse(internalMetadata, params);
}

async navigate(url: string) {
for (const p of this._playwright.allPages())
await p.mainFrame().goto(internalMetadata, url);
}

async setRecorderMode(params: { mode: Mode, file?: string, testIdAttributeName?: string }) {
async setRecorderMode(params: {
mode: Mode,
file?: string,
testIdAttributeName?: string,
browserName?: 'chromium' | 'firefox' | 'webkit',
contextOptions?: BrowserNewContextForReuseParams,
launchOptions?: BrowserTypeLaunchParams,
}) {
// TODO: |file| is only used in the legacy mode.
await this._closeBrowsersWithoutPages();

Expand All @@ -105,15 +113,22 @@ export class DebugController extends SdkObject {
return;
}

if (!this._playwright.allBrowsers().length)
await this._playwright.chromium.launch(internalMetadata, { headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS });
// Previous browser + launchOptions did not match the previous one.
const launchOptions = {
...params.launchOptions,
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS,
};
const browserName = params.browserName ?? 'chromium';
let browser = this._playwright.findBrowserWithMatchingOptions(browserName, launchOptions);
if (!browser) {
await this.closeAllBrowsers();
browser = await this._playwright[browserName].launch(internalMetadata, launchOptions);
}
// Create page if none.
const pages = this._playwright.allPages();
if (!pages.length) {
const [browser] = this._playwright.allBrowsers();
const { context } = await browser.newContextForReuse({}, internalMetadata);
const { context } = await browser.newContextForReuse(params.contextOptions || {}, internalMetadata);
await context.reapplyContextOptionsIfNeeded(params.contextOptions);
if (!context.pages().length)
await context.newPage(internalMetadata);
}
// Update test id attribute.
if (params.testIdAttributeName) {
for (const page of this._playwright.allPages())
Expand Down
35 changes: 35 additions & 0 deletions packages/playwright-core/src/server/playwright.ts
Expand Up @@ -28,6 +28,7 @@ import { debugLogger } from '../utils/debugLogger';
import type { Page } from './page';
import { DebugController } from './debugController';
import type { Language } from '../utils/isomorphic/locatorGenerators';
import type { LaunchOptions } from './types';

type PlaywrightOptions = {
socksProxyPort?: number;
Expand Down Expand Up @@ -81,6 +82,40 @@ export class Playwright extends SdkObject {
allPages(): Page[] {
return [...this._allPages];
}

findBrowserWithMatchingOptions(requestedBrowserName: string | null, requestedOptions: LaunchOptions): Browser | undefined {
return this.allBrowsers().find(b => {
if (b.options.name !== (requestedBrowserName ?? 'chromium'))
return false;
return launchOptionsHash(b.options.originalLaunchOptions) === launchOptionsHash(requestedOptions);
});
}
}

const defaultLaunchOptions: LaunchOptions = {
ignoreAllDefaultArgs: false,
handleSIGINT: false,
handleSIGTERM: false,
handleSIGHUP: false,
headless: true,
devtools: false,
};

const optionsThatAllowBrowserReuse: (keyof LaunchOptions)[] = [
'headless',
'tracesDir',
];

function launchOptionsHash(options: LaunchOptions) {
const copy = { ...options };
for (const k of Object.keys(copy)) {
const key = k as keyof LaunchOptions;
if (copy[key] === defaultLaunchOptions[key])
delete copy[key];
}
for (const key of optionsThatAllowBrowserReuse)
delete copy[key];
return JSON.stringify(copy);
}

export function createPlaywright(options: PlaywrightOptions) {
Expand Down
6 changes: 6 additions & 0 deletions packages/protocol/src/channels.ts
Expand Up @@ -701,9 +701,15 @@ export type DebugControllerNavigateResult = void;
export type DebugControllerSetRecorderModeParams = {
mode: 'inspecting' | 'recording' | 'none',
testIdAttributeName?: string,
browserName?: 'chromium' | 'firefox' | 'webkit',
contextOptions?: BrowserNewContextForReuseParams,
launchOptions?: BrowserTypeLaunchParams,
};
export type DebugControllerSetRecorderModeOptions = {
testIdAttributeName?: string,
browserName?: 'chromium' | 'firefox' | 'webkit',
contextOptions?: BrowserNewContextForReuseParams,
launchOptions?: BrowserTypeLaunchParams,
};
export type DebugControllerSetRecorderModeResult = void;
export type DebugControllerHighlightParams = {
Expand Down
8 changes: 8 additions & 0 deletions packages/protocol/src/protocol.yml
Expand Up @@ -745,6 +745,14 @@ DebugController:
- recording
- none
testIdAttributeName: string?
browserName:
type: enum?
literals:
- chromium
- firefox
- webkit
contextOptions: BrowserNewContextForReuseParams?
launchOptions: BrowserTypeLaunchParams?

highlight:
parameters:
Expand Down

0 comments on commit 4717e1d

Please sign in to comment.