playwriter 0.0.30 → 0.0.33
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/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 +25 -49
- 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/debugger-examples-types.ts +4 -1
- package/src/mcp.ts +25 -49
- 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.33",
|
|
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",
|
|
@@ -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
|
@@ -428,75 +428,51 @@ const server = new McpServer({
|
|
|
428
428
|
})
|
|
429
429
|
|
|
430
430
|
const promptContent =
|
|
431
|
-
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'prompt.md'), 'utf-8') +
|
|
431
|
+
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', '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
|
],
|
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 }
|