Skip to content

feat(codegen): add user-data-dir option #35814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/src/codegen.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,30 @@ pwsh bin/Debug/netX/playwright.ps1 codegen --load-storage=auth.json github.com/m

<img width="1394" alt="github signed in showing use of load storage scharp" src="https://user-images.githubusercontent.com/13063165/220928354-caa0e958-fe09-4125-9b54-67483064da51.png" />

#### Use existing userDataDir

Run `codegen` with `--user-data-dir` to set a fixed [user data directory](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context-option-user-data-dir) for the browser session. If you create a custom browser user data directory, codegen will use this existing browser profile and have access to any authentication state present in that profile.

:::warning
[As of Chrome 136, the default user data directory cannot be accessed via automated tooling](https://developer.chrome.com/blog/remote-debugging-port), such as Playwright. You must create a separate user data directory for use in testing.
:::

```bash js
npx playwright codegen --user-data-dir=/path/to/your/browser/data/ github.com/microsoft/playwright
```

```bash java
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen --user-data-dir=/path/to/your/browser/data/ github.com/microsoft/playwright"
```

```bash python
playwright codegen --user-data-dir=/path/to/your/browser/data/ github.com/microsoft/playwright
```

```bash csharp
pwsh bin/Debug/netX/playwright.ps1 codegen --user-data-dir=/path/to/your/browser/data/ github.com/microsoft/playwright
```

## Record using custom setup

If you would like to use codegen in some non-standard setup (for example, use [`method: BrowserContext.route`]), it is possible to call [`method: Page.pause`] that will open a separate window with codegen controls.
Expand Down
73 changes: 39 additions & 34 deletions packages/playwright-core/src/cli/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ type Options = {
timezone?: string;
viewportSize?: string;
userAgent?: string;
userDataDir?: string;
};

type CaptureOptions = {
Expand Down Expand Up @@ -479,37 +480,6 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro
launchOptions.proxy.bypass = options.proxyBypass;
}

const browser = await browserType.launch(launchOptions);

if (process.env.PWTEST_CLI_IS_UNDER_TEST) {
(process as any)._didSetSourcesForTest = (text: string) => {
process.stdout.write('\n-------------8<-------------\n');
process.stdout.write(text);
process.stdout.write('\n-------------8<-------------\n');
const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN;
if (autoExitCondition && text.includes(autoExitCondition)) {
// Firefox needs a break here
setTimeout(() => {
closeBrowser();
}, 1000);
}
};
// Make sure we exit abnormally when browser crashes.
const logs: string[] = [];
require('playwright-core/lib/utilsBundle').debug.log = (...args: any[]) => {
const line = require('util').format(...args) + '\n';
logs.push(line);
process.stderr.write(line);
};
browser.on('disconnected', () => {
const hasCrashLine = logs.some(line => line.includes('process did exit:') && !line.includes('process did exit: exitCode=0, signal=null'));
if (hasCrashLine) {
process.stderr.write('Detected browser crash.\n');
gracefullyProcessExitDoNotHang(1);
}
});
}

// Viewport size
if (options.viewportSize) {
try {
Expand Down Expand Up @@ -574,9 +544,41 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro
contextOptions.serviceWorkers = 'block';
}

// Close app when the last window closes.
let browser: Browser;
let context: BrowserContext;

const context = await browser.newContext(contextOptions);
if (options.userDataDir) {
context = await browserType.launchPersistentContext(options.userDataDir, { ...launchOptions, ...contextOptions });
browser = context.browser()!;
} else {
browser = await browserType.launch(launchOptions);
context = await browser.newContext(contextOptions);
}

if (process.env.PWTEST_CLI_IS_UNDER_TEST) {
(process as any)._didSetSourcesForTest = (text: string) => {
process.stdout.write('\n-------------8<-------------\n');
process.stdout.write(text);
process.stdout.write('\n-------------8<-------------\n');
const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN;
if (autoExitCondition && text.includes(autoExitCondition))
closeBrowser();
};
// Make sure we exit abnormally when browser crashes.
const logs: string[] = [];
require('playwright-core/lib/utilsBundle').debug.log = (...args: any[]) => {
const line = require('util').format(...args) + '\n';
logs.push(line);
process.stderr.write(line);
};
browser.on('disconnected', () => {
const hasCrashLine = logs.some(line => line.includes('process did exit:') && !line.includes('process did exit: exitCode=0, signal=null'));
if (hasCrashLine) {
process.stderr.write('Detected browser crash.\n');
gracefullyProcessExitDoNotHang(1);
}
});
}

let closingBrowser = false;
async function closeBrowser() {
Expand Down Expand Up @@ -620,7 +622,9 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro
}

async function openPage(context: BrowserContext, url: string | undefined): Promise<Page> {
const page = await context.newPage();
let page = context.pages()[0];
if (!page)
page = await context.newPage();
if (url) {
if (fs.existsSync(url))
url = 'file://' + path.resolve(url);
Expand Down Expand Up @@ -759,6 +763,7 @@ function commandWithOpenOptions(command: string, description: string, options: a
.option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"')
.option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds, no timeout by default')
.option('--user-agent <ua string>', 'specify user agent string')
.option('--user-data-dir <directory>', 'use the specified user data directory instead of a new context')
.option('--viewport-size <size>', 'specify browser viewport size in pixels, for example "1280, 720"');
}

Expand Down
18 changes: 18 additions & 0 deletions tests/installation/playwright-cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { test, expect } from './npmTest';
import path from 'path';
import fs from 'fs';
import os from 'os';

test('cli should work', async ({ exec, tmpWorkspace }) => {
await exec('npm i playwright');
Expand All @@ -31,6 +32,23 @@ test('cli should work', async ({ exec, tmpWorkspace }) => {
expect(result).toContain(`{ page }`);
});

await test.step('codegen with user data dir', async () => {
const userDataDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-custom-user-data-dir'));

try {
const result = await exec(`npx playwright codegen --user-data-dir ${userDataDir} about:blank`, {
env: {
PWTEST_CLI_IS_UNDER_TEST: '1',
PWTEST_CLI_AUTO_EXIT_WHEN: `goto('about:blank')`,
}
});
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
expect(result).toContain(`{ page }`);
} finally {
fs.rmdirSync(userDataDir, { recursive: true });
}
});

await test.step('codegen --target=javascript', async () => {
const result = await exec('npx playwright codegen --target=javascript', {
env: {
Expand Down
Loading