feat(debug): PWDEBUG=console vs PWDEBUG=inspector (#6213)

This supports `PWDEBUG=console` that:
- runs headed;
- disables timeouts;
- adds `playwright` console helper.

When using `PWDEBUG=anything-but-console`, we open inspector.
Docs keep suggesting `PWDEBUG=1`.
This commit is contained in:
Dmitry Gozman 2021-04-20 15:58:34 -07:00 committed by GitHub
parent 4dd8a1c8f1
commit ad731c1535
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 130 additions and 119 deletions

View file

@ -42,7 +42,98 @@ chromium.launch(headless=False, slow_mo=100) # or firefox, webkit
``` ```
## Visual Studio Code debugger (Node.JS) ## Browser Developer Tools
You can use browser developer tools in Chromium, Firefox and WebKit while running
a Playwright script in headed mode. Developer tools help to:
* Inspect the DOM tree and **find element selectors**
* **See console logs** during execution (or learn how to [read logs via API](./verification.md#console-logs))
* Check **network activity** and other developer tools features
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></img></a>
Using a [`method: Page.pause`] method is an easy way to pause the Playwright script execution
and inspect the page in Developer tools. It will also open [Playwright Inspector](./inspector.md) to help with debugging.
:::note
**For WebKit**: launching WebKit Inspector during the execution will
prevent the Playwright script from executing any further.
:::
:::note
**For Chromium**: you can also open developer tools through a launch option.
```js
await chromium.launch({ devtools: true });
```
```java
chromium.launch(new BrowserType.LaunchOptions().setDevtools(true));
```
```python async
await chromium.launch(devtools=True)
```
```python sync
chromium.launch(devtools=True)
```
:::
## Run in Debug Mode
Set the `PWDEBUG` environment variable to run your scripts in debug mode. Using `PWDEBUG=1` will open [Playwright Inspector](./inspector.md).
Using `PWDEBUG=console` will configure the browser for debugging in Developer tools console:
* **Runs headed**: Browsers always launch in headed mode
* **Disables timeout**: Sets default timeout to 0 (= no timeout)
* **Console helper**: Configures a `playwright` object in the browser to generate and highlight
[Playwright selectors](./selectors.md). This can be used to verify text or
composite selectors.
```sh js
# Linux/macOS
$ PWDEBUG=console npm run test
# Windows
$ set PWDEBUG=console
$ npm run test
```
```sh java
# Linux/macOS
$ PWDEBUG=console mvn test
# Windows
$ set PWDEBUG=console
$ mvn test
```
```sh python
# Linux/macOS
$ PWDEBUG=console pytest -s
# Windows
$ set PWDEBUG=console
$ pytest -s
```
## Selectors in Developer Tools Console
When running in Debug Mode with `PWDEBUG=console`, a `playwright` object is available in Developer tools console.
1. Run with `PWDEBUG=console`
1. Setup a breakpoint to pause the execution
1. Open the console panel in browser developer tools
1. Use the `playwright` API
* `playwright.$(selector)`: Highlight the first occurrence of the selector. This reflects
how `page.$` would see the page.
* `playwright.$$(selector)`: Highlight all occurrences of the selector. This reflects
how `page.$$` would see the page.
* `playwright.inspect(selector)`: Inspect the selector in the Elements panel.
* `playwright.clear()`: Clear existing highlights.
* `playwright.selector(element)`: Generate a selector that points to the element.
<a href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png" width="500" alt="Highlight selectors"></img></a>
## Visual Studio Code debugger (Node.js)
The VS Code debugger can be used to pause and resume execution of Playwright The VS Code debugger can be used to pause and resume execution of Playwright
scripts with breakpoints. The debugger can be configured in two ways. scripts with breakpoints. The debugger can be configured in two ways.
@ -53,111 +144,13 @@ Setup [`launch.json` configuration](https://code.visualstudio.com/docs/nodejs/no
for your Node.js project. Once configured launch the scripts with F5 and use for your Node.js project. Once configured launch the scripts with F5 and use
breakpoints. breakpoints.
### Use the new JavaScript debugging terminal ### Use the JavaScript Debug Terminal
1. Open [JavaScript Debug Terminal](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_javascript-debug-terminal)
1. Set a breakpoint in VS Code 1. Set a breakpoint in VS Code
* Use the `debugger` keyword or set a breakpoint in the VS Code UI * Use the `debugger` keyword or set a breakpoint in the VS Code UI
1. Run your Node.js script from the terminal 1. Run your Node.js script from the terminal
## Browser Developer Tools
You can use browser developer tools in Chromium, Firefox and WebKit while running
a Playwright script. Developer tools help to:
* Inspect the DOM tree and **find element selectors**
* **See console logs** during execution (or learn how to [read logs via API](./verification.md#console-logs))
* Check **network activity** and other developer tools features
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></img></a>
> **For WebKit**: Note that launching WebKit Inspector during the execution will
prevent the Playwright script from executing any further.
### API for Chromium
In Chromium, you can also open developer tools through a launch option.
```js
await chromium.launch({ devtools: true });
```
```java
chromium.launch(new BrowserType.LaunchOptions().setDevtools(true));
```
```python async
await chromium.launch(devtools=True)
```
```python sync
chromium.launch(devtools=True)
```
## Run in Debug Mode
Set the `PWDEBUG` environment variable to run your scripts in debug mode. This
configures the browser for debugging.
```sh js
# Linux/macOS
$ PWDEBUG=1 npm run test
# Windows
$ set PWDEBUG=1
$ npm run test
```
```sh java
# Linux/macOS
$ PWDEBUG=1 mvn test
# Windows
$ set PWDEBUG=1
$ mvn test
```
```sh python
# Linux/macOS
$ PWDEBUG=1 pytest -s
# Windows
$ set PWDEBUG=1
$ pytest -s
```
### Defaults
With PWDEBUG, the following defaults are configured for you:
* **Run in headed**: With PWDEBUG, browsers always launch in headed mode
* **Disables timeout**: PWDEBUG sets timeout to 0 (= no timeout)
### Debugging Selectors
PWDEBUG configures a `playwright` object in the browser to highlight
[Playwright selectors](./selectors.md). This can be used to verify text or
composite selectors. To use this:
1. Setup a breakpoint to pause the execution
1. Open the console panel in browser developer tools
1. Use the `playwright` API
* `playwright.$(selector)`: Highlight the first occurrence of the selector. This reflects
how `page.$` would see the page.
* `playwright.$$(selector)`: Highlight all occurrences of the selector. This reflects
how `page.$$` would see the page.
* `playwright.inspect(selector)`: Inspect the selector in the Elements panel.
* `playwright.clear()`: Clear existing highlights.
<a href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png" width="500" alt="Highlight selectors"></img></a>
### Evaluate Source Maps
PWDEBUG also enables source maps for [`method: Page.evaluate`] [executions](./core-concepts.md#evaluation).
This improves the debugging experience for JavaScript executions in the page context.
<a href="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"><img src="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png" width="500" alt="Highlight selectors"></img></a>
## Verbose API logs ## Verbose API logs
Playwright supports verbose logging with the `DEBUG` environment variable. Playwright supports verbose logging with the `DEBUG` environment variable.

View file

@ -43,7 +43,7 @@ configures Playwright for debugging and opens the inspector.
$ pytest -s $ pytest -s
``` ```
Additional useful defaults are configured when `PWDEBUG` is set: Additional useful defaults are configured when `PWDEBUG=1` is set:
- Browsers launch in the headed mode - Browsers launch in the headed mode
- Default timeout is set to 0 (= no timeout) - Default timeout is set to 0 (= no timeout)
@ -84,7 +84,7 @@ configures Playwright for debugging and opens the inspector.
## Stepping through the Playwright script ## Stepping through the Playwright script
When `PWDEBUG` is set, Playwright Inspector window will be opened and the script will be When `PWDEBUG=1` is set, Playwright Inspector window will be opened and the script will be
paused on the first Playwright statement: paused on the first Playwright statement:
<img width="557" alt="Paused on line" src="https://user-images.githubusercontent.com/883973/108614337-71761580-73ae-11eb-9f61-3d29c52c9520.png"></img> <img width="557" alt="Paused on line" src="https://user-images.githubusercontent.com/883973/108614337-71761580-73ae-11eb-9f61-3d29c52c9520.png"></img>
@ -100,12 +100,25 @@ By the time Playwright has paused on that click action, it has already performed
If actionability can't be reached, it'll show action as pending: If actionability can't be reached, it'll show action as pending:
<img width="712" alt="Screen Shot 2021-02-20 at 7 36 06 PM" src="https://user-images.githubusercontent.com/883973/108614840-e6e3e500-73b2-11eb-998f-0cf31b2aa9a2.png"></img> <img width="712" alt="Pending action" src="https://user-images.githubusercontent.com/883973/108614840-e6e3e500-73b2-11eb-998f-0cf31b2aa9a2.png"></img>
You can step over each action using the "Step over" action or resume script without further pauses: You can step over each action using the "Step over" action or resume script without further pauses:
<center><img width="98" alt="Stepping toolbar" src="https://user-images.githubusercontent.com/883973/108614389-f9f4b600-73ae-11eb-8df2-8d9ce9da5d5c.png"></img></center> <center><img width="98" alt="Stepping toolbar" src="https://user-images.githubusercontent.com/883973/108614389-f9f4b600-73ae-11eb-8df2-8d9ce9da5d5c.png"></img></center>
## Using Browser Developer Tools
You can use browser developer tools in Chromium, Firefox and WebKit while running
a Playwright script, with or without Playwright inspector. Developer tools help to:
* Inspect the DOM tree
* **See console logs** during execution (or learn how to [read logs via API](./verification.md#console-logs))
* Check **network activity** and other developer tools features
:::note
**For WebKit**: launching WebKit Inspector during the execution will
prevent the Playwright script from executing any further.
:::
## Debugging Selectors ## Debugging Selectors
@ -113,13 +126,13 @@ You can step over each action using the "Step over" action or resume script with
automatically generate selectors for those elements. automatically generate selectors for those elements.
- To verify where selector points, paste it into the inspector input field: - To verify where selector points, paste it into the inspector input field:
<img width="602" alt="Screen Shot 2021-02-20 at 7 27 20 PM" src="https://user-images.githubusercontent.com/883973/108614696-ad5eaa00-73b1-11eb-81f5-9eebe62543a2.png"></img> <img width="602" alt="Selectors toolbar" src="https://user-images.githubusercontent.com/883973/108614696-ad5eaa00-73b1-11eb-81f5-9eebe62543a2.png"></img>
## Recording scripts ## Recording scripts
At any moment, clicking Record action enables recorder (codegen) mode. At any moment, clicking Record action enables recorder (codegen) mode.
Every action on the target page is turned into the generated script: Every action on the target page is turned into the generated script:
<img width="712" alt="Screen Shot 2021-02-20 at 7 40 02 PM" src="https://user-images.githubusercontent.com/883973/108614897-85704600-73b3-11eb-8bcd-f2e129786c49.png"></img> <img width="712" alt="Recorded script" src="https://user-images.githubusercontent.com/883973/108614897-85704600-73b3-11eb-8bcd-f2e129786c49.png"></img>
You can copy entire generated script or clear it using toolbar actions. You can copy entire generated script or clear it using toolbar actions.

View file

@ -16,7 +16,7 @@
*/ */
import { TimeoutSettings } from '../utils/timeoutSettings'; import { TimeoutSettings } from '../utils/timeoutSettings';
import { isDebugMode, mkdirIfNeeded, createGuid } from '../utils/utils'; import { debugMode, mkdirIfNeeded, createGuid } from '../utils/utils';
import { Browser, BrowserOptions } from './browser'; import { Browser, BrowserOptions } from './browser';
import { Download } from './download'; import { Download } from './download';
import * as frames from './frames'; import * as frames from './frames';
@ -377,7 +377,7 @@ export function validateBrowserContextOptions(options: types.BrowserContextOptio
throw new Error(`Browser needs to be launched with the global proxy. If all contexts override the proxy, global proxy will be never used and can be any string, for example "launch({ proxy: { server: 'per-context' } })"`); throw new Error(`Browser needs to be launched with the global proxy. If all contexts override the proxy, global proxy will be never used and can be any string, for example "launch({ proxy: { server: 'per-context' } })"`);
options.proxy = normalizeProxySettings(options.proxy); options.proxy = normalizeProxySettings(options.proxy);
} }
if (isDebugMode()) if (debugMode() === 'inspector')
options.bypassCSP = true; options.bypassCSP = true;
verifyGeolocation(options.geolocation); verifyGeolocation(options.geolocation);
if (!options._debugName) if (!options._debugName)

View file

@ -28,7 +28,7 @@ import { Progress, ProgressController } from './progress';
import * as types from './types'; import * as types from './types';
import { DEFAULT_TIMEOUT, TimeoutSettings } from '../utils/timeoutSettings'; import { DEFAULT_TIMEOUT, TimeoutSettings } from '../utils/timeoutSettings';
import { validateHostRequirements } from './validateDependencies'; import { validateHostRequirements } from './validateDependencies';
import { isDebugMode } from '../utils/utils'; import { debugMode } from '../utils/utils';
import { helper } from './helper'; import { helper } from './helper';
import { RecentLogsCollector } from '../utils/debugLogger'; import { RecentLogsCollector } from '../utils/debugLogger';
import { CallMetadata, SdkObject } from './instrumentation'; import { CallMetadata, SdkObject } from './instrumentation';
@ -271,7 +271,7 @@ function copyTestHooks(from: object, to: object) {
function validateLaunchOptions<Options extends types.LaunchOptions>(options: Options): Options { function validateLaunchOptions<Options extends types.LaunchOptions>(options: Options): Options {
const { devtools = false } = options; const { devtools = false } = options;
let { headless = !devtools } = options; let { headless = !devtools } = options;
if (isDebugMode()) if (debugMode())
headless = false; headless = false;
return { ...options, devtools, headless }; return { ...options, devtools, headless };
} }

View file

@ -25,7 +25,7 @@ import { ConnectionTransport, ProtocolRequest, WebSocketTransport } from '../tra
import { CRDevTools } from './crDevTools'; import { CRDevTools } from './crDevTools';
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import * as types from '../types'; import * as types from '../types';
import { isDebugMode } from '../../utils/utils'; import { debugMode } from '../../utils/utils';
import { RecentLogsCollector } from '../../utils/debugLogger'; import { RecentLogsCollector } from '../../utils/debugLogger';
import { ProgressController } from '../progress'; import { ProgressController } from '../progress';
import { TimeoutSettings } from '../../utils/timeoutSettings'; import { TimeoutSettings } from '../../utils/timeoutSettings';
@ -40,7 +40,7 @@ export class Chromium extends BrowserType {
constructor(playwrightOptions: PlaywrightOptions) { constructor(playwrightOptions: PlaywrightOptions) {
super('chromium', playwrightOptions); super('chromium', playwrightOptions);
if (isDebugMode()) if (debugMode())
this._devtools = this._createDevTools(); this._devtools = this._createDevTools();
} }

View file

@ -18,12 +18,15 @@ import { BrowserContext } from '../browserContext';
import { RecorderSupplement } from './recorderSupplement'; import { RecorderSupplement } from './recorderSupplement';
import { debugLogger } from '../../utils/debugLogger'; import { debugLogger } from '../../utils/debugLogger';
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation'; import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
import { isDebugMode, isUnderTest } from '../../utils/utils'; import { debugMode, isUnderTest } from '../../utils/utils';
import * as consoleApiSource from '../../generated/consoleApiSource';
export class InspectorController implements InstrumentationListener { export class InspectorController implements InstrumentationListener {
async onContextCreated(context: BrowserContext): Promise<void> { async onContextCreated(context: BrowserContext): Promise<void> {
if (isDebugMode()) if (debugMode() === 'inspector')
await RecorderSupplement.getOrCreate(context, { pauseOnNextStatement: true }); await RecorderSupplement.getOrCreate(context, { pauseOnNextStatement: true });
else if (debugMode() === 'console')
await context.extendInjectedScript(consoleApiSource.source);
} }
async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> { async onBeforeCall(sdkObject: SdkObject, metadata: CallMetadata): Promise<void> {

View file

@ -15,10 +15,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { isDebugMode } from './utils'; import { debugMode } from './utils';
export const DEFAULT_TIMEOUT = 30000; export const DEFAULT_TIMEOUT = 30000;
const TIMEOUT = isDebugMode() ? 0 : DEFAULT_TIMEOUT; const TIMEOUT = debugMode() ? 0 : DEFAULT_TIMEOUT;
export class TimeoutSettings { export class TimeoutSettings {
private _parent: TimeoutSettings | undefined; private _parent: TimeoutSettings | undefined;

View file

@ -88,9 +88,11 @@ export function isError(obj: any): obj is Error {
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error'); return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
} }
const isInDebugMode = !!getFromENV('PWDEBUG'); const debugEnv = getFromENV('PWDEBUG') || '';
export function isDebugMode(): boolean { export function debugMode() {
return isInDebugMode; if (debugEnv === 'console')
return 'console';
return debugEnv ? 'inspector' : '';
} }
let _isUnderTest = false; let _isUnderTest = false;