playwriter 0.0.29 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdp-relay.d.ts.map +1 -1
- package/dist/cdp-relay.js +7 -4
- package/dist/cdp-relay.js.map +1 -1
- package/dist/debugger-api.md +457 -0
- package/dist/debugger-examples-types.d.ts +7 -1
- package/dist/debugger-examples-types.d.ts.map +1 -1
- package/dist/editor-api.md +364 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +27 -51
- package/dist/mcp.js.map +1 -1
- package/dist/styles-api.md +117 -0
- package/dist/styles-examples.d.ts +8 -0
- package/dist/styles-examples.d.ts.map +1 -0
- package/dist/styles-examples.js +64 -0
- package/dist/styles-examples.js.map +1 -0
- package/package.json +2 -2
- package/src/cdp-relay.ts +7 -4
- package/src/debugger-examples-types.ts +4 -1
- package/src/mcp.ts +27 -51
- package/src/prompt.md +4 -73
- package/src/styles-examples.ts +77 -0
- package/dist/debugger-examples.ts +0 -66
- package/dist/prompt.md +0 -328
- package/dist/resource.md +0 -436
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Styles API Reference
|
|
2
|
+
|
|
3
|
+
The getStylesForLocator function inspects CSS styles applied to an element, similar to browser DevTools "Styles" panel.
|
|
4
|
+
|
|
5
|
+
## Types
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import type { CDPSession } from './cdp-session.js';
|
|
9
|
+
import type { Locator } from 'playwright-core';
|
|
10
|
+
export interface StyleSource {
|
|
11
|
+
url: string;
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
}
|
|
15
|
+
export type StyleDeclarations = Record<string, string>;
|
|
16
|
+
export interface StyleRule {
|
|
17
|
+
selector: string;
|
|
18
|
+
source: StyleSource | null;
|
|
19
|
+
origin: 'regular' | 'user-agent' | 'injected' | 'inspector';
|
|
20
|
+
declarations: StyleDeclarations;
|
|
21
|
+
inheritedFrom: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface StylesResult {
|
|
24
|
+
element: string;
|
|
25
|
+
inlineStyle: StyleDeclarations | null;
|
|
26
|
+
rules: StyleRule[];
|
|
27
|
+
}
|
|
28
|
+
export declare function getStylesForLocator({ locator, cdp, includeUserAgentStyles, }: {
|
|
29
|
+
locator: Locator;
|
|
30
|
+
cdp: CDPSession;
|
|
31
|
+
includeUserAgentStyles?: boolean;
|
|
32
|
+
}): Promise<StylesResult>;
|
|
33
|
+
export declare function formatStylesAsText(styles: StylesResult): string;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { page, getStylesForLocator, formatStylesAsText, console } from './debugger-examples-types.js'
|
|
40
|
+
|
|
41
|
+
// Example: Get styles for an element and display them
|
|
42
|
+
async function getElementStyles() {
|
|
43
|
+
const loc = page.locator('.my-button')
|
|
44
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
45
|
+
console.log(formatStylesAsText(styles))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Example: Inspect computed styles for a specific element
|
|
49
|
+
async function inspectButtonStyles() {
|
|
50
|
+
const button = page.getByRole('button', { name: 'Submit' })
|
|
51
|
+
const styles = await getStylesForLocator({ locator: button })
|
|
52
|
+
|
|
53
|
+
console.log('Element:', styles.element)
|
|
54
|
+
|
|
55
|
+
if (styles.inlineStyle) {
|
|
56
|
+
console.log('Inline styles:', styles.inlineStyle)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const rule of styles.rules) {
|
|
60
|
+
console.log(`${rule.selector}: ${JSON.stringify(rule.declarations)}`)
|
|
61
|
+
if (rule.source) {
|
|
62
|
+
console.log(` Source: ${rule.source.url}:${rule.source.line}`)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Example: Include browser default (user-agent) styles
|
|
68
|
+
async function getStylesWithUserAgent() {
|
|
69
|
+
const loc = page.locator('input[type="text"]')
|
|
70
|
+
const styles = await getStylesForLocator({
|
|
71
|
+
locator: loc,
|
|
72
|
+
includeUserAgentStyles: true,
|
|
73
|
+
})
|
|
74
|
+
console.log(formatStylesAsText(styles))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Example: Find where a CSS property is defined
|
|
78
|
+
async function findPropertySource() {
|
|
79
|
+
const loc = page.locator('.card')
|
|
80
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
81
|
+
|
|
82
|
+
const backgroundRule = styles.rules.find((r) => 'background-color' in r.declarations)
|
|
83
|
+
if (backgroundRule) {
|
|
84
|
+
console.log('background-color defined by:', backgroundRule.selector)
|
|
85
|
+
if (backgroundRule.source) {
|
|
86
|
+
console.log(` at ${backgroundRule.source.url}:${backgroundRule.source.line}`)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Example: Check inherited styles
|
|
92
|
+
async function checkInheritedStyles() {
|
|
93
|
+
const loc = page.locator('.nested-text')
|
|
94
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
95
|
+
|
|
96
|
+
const inheritedRules = styles.rules.filter((r) => r.inheritedFrom)
|
|
97
|
+
for (const rule of inheritedRules) {
|
|
98
|
+
console.log(`Inherited from ${rule.inheritedFrom}: ${rule.selector}`)
|
|
99
|
+
console.log(' Properties:', rule.declarations)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Example: Compare styles between two elements
|
|
104
|
+
async function compareStyles() {
|
|
105
|
+
const primary = await getStylesForLocator({ locator: page.locator('.btn-primary') })
|
|
106
|
+
const secondary = await getStylesForLocator({ locator: page.locator('.btn-secondary') })
|
|
107
|
+
|
|
108
|
+
console.log('Primary button:')
|
|
109
|
+
console.log(formatStylesAsText(primary))
|
|
110
|
+
|
|
111
|
+
console.log('Secondary button:')
|
|
112
|
+
console.log(formatStylesAsText(secondary))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { getElementStyles, inspectButtonStyles, getStylesWithUserAgent, findPropertySource, checkInheritedStyles, compareStyles }
|
|
116
|
+
|
|
117
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare function getElementStyles(): Promise<void>;
|
|
2
|
+
declare function inspectButtonStyles(): Promise<void>;
|
|
3
|
+
declare function getStylesWithUserAgent(): Promise<void>;
|
|
4
|
+
declare function findPropertySource(): Promise<void>;
|
|
5
|
+
declare function checkInheritedStyles(): Promise<void>;
|
|
6
|
+
declare function compareStyles(): Promise<void>;
|
|
7
|
+
export { getElementStyles, inspectButtonStyles, getStylesWithUserAgent, findPropertySource, checkInheritedStyles, compareStyles };
|
|
8
|
+
//# sourceMappingURL=styles-examples.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles-examples.d.ts","sourceRoot":"","sources":["../src/styles-examples.ts"],"names":[],"mappings":"AAGA,iBAAe,gBAAgB,kBAI9B;AAGD,iBAAe,mBAAmB,kBAgBjC;AAGD,iBAAe,sBAAsB,kBAOpC;AAGD,iBAAe,kBAAkB,kBAWhC;AAGD,iBAAe,oBAAoB,kBASlC;AAGD,iBAAe,aAAa,kBAS3B;AAED,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { page, getStylesForLocator, formatStylesAsText, console } from './debugger-examples-types.js';
|
|
2
|
+
// Example: Get styles for an element and display them
|
|
3
|
+
async function getElementStyles() {
|
|
4
|
+
const loc = page.locator('.my-button');
|
|
5
|
+
const styles = await getStylesForLocator({ locator: loc });
|
|
6
|
+
console.log(formatStylesAsText(styles));
|
|
7
|
+
}
|
|
8
|
+
// Example: Inspect computed styles for a specific element
|
|
9
|
+
async function inspectButtonStyles() {
|
|
10
|
+
const button = page.getByRole('button', { name: 'Submit' });
|
|
11
|
+
const styles = await getStylesForLocator({ locator: button });
|
|
12
|
+
console.log('Element:', styles.element);
|
|
13
|
+
if (styles.inlineStyle) {
|
|
14
|
+
console.log('Inline styles:', styles.inlineStyle);
|
|
15
|
+
}
|
|
16
|
+
for (const rule of styles.rules) {
|
|
17
|
+
console.log(`${rule.selector}: ${JSON.stringify(rule.declarations)}`);
|
|
18
|
+
if (rule.source) {
|
|
19
|
+
console.log(` Source: ${rule.source.url}:${rule.source.line}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Example: Include browser default (user-agent) styles
|
|
24
|
+
async function getStylesWithUserAgent() {
|
|
25
|
+
const loc = page.locator('input[type="text"]');
|
|
26
|
+
const styles = await getStylesForLocator({
|
|
27
|
+
locator: loc,
|
|
28
|
+
includeUserAgentStyles: true,
|
|
29
|
+
});
|
|
30
|
+
console.log(formatStylesAsText(styles));
|
|
31
|
+
}
|
|
32
|
+
// Example: Find where a CSS property is defined
|
|
33
|
+
async function findPropertySource() {
|
|
34
|
+
const loc = page.locator('.card');
|
|
35
|
+
const styles = await getStylesForLocator({ locator: loc });
|
|
36
|
+
const backgroundRule = styles.rules.find((r) => 'background-color' in r.declarations);
|
|
37
|
+
if (backgroundRule) {
|
|
38
|
+
console.log('background-color defined by:', backgroundRule.selector);
|
|
39
|
+
if (backgroundRule.source) {
|
|
40
|
+
console.log(` at ${backgroundRule.source.url}:${backgroundRule.source.line}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Example: Check inherited styles
|
|
45
|
+
async function checkInheritedStyles() {
|
|
46
|
+
const loc = page.locator('.nested-text');
|
|
47
|
+
const styles = await getStylesForLocator({ locator: loc });
|
|
48
|
+
const inheritedRules = styles.rules.filter((r) => r.inheritedFrom);
|
|
49
|
+
for (const rule of inheritedRules) {
|
|
50
|
+
console.log(`Inherited from ${rule.inheritedFrom}: ${rule.selector}`);
|
|
51
|
+
console.log(' Properties:', rule.declarations);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Example: Compare styles between two elements
|
|
55
|
+
async function compareStyles() {
|
|
56
|
+
const primary = await getStylesForLocator({ locator: page.locator('.btn-primary') });
|
|
57
|
+
const secondary = await getStylesForLocator({ locator: page.locator('.btn-secondary') });
|
|
58
|
+
console.log('Primary button:');
|
|
59
|
+
console.log(formatStylesAsText(primary));
|
|
60
|
+
console.log('Secondary button:');
|
|
61
|
+
console.log(formatStylesAsText(secondary));
|
|
62
|
+
}
|
|
63
|
+
export { getElementStyles, inspectButtonStyles, getStylesWithUserAgent, findPropertySource, checkInheritedStyles, compareStyles };
|
|
64
|
+
//# sourceMappingURL=styles-examples.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles-examples.js","sourceRoot":"","sources":["../src/styles-examples.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playwriter",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.32",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"repository": "https://github.com/remorses/playwriter",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "rm -rf dist *.tsbuildinfo && mkdir dist &&
|
|
10
|
+
"build": "rm -rf dist *.tsbuildinfo && mkdir dist && bun scripts/build-selector-generator.ts && bun scripts/build-bippy.ts && tsc && bun scripts/build-resources.ts",
|
|
11
11
|
"prepublishOnly": "pnpm build",
|
|
12
12
|
"watch": "tsc -w",
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
package/src/cdp-relay.ts
CHANGED
|
@@ -271,14 +271,17 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
|
|
|
271
271
|
const contextCreatedPromise = new Promise<void>((resolve) => {
|
|
272
272
|
const handler = ({ event }: { event: CDPEventBase }) => {
|
|
273
273
|
if (event.method === 'Runtime.executionContextCreated' && event.sessionId === sessionId) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
274
|
+
const params = event.params as Protocol.Runtime.ExecutionContextCreatedEvent | undefined
|
|
275
|
+
if (params?.context?.auxData?.isDefault === true) {
|
|
276
|
+
clearTimeout(timeout)
|
|
277
|
+
emitter.off('cdp:event', handler)
|
|
278
|
+
resolve()
|
|
279
|
+
}
|
|
277
280
|
}
|
|
278
281
|
}
|
|
279
282
|
const timeout = setTimeout(() => {
|
|
280
283
|
emitter.off('cdp:event', handler)
|
|
281
|
-
logger?.log(chalk.yellow(`IMPORTANT: Runtime.enable timed out waiting for executionContextCreated (sessionId: ${sessionId}). This may cause pages to not be visible immediately.`))
|
|
284
|
+
logger?.log(chalk.yellow(`IMPORTANT: Runtime.enable timed out waiting for main frame executionContextCreated (sessionId: ${sessionId}). This may cause pages to not be visible immediately.`))
|
|
282
285
|
resolve()
|
|
283
286
|
}, 3000)
|
|
284
287
|
emitter.on('cdp:event', handler)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { Page } from 'playwright-core'
|
|
1
|
+
import type { Page, Locator } from 'playwright-core'
|
|
2
2
|
import type { CDPSession } from './cdp-session.js'
|
|
3
3
|
import type { Debugger } from './debugger.js'
|
|
4
4
|
import type { Editor } from './editor.js'
|
|
5
|
+
import type { StylesResult } from './styles.js'
|
|
5
6
|
|
|
6
7
|
export declare const page: Page
|
|
7
8
|
export declare const getCDPSession: (options: { page: Page }) => Promise<CDPSession>
|
|
8
9
|
export declare const createDebugger: (options: { cdp: CDPSession }) => Debugger
|
|
9
10
|
export declare const createEditor: (options: { cdp: CDPSession }) => Editor
|
|
11
|
+
export declare const getStylesForLocator: (options: { locator: Locator; includeUserAgentStyles?: boolean }) => Promise<StylesResult>
|
|
12
|
+
export declare const formatStylesAsText: (styles: StylesResult) => string
|
|
10
13
|
export declare const console: { log: (...args: unknown[]) => void }
|
package/src/mcp.ts
CHANGED
|
@@ -431,72 +431,48 @@ const promptContent =
|
|
|
431
431
|
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'prompt.md'), 'utf-8') +
|
|
432
432
|
`\n\nfor debugging internal playwriter errors, check playwriter relay server logs at: ${LOG_FILE_PATH}`
|
|
433
433
|
|
|
434
|
-
server.resource('debugger-api', 'playwriter
|
|
434
|
+
server.resource('debugger-api', 'https://playwriter.dev/resources/debugger-api.md', { mimeType: 'text/plain' }, async () => {
|
|
435
435
|
const packageJsonPath = require.resolve('playwriter/package.json')
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
const debuggerTypes = fs
|
|
439
|
-
.readFileSync(path.join(distDir, 'debugger.d.ts'), 'utf-8')
|
|
440
|
-
.replace(/\/\/# sourceMappingURL=.*$/gm, '')
|
|
441
|
-
.trim()
|
|
442
|
-
const debuggerExamples = fs.readFileSync(path.join(distDir, 'debugger-examples.ts'), 'utf-8')
|
|
436
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
437
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'debugger-api.md'), 'utf-8')
|
|
443
438
|
|
|
444
439
|
return {
|
|
445
440
|
contents: [
|
|
446
441
|
{
|
|
447
|
-
uri: 'playwriter
|
|
448
|
-
text:
|
|
449
|
-
# Debugger API Reference
|
|
450
|
-
|
|
451
|
-
## Types
|
|
452
|
-
|
|
453
|
-
\`\`\`ts
|
|
454
|
-
${debuggerTypes}
|
|
455
|
-
\`\`\`
|
|
456
|
-
|
|
457
|
-
## Examples
|
|
458
|
-
|
|
459
|
-
\`\`\`ts
|
|
460
|
-
${debuggerExamples}
|
|
461
|
-
\`\`\`
|
|
462
|
-
`,
|
|
442
|
+
uri: 'https://playwriter.dev/resources/debugger-api.md',
|
|
443
|
+
text: content,
|
|
463
444
|
mimeType: 'text/plain',
|
|
464
445
|
},
|
|
465
446
|
],
|
|
466
447
|
}
|
|
467
448
|
})
|
|
468
449
|
|
|
469
|
-
server.resource('editor-api', 'playwriter
|
|
450
|
+
server.resource('editor-api', 'https://playwriter.dev/resources/editor-api.md', { mimeType: 'text/plain' }, async () => {
|
|
470
451
|
const packageJsonPath = require.resolve('playwriter/package.json')
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
const editorTypes = fs
|
|
474
|
-
.readFileSync(path.join(distDir, 'editor.d.ts'), 'utf-8')
|
|
475
|
-
.replace(/\/\/# sourceMappingURL=.*$/gm, '')
|
|
476
|
-
.trim()
|
|
477
|
-
const editorExamples = fs.readFileSync(path.join(distDir, 'editor-examples.ts'), 'utf-8')
|
|
452
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
453
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'editor-api.md'), 'utf-8')
|
|
478
454
|
|
|
479
455
|
return {
|
|
480
456
|
contents: [
|
|
481
457
|
{
|
|
482
|
-
uri: 'playwriter
|
|
483
|
-
text:
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
\`\`\`ts
|
|
491
|
-
${editorTypes}
|
|
492
|
-
\`\`\`
|
|
458
|
+
uri: 'https://playwriter.dev/resources/editor-api.md',
|
|
459
|
+
text: content,
|
|
460
|
+
mimeType: 'text/plain',
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
}
|
|
464
|
+
})
|
|
493
465
|
|
|
494
|
-
|
|
466
|
+
server.resource('styles-api', 'https://playwriter.dev/resources/styles-api.md', { mimeType: 'text/plain' }, async () => {
|
|
467
|
+
const packageJsonPath = require.resolve('playwriter/package.json')
|
|
468
|
+
const packageDir = path.dirname(packageJsonPath)
|
|
469
|
+
const content = fs.readFileSync(path.join(packageDir, 'dist', 'styles-api.md'), 'utf-8')
|
|
495
470
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
471
|
+
return {
|
|
472
|
+
contents: [
|
|
473
|
+
{
|
|
474
|
+
uri: 'https://playwriter.dev/resources/styles-api.md',
|
|
475
|
+
text: content,
|
|
500
476
|
mimeType: 'text/plain',
|
|
501
477
|
},
|
|
502
478
|
],
|
|
@@ -839,13 +815,13 @@ server.tool(
|
|
|
839
815
|
server.tool(
|
|
840
816
|
'reset',
|
|
841
817
|
dedent`
|
|
842
|
-
Recreates the CDP connection and resets the browser/page/context. Use this when the MCP stops responding, you get connection errors, assertion failures, page closed, or
|
|
818
|
+
Recreates the CDP connection and resets the browser/page/context. Use this when the MCP stops responding, you get connection errors, if there are no pages in context, assertion failures, page closed, or other issues.
|
|
843
819
|
|
|
844
820
|
After calling this tool, the page and context variables are automatically updated in the execution environment.
|
|
845
821
|
|
|
846
|
-
|
|
822
|
+
This tools also removes any custom properties you may have added to the global scope AND clearing all keys from the \`state\` object. Only \`page\`, \`context\`, \`state\` (empty), \`console\`, and utility functions will remain.
|
|
847
823
|
|
|
848
|
-
if playwright always returns all pages as about:blank urls and evaluate does not work you should
|
|
824
|
+
if playwright always returns all pages as about:blank urls and evaluate does not work you should ask the user to restart Chrome. This is a known Chrome bug.
|
|
849
825
|
`,
|
|
850
826
|
{},
|
|
851
827
|
async () => {
|
package/src/prompt.md
CHANGED
|
@@ -95,82 +95,13 @@ you have access to some functions in addition to playwright methods:
|
|
|
95
95
|
- `page`: the page object to create the session for
|
|
96
96
|
- Returns: `{ send(method, params?), on(event, callback), off(event, callback) }`
|
|
97
97
|
- Example: `const cdp = await getCDPSession({ page }); const metrics = await cdp.send('Page.getLayoutMetrics');`
|
|
98
|
-
- `createDebugger({ cdp })`: creates a Debugger instance for setting breakpoints, stepping, and inspecting variables.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- Example:
|
|
102
|
-
```js
|
|
103
|
-
const cdp = await getCDPSession({ page }); const dbg = createDebugger({ cdp }); await dbg.enable();
|
|
104
|
-
console.log(dbg.listScripts({ search: 'app' }));
|
|
105
|
-
await dbg.setBreakpoint({ file: 'https://example.com/app.js', line: 42 });
|
|
106
|
-
// conditional breakpoint - only pause when userId is 123
|
|
107
|
-
await dbg.setBreakpoint({ file: 'app.js', line: 50, condition: 'userId === 123' });
|
|
108
|
-
// XHR breakpoint - pause when fetch/XHR URL contains '/api'
|
|
109
|
-
await dbg.setXHRBreakpoint({ url: '/api' });
|
|
110
|
-
// blackbox framework code when stepping
|
|
111
|
-
await dbg.setBlackboxPatterns({ patterns: ['node_modules/'] });
|
|
112
|
-
// user triggers the code, then:
|
|
113
|
-
if (dbg.isPaused()) { console.log(await dbg.getLocation()); console.log(await dbg.inspectLocalVariables()); await dbg.resume(); }
|
|
114
|
-
```
|
|
115
|
-
- `createEditor({ cdp })`: creates an Editor instance for viewing and live-editing page scripts and CSS stylesheets. Provides a Claude Code-like interface.
|
|
116
|
-
- `cdp`: a CDPSession from `getCDPSession`
|
|
117
|
-
- Methods: `enable()`, `list({ pattern? })`, `read({ url, offset?, limit? })`, `edit({ url, oldString, newString, dryRun? })`, `grep({ regex, pattern? })`, `write({ url, content, dryRun? })`
|
|
118
|
-
- `pattern` parameter: regex to filter URLs (e.g. `/\.js/` for JS files, `/\.css/` for CSS files)
|
|
119
|
-
- Inline scripts get `inline://` URLs, inline styles get `inline-css://` URLs - use grep() to find them by content
|
|
120
|
-
- Example:
|
|
121
|
-
```js
|
|
122
|
-
const cdp = await getCDPSession({ page }); const editor = createEditor({ cdp }); await editor.enable();
|
|
123
|
-
// list all scripts and stylesheets
|
|
124
|
-
console.log(editor.list());
|
|
125
|
-
// list only JS files
|
|
126
|
-
console.log(editor.list({ pattern: /\.js/ }));
|
|
127
|
-
// list only CSS files
|
|
128
|
-
console.log(editor.list({ pattern: /\.css/ }));
|
|
129
|
-
// read a script with line numbers (like Claude Code Read tool)
|
|
130
|
-
const { content, totalLines } = await editor.read({ url: 'https://example.com/app.js', offset: 0, limit: 50 });
|
|
131
|
-
console.log(content);
|
|
132
|
-
// edit a script (like Claude Code Edit tool) - exact string replacement
|
|
133
|
-
await editor.edit({ url: 'https://example.com/app.js', oldString: 'DEBUG = false', newString: 'DEBUG = true' });
|
|
134
|
-
// edit CSS
|
|
135
|
-
await editor.edit({ url: 'https://example.com/styles.css', oldString: 'color: red', newString: 'color: blue' });
|
|
136
|
-
// search across all scripts (like Grep) - useful for finding inline scripts
|
|
137
|
-
const matches = await editor.grep({ regex: /myFunction/ });
|
|
138
|
-
if (matches.length > 0) { await editor.edit({ url: matches[0].url, oldString: 'return false', newString: 'return true' }); }
|
|
139
|
-
// search only in CSS files
|
|
140
|
-
const cssMatches = await editor.grep({ regex: /background-color/, pattern: /\.css/ });
|
|
141
|
-
```
|
|
142
|
-
- `getStylesForLocator({ locator, includeUserAgentStyles? })`: gets the CSS styles applied to an element, similar to browser DevTools "Styles" panel.
|
|
143
|
-
- `locator`: a Playwright Locator for the element to inspect
|
|
144
|
-
- `includeUserAgentStyles`: (optional, default: false) include browser default styles
|
|
145
|
-
- Returns: `StylesResult` object with:
|
|
146
|
-
- `element`: string description of the element (e.g. `div#main.container`)
|
|
147
|
-
- `inlineStyle`: object of `{ property: value }` inline styles, or null
|
|
148
|
-
- `rules`: array of CSS rules that apply to this element, each with:
|
|
149
|
-
- `selector`: the CSS selector that matched
|
|
150
|
-
- `source`: `{ url, line, column }` location in the stylesheet, or null
|
|
151
|
-
- `origin`: `"regular"` | `"user-agent"` | `"injected"` | `"inspector"`
|
|
152
|
-
- `declarations`: object of `{ property: value }` (values include `!important` if applicable)
|
|
153
|
-
- `inheritedFrom`: element description if inherited (e.g. `body`), or null for direct matches
|
|
154
|
-
- Example:
|
|
155
|
-
```js
|
|
156
|
-
const loc = page.locator('.my-button');
|
|
157
|
-
const styles = await getStylesForLocator({ locator: loc });
|
|
158
|
-
console.log(formatStylesAsText(styles));
|
|
159
|
-
```
|
|
160
|
-
- `formatStylesAsText(styles)`: formats a `StylesResult` object as human-readable text. Use this to display styles in a readable format.
|
|
98
|
+
- `createDebugger({ cdp })`: creates a Debugger instance for setting breakpoints, stepping, and inspecting variables. Read the `https://playwriter.dev/resources/debugger-api.md` resource for full API docs and examples.
|
|
99
|
+
- `createEditor({ cdp })`: creates an Editor instance for viewing and live-editing page scripts and CSS stylesheets. Read the `https://playwriter.dev/resources/editor-api.md` resource for full API docs and examples.
|
|
100
|
+
- `getStylesForLocator({ locator })`: gets the CSS styles applied to an element, similar to browser DevTools "Styles" panel. Read the `https://playwriter.dev/resources/styles-api.md` resource for full API docs and examples.
|
|
161
101
|
- `getReactSource({ locator })`: gets the React component source location (file, line, column) for an element.
|
|
162
102
|
- `locator`: a Playwright Locator or ElementHandle for the element to inspect
|
|
163
103
|
- Returns: `{ fileName, lineNumber, columnNumber, componentName }` or `null` if not found
|
|
164
|
-
- **Important**: Only works on **local dev servers** (localhost with Vite, Next.js, CRA in dev mode). Production builds strip source info.
|
|
165
|
-
- Example:
|
|
166
|
-
```js
|
|
167
|
-
const loc = page.locator('.my-component');
|
|
168
|
-
const source = await getReactSource({ locator: loc });
|
|
169
|
-
if (source) {
|
|
170
|
-
console.log(`Component: ${source.componentName}`);
|
|
171
|
-
console.log(`File: ${source.fileName}:${source.lineNumber}:${source.columnNumber}`);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
104
|
+
- **Important**: Only works on **local dev servers** (localhost with Vite, Next.js, CRA in dev mode). Production builds strip source info.
|
|
174
105
|
|
|
175
106
|
example:
|
|
176
107
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { page, getStylesForLocator, formatStylesAsText, console } from './debugger-examples-types.js'
|
|
2
|
+
|
|
3
|
+
// Example: Get styles for an element and display them
|
|
4
|
+
async function getElementStyles() {
|
|
5
|
+
const loc = page.locator('.my-button')
|
|
6
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
7
|
+
console.log(formatStylesAsText(styles))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Example: Inspect computed styles for a specific element
|
|
11
|
+
async function inspectButtonStyles() {
|
|
12
|
+
const button = page.getByRole('button', { name: 'Submit' })
|
|
13
|
+
const styles = await getStylesForLocator({ locator: button })
|
|
14
|
+
|
|
15
|
+
console.log('Element:', styles.element)
|
|
16
|
+
|
|
17
|
+
if (styles.inlineStyle) {
|
|
18
|
+
console.log('Inline styles:', styles.inlineStyle)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const rule of styles.rules) {
|
|
22
|
+
console.log(`${rule.selector}: ${JSON.stringify(rule.declarations)}`)
|
|
23
|
+
if (rule.source) {
|
|
24
|
+
console.log(` Source: ${rule.source.url}:${rule.source.line}`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Example: Include browser default (user-agent) styles
|
|
30
|
+
async function getStylesWithUserAgent() {
|
|
31
|
+
const loc = page.locator('input[type="text"]')
|
|
32
|
+
const styles = await getStylesForLocator({
|
|
33
|
+
locator: loc,
|
|
34
|
+
includeUserAgentStyles: true,
|
|
35
|
+
})
|
|
36
|
+
console.log(formatStylesAsText(styles))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Example: Find where a CSS property is defined
|
|
40
|
+
async function findPropertySource() {
|
|
41
|
+
const loc = page.locator('.card')
|
|
42
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
43
|
+
|
|
44
|
+
const backgroundRule = styles.rules.find((r) => 'background-color' in r.declarations)
|
|
45
|
+
if (backgroundRule) {
|
|
46
|
+
console.log('background-color defined by:', backgroundRule.selector)
|
|
47
|
+
if (backgroundRule.source) {
|
|
48
|
+
console.log(` at ${backgroundRule.source.url}:${backgroundRule.source.line}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Example: Check inherited styles
|
|
54
|
+
async function checkInheritedStyles() {
|
|
55
|
+
const loc = page.locator('.nested-text')
|
|
56
|
+
const styles = await getStylesForLocator({ locator: loc })
|
|
57
|
+
|
|
58
|
+
const inheritedRules = styles.rules.filter((r) => r.inheritedFrom)
|
|
59
|
+
for (const rule of inheritedRules) {
|
|
60
|
+
console.log(`Inherited from ${rule.inheritedFrom}: ${rule.selector}`)
|
|
61
|
+
console.log(' Properties:', rule.declarations)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Example: Compare styles between two elements
|
|
66
|
+
async function compareStyles() {
|
|
67
|
+
const primary = await getStylesForLocator({ locator: page.locator('.btn-primary') })
|
|
68
|
+
const secondary = await getStylesForLocator({ locator: page.locator('.btn-secondary') })
|
|
69
|
+
|
|
70
|
+
console.log('Primary button:')
|
|
71
|
+
console.log(formatStylesAsText(primary))
|
|
72
|
+
|
|
73
|
+
console.log('Secondary button:')
|
|
74
|
+
console.log(formatStylesAsText(secondary))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { getElementStyles, inspectButtonStyles, getStylesWithUserAgent, findPropertySource, checkInheritedStyles, compareStyles }
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { page, getCDPSession, createDebugger, console } from './debugger-examples-types.js'
|
|
2
|
-
|
|
3
|
-
// Example: List available scripts and set a breakpoint
|
|
4
|
-
async function listScriptsAndSetBreakpoint() {
|
|
5
|
-
const cdp = await getCDPSession({ page })
|
|
6
|
-
const dbg = createDebugger({ cdp })
|
|
7
|
-
await dbg.enable()
|
|
8
|
-
|
|
9
|
-
const scripts = await dbg.listScripts({ search: 'app' })
|
|
10
|
-
console.log(scripts)
|
|
11
|
-
|
|
12
|
-
if (scripts.length > 0) {
|
|
13
|
-
const bpId = await dbg.setBreakpoint({ file: scripts[0].url, line: 100 })
|
|
14
|
-
console.log('Breakpoint set:', bpId)
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Example: Inspect state when paused at a breakpoint
|
|
19
|
-
async function inspectWhenPaused() {
|
|
20
|
-
const cdp = await getCDPSession({ page })
|
|
21
|
-
const dbg = createDebugger({ cdp })
|
|
22
|
-
await dbg.enable()
|
|
23
|
-
|
|
24
|
-
if (dbg.isPaused()) {
|
|
25
|
-
const loc = await dbg.getLocation()
|
|
26
|
-
console.log('Paused at:', loc.url, 'line', loc.lineNumber)
|
|
27
|
-
console.log('Source:', loc.sourceContext)
|
|
28
|
-
|
|
29
|
-
const vars = await dbg.inspectLocalVariables()
|
|
30
|
-
console.log('Variables:', vars)
|
|
31
|
-
|
|
32
|
-
const result = await dbg.evaluate({ expression: 'myVar.length' })
|
|
33
|
-
console.log('myVar.length =', result.value)
|
|
34
|
-
|
|
35
|
-
await dbg.stepOver()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Example: Step through code
|
|
40
|
-
async function stepThroughCode() {
|
|
41
|
-
const cdp = await getCDPSession({ page })
|
|
42
|
-
const dbg = createDebugger({ cdp })
|
|
43
|
-
await dbg.enable()
|
|
44
|
-
|
|
45
|
-
await dbg.setBreakpoint({ file: 'https://example.com/app.js', line: 42 })
|
|
46
|
-
|
|
47
|
-
if (dbg.isPaused()) {
|
|
48
|
-
await dbg.stepOver()
|
|
49
|
-
await dbg.stepInto()
|
|
50
|
-
await dbg.stepOut()
|
|
51
|
-
await dbg.resume()
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Example: Cleanup all breakpoints
|
|
56
|
-
async function cleanupBreakpoints() {
|
|
57
|
-
const cdp = await getCDPSession({ page })
|
|
58
|
-
const dbg = createDebugger({ cdp })
|
|
59
|
-
|
|
60
|
-
const breakpoints = dbg.listBreakpoints()
|
|
61
|
-
for (const bp of breakpoints) {
|
|
62
|
-
await dbg.deleteBreakpoint({ breakpointId: bp.id })
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { listScriptsAndSetBreakpoint, inspectWhenPaused, stepThroughCode, cleanupBreakpoints }
|