feat(debug): introduce npx playwright debug (#5679)

This commit is contained in:
Pavel Feldman 2021-03-03 22:25:14 -08:00 committed by GitHub
parent ff91858bd3
commit a9238ce2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 119 deletions

View File

@ -45,13 +45,13 @@ Run `codegen` and perform actions in the browser. Playwright CLI will generate J
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end. This is useful to separately record authentication step and reuse it later.
```sh js
$ npx playwright --save-storage=auth.json codegen
$ npx playwright codegen --save-storage=auth.json
# Perform authentication and exit.
# auth.json will contain the storage state.
```
```sh python
$ playwright --save-storage=auth.json codegen
$ playwright codegen --save-storage=auth.json
# Perform authentication and exit.
# auth.json will contain the storage state.
```
@ -59,14 +59,14 @@ $ playwright --save-storage=auth.json codegen
Run with `--load-storage` to consume previously loaded storage. This way, all [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) will be restored, bringing most web apps to the authenticated state.
```sh js
$ npx playwright --load-storage=auth.json open my.web.app
$ npx playwright --load-storage=auth.json codegen my.web.app
$ npx playwright open --load-storage=auth.json my.web.app
$ npx playwright codegen --load-storage=auth.json my.web.app
# Perform actions in authenticated state.
```
```sh python
$ playwright --load-storage=auth.json open my.web.app
$ playwright --load-storage=auth.json codegen my.web.app
$ playwright open --load-storage=auth.json my.web.app
$ playwright codegen --load-storage=auth.json my.web.app
# Perform actions in authenticated state.
```
@ -177,34 +177,34 @@ $ playwright wk example.com
```sh js
# Emulate iPhone 11.
$ npx playwright --device="iPhone 11" open wikipedia.org
$ npx playwright open --device="iPhone 11" wikipedia.org
```
```sh python
# Emulate iPhone 11.
$ playwright --device="iPhone 11" open wikipedia.org
$ playwright open --device="iPhone 11" wikipedia.org
```
### Emulate color scheme and viewport size
```sh js
# Emulate screen size and color scheme.
$ npx playwright --viewport-size=800,600 --color-scheme=dark open twitter.com
$ npx playwright open --viewport-size=800,600 --color-scheme=dark twitter.com
```
```sh python
# Emulate screen size and color scheme.
$ playwright --viewport-size=800,600 --color-scheme=dark open twitter.com
$ playwright open --viewport-size=800,600 --color-scheme=dark twitter.com
```
### Emulate geolocation, language and timezone
```sh js
# Emulate timezone, language & location
# Once page opens, click the "my location" button to see geolocation in action
$ npx playwright --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" open maps.google.com
$ npx playwright open --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
```
```sh python
# Emulate timezone, language & location
# Once page opens, click the "my location" button to see geolocation in action
$ playwright --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" open maps.google.com
$ playwright open --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
```
## Inspect selectors
@ -264,20 +264,18 @@ $ playwright screenshot --help
```sh js
# Wait 3 seconds before capturing a screenshot after page loads ('load' event fires)
$ npx playwright \
--device="iPhone 11" \
--color-scheme=dark \
screenshot \
$ npx playwright screenshot \
--device="iPhone 11" \
--color-scheme=dark \
--wait-for-timeout=3000 \
twitter.com twitter-iphone.png
```
```sh python
# Wait 3 seconds before capturing a screenshot after page loads ('load' event fires)
$ playwright \
--device="iPhone 11" \
--color-scheme=dark \
screenshot \
$ playwright screenshot \
--device="iPhone 11" \
--color-scheme=dark \
--wait-for-timeout=3000 \
twitter.com twitter-iphone.png
```

View File

@ -30,105 +30,58 @@ import { Browser } from '../client/browser';
import { Page } from '../client/page';
import { BrowserType } from '../client/browserType';
import { BrowserContextOptions, LaunchOptions } from '../client/types';
import { spawn } from 'child_process';
program
.version('Version ' + require('../../package.json').version)
.name('npx playwright')
.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium')
.option('--color-scheme <scheme>', 'emulate preferred color scheme, "light" or "dark"')
.option('--device <deviceName>', 'emulate device, for example "iPhone 11"')
.option('--geolocation <coordinates>', 'specify geolocation coordinates, for example "37.819722,-122.478611"')
.option('--lang <language>', 'specify language / locale, for example "en-GB"')
.option('--load-storage <filename>', 'load context storage state from the file, previously saved with --save-storage')
.option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"')
.option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage')
.option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"')
.option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds', '10000')
.option('--user-agent <ua string>', 'specify user agent string')
.option('--viewport-size <size>', 'specify browser viewport size in pixels, for example "1280, 720"');
.name('npx playwright');
program
.command('open [url]')
.description('open page in browser specified via -b, --browser')
commandWithOpenOptions('open [url]', 'open page in browser specified via -b, --browser', [])
.action(function(url, command) {
open(command.parent, url, language());
}).on('--help', function() {
open(command, url, language());
})
.on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ open');
console.log(' $ -b webkit open https://example.com');
console.log(' $ open -b webkit https://example.com');
});
const browsers = [
{ alias: 'cr', name: 'Chromium', type: 'chromium' },
{ alias: 'ff', name: 'Firefox', type: 'firefox' },
{ alias: 'wk', name: 'WebKit', type: 'webkit' },
];
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions',
[
['-o, --output <file name>', 'saves the generated script to a file'],
['--target <language>', `language to use, one of javascript, python, python-async, csharp`, language()],
]).action(function(url, command) {
codegen(command, url, command.target, command.output);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ codegen');
console.log(' $ codegen --target=python');
console.log(' $ codegen -b webkit https://example.com');
});
for (const {alias, name, type} of browsers) {
program
.command(`${alias} [url]`)
.description(`open page in ${name}`)
.option('--target <language>', `language to use, one of javascript, python, python-async, csharp`, language())
.action(function(url, command) {
open({ ...command.parent, browser: type }, url, command.target);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(` $ ${alias} https://example.com`);
program
.command('debug <app> [args...]')
.description('run command in debug mode: disable timeout, open inspector')
.action(function(app, args) {
spawn(app, args, {
env: { ...process.env, PWDEBUG: '1' },
stdio: 'inherit'
});
}
program
.command('codegen [url]')
.description('open page and generate code for user actions')
.option('-o, --output <file name>', 'saves the generated script to a file')
.option('--target <language>', `language to use, one of javascript, python, python-async, csharp`, language())
.action(function(url, command) {
codegen(command.parent, url, command.target, command.output);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ codegen');
console.log(' $ codegen --target=python');
console.log(' $ -b webkit codegen https://example.com');
});
program
.command('screenshot <url> <filename>')
.description('capture a page screenshot')
.option('--wait-for-selector <selector>', 'wait for selector before taking a screenshot')
.option('--wait-for-timeout <timeout>', 'wait for timeout in milliseconds before taking a screenshot')
.option('--full-page', 'whether to take a full page screenshot (entire scrollable area)')
.action(function(url, filename, command) {
screenshot(command.parent, command, url, filename);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ -b webkit screenshot https://example.com example.png');
});
program
.command('pdf <url> <filename>')
.description('save page as pdf')
.option('--wait-for-selector <selector>', 'wait for given selector before saving as pdf')
.option('--wait-for-timeout <timeout>', 'wait for given timeout in milliseconds before saving as pdf')
.action(function(url, filename, command) {
pdf(command.parent, command, url, filename);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ pdf https://example.com example.pdf');
console.log(' $ debug node test.js');
console.log(' $ debug npm run test');
});
program
.command('install [browserType...]')
.description('Ensure browsers necessary for this version of Playwright are installed')
.description('ensure browsers necessary for this version of Playwright are installed')
.action(function(browserType) {
const allBrowsers = new Set(['chromium', 'firefox', 'webkit']);
for (const type of browserType) {
@ -143,6 +96,51 @@ program
});
});
const browsers = [
{ alias: 'cr', name: 'Chromium', type: 'chromium' },
{ alias: 'ff', name: 'Firefox', type: 'firefox' },
{ alias: 'wk', name: 'WebKit', type: 'webkit' },
];
for (const {alias, name, type} of browsers) {
commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, [])
.action(function(url, command) {
open({ ...command, browser: type }, url, command.target);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(` $ ${alias} https://example.com`);
});
}
commandWithOpenOptions('screenshot <url> <filename>', 'capture a page screenshot',
[
['--wait-for-selector <selector>', 'wait for selector before taking a screenshot'],
['--wait-for-timeout <timeout>', 'wait for timeout in milliseconds before taking a screenshot'],
['--full-page', 'whether to take a full page screenshot (entire scrollable area)'],
]).action(function(url, filename, command) {
screenshot(command, command, url, filename);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ screenshot -b webkit https://example.com example.png');
});
commandWithOpenOptions('pdf <url> <filename>', 'save page as pdf',
[
['--wait-for-selector <selector>', 'wait for given selector before saving as pdf'],
['--wait-for-timeout <timeout>', 'wait for given timeout in milliseconds before saving as pdf'],
]).action(function(url, filename, command) {
pdf(command, command, url, filename);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ pdf https://example.com example.pdf');
});
if (process.env.PWTRACE) {
program
.command('show-trace [trace]')
@ -427,3 +425,22 @@ function validateOptions(options: Options) {
function language(): string {
return process.env.PW_CLI_TARGET_LANG || 'javascript';
}
function commandWithOpenOptions(command: string, description: string, options: any[][]): program.Command {
let result = program.command(command).description(description);
for (const option of options)
result = result.option(option[0], ...option.slice(1));
return result
.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium')
.option('--color-scheme <scheme>', 'emulate preferred color scheme, "light" or "dark"')
.option('--device <deviceName>', 'emulate device, for example "iPhone 11"')
.option('--geolocation <coordinates>', 'specify geolocation coordinates, for example "37.819722,-122.478611"')
.option('--load-storage <filename>', 'load context storage state from the file, previously saved with --save-storage')
.option('--lang <language>', 'specify language / locale, for example "en-GB"')
.option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"')
.option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage')
.option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"')
.option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds', '10000')
.option('--user-agent <ua string>', 'specify user agent string')
.option('--viewport-size <size>', 'specify browser viewport size in pixels, for example "1280, 720"');
}

View File

@ -38,6 +38,7 @@ var context = await browser.NewContextAsync();`;
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
const cli = runCLI([
'codegen',
'--color-scheme=dark',
'--geolocation=37.819722,-122.478611',
'--lang=es',
@ -45,7 +46,6 @@ it('should print the correct context options for custom settings', async ({ brow
'--timezone=Europe/Rome',
'--user-agent=hardkodemium',
'--viewport-size=1280,720',
'codegen',
'--target=csharp',
emptyHTML]);
const expectedResult = `await Playwright.InstallAsync();
@ -77,7 +77,7 @@ var context = await browser.NewContextAsync(
});
it('should print the correct context options when using a device', async ({ runCLI }) => {
const cli = runCLI(['--device=Pixel 2', 'codegen', '--target=csharp', emptyHTML]);
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=csharp', emptyHTML]);
const expectedResult = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(headless: false);
@ -88,6 +88,7 @@ var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
const cli = runCLI([
'codegen',
'--device=iPhone 11',
'--color-scheme=dark',
'--geolocation=37.819722,-122.478611',
@ -96,7 +97,6 @@ it('should print the correct context options when using a device and additional
'--timezone=Europe/Rome',
'--user-agent=hardkodemium',
'--viewport-size=1280,720',
'codegen',
'--target=csharp',
emptyHTML]);
const expectedResult = `await Playwright.InstallAsync();
@ -134,7 +134,7 @@ it('should print load/save storageState', async ({ browserName, runCLI, testInfo
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=csharp', emptyHTML]);
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]);
const expectedResult1 = `await Playwright.InstallAsync();
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(headless: false);

View File

@ -38,7 +38,7 @@ public class Example {
});
it('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
const cli = runCLI(['--color-scheme=light', 'codegen', '--target=java', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--target=java', emptyHTML]);
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withColorScheme(ColorScheme.LIGHT));`;
await cli.waitFor(expectedResult);
@ -46,7 +46,7 @@ it('should print the correct context options for custom settings', async ({ runC
});
it('should print the correct context options when using a device', async ({ runCLI }) => {
const cli = runCLI(['--device=Pixel 2', 'codegen', '--target=java', emptyHTML]);
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=java', emptyHTML]);
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
.withViewportSize(411, 731)
@ -58,7 +58,7 @@ it('should print the correct context options when using a device', async ({ runC
});
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', 'codegen', '--target=java', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=java', emptyHTML]);
const expectedResult = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withColorScheme(ColorScheme.LIGHT)
.withUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1")
@ -74,7 +74,7 @@ it('should print load/save storage_state', async ({ runCLI, browserName, testInf
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=java', emptyHTML]);
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=java', emptyHTML]);
const expectedResult1 = `BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.withStorageStatePath(Paths.get("${loadFileName}")));`;
await cli.waitFor(expectedResult1);

View File

@ -36,7 +36,7 @@ it('should print the correct imports and context options', async ({ browserName,
});
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
const cli = runCLI(['--color-scheme=light', 'codegen', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', emptyHTML]);
const expectedResult = `const { ${browserName} } = require('playwright');
(async () => {
@ -52,7 +52,7 @@ it('should print the correct context options for custom settings', async ({ brow
it('should print the correct context options when using a device', async ({ runCLI }) => {
const cli = runCLI(['--device=Pixel 2', 'codegen', emptyHTML]);
const cli = runCLI(['codegen', '--device=Pixel 2', emptyHTML]);
const expectedResult = `const { chromium, devices } = require('playwright');
(async () => {
@ -67,7 +67,7 @@ it('should print the correct context options when using a device', async ({ runC
});
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', 'codegen', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', emptyHTML]);
const expectedResult = `const { webkit, devices } = require('playwright');
(async () => {
@ -114,7 +114,7 @@ it('should print load/save storageState', async ({ browserName, runCLI, testInfo
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', emptyHTML]);
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, emptyHTML]);
const expectedResult1 = `const { ${browserName} } = require('playwright');
(async () => {

View File

@ -35,7 +35,7 @@ async def run(playwright):
});
it('should print the correct context options for custom settings', async ({ browserName, runCLI }) => {
const cli = runCLI(['--color-scheme=light', 'codegen', '--target=python-async', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--target=python-async', emptyHTML]);
const expectedResult = `import asyncio
from playwright.async_api import async_playwright
@ -47,7 +47,7 @@ async def run(playwright):
});
it('should print the correct context options when using a device', async ({ runCLI }) => {
const cli = runCLI(['--device=Pixel 2', 'codegen', '--target=python-async', emptyHTML]);
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=python-async', emptyHTML]);
const expectedResult = `import asyncio
from playwright.async_api import async_playwright
@ -59,7 +59,7 @@ async def run(playwright):
});
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', 'codegen', '--target=python-async', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=python-async', emptyHTML]);
const expectedResult = `import asyncio
from playwright.async_api import async_playwright
@ -105,7 +105,7 @@ it('should print load/save storage_state', async ({ browserName, runCLI, testInf
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=python-async', emptyHTML]);
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python-async', emptyHTML]);
const expectedResult1 = `import asyncio
from playwright.async_api import async_playwright

View File

@ -34,7 +34,7 @@ def run(playwright):
});
it('should print the correct context options for custom settings', async ({ runCLI, browserName }) => {
const cli = runCLI(['--color-scheme=light', 'codegen', '--target=python', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--target=python', emptyHTML]);
const expectedResult = `from playwright.sync_api import sync_playwright
def run(playwright):
@ -45,7 +45,7 @@ def run(playwright):
});
it('should print the correct context options when using a device', async ({ runCLI }) => {
const cli = runCLI(['--device=Pixel 2', 'codegen', '--target=python', emptyHTML]);
const cli = runCLI(['codegen', '--device=Pixel 2', '--target=python', emptyHTML]);
const expectedResult = `from playwright.sync_api import sync_playwright
def run(playwright):
@ -56,7 +56,7 @@ def run(playwright):
});
it('should print the correct context options when using a device and additional options', async ({ runCLI }) => {
const cli = runCLI(['--color-scheme=light', '--device=iPhone 11', 'codegen', '--target=python', emptyHTML]);
const cli = runCLI(['codegen', '--color-scheme=light', '--device=iPhone 11', '--target=python', emptyHTML]);
const expectedResult = `from playwright.sync_api import sync_playwright
def run(playwright):
@ -98,7 +98,7 @@ it('should print load/save storage_state', async ({ runCLI, browserName, testInf
const loadFileName = testInfo.outputPath('load.json');
const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, 'codegen', '--target=python', emptyHTML]);
const cli = runCLI(['codegen', `--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=python', emptyHTML]);
const expectedResult1 = `from playwright.sync_api import sync_playwright
def run(playwright):

View File

@ -170,8 +170,8 @@ class CLIMock {
this.data = '';
this.process = spawn('node', [
path.join(__dirname, '..', '..', 'lib', 'cli', 'cli.js'),
...args,
`--browser=${browserName}`,
...args
], {
env: {
...process.env,