rhachet-roles-bhrowser 0.0.0
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/LICENSE +21 -0
- package/dist/contract/sdk/index.d.ts +3 -0
- package/dist/contract/sdk/index.js +10 -0
- package/dist/contract/sdk/index.js.map +1 -0
- package/dist/domain.roles/getRoleRegistry.d.ts +6 -0
- package/dist/domain.roles/getRoleRegistry.js +17 -0
- package/dist/domain.roles/getRoleRegistry.js.map +1 -0
- package/dist/domain.roles/inspector/boot.yml +4 -0
- package/dist/domain.roles/inspector/getInspectorRole.d.ts +6 -0
- package/dist/domain.roles/inspector/getInspectorRole.js +34 -0
- package/dist/domain.roles/inspector/getInspectorRole.js.map +1 -0
- package/dist/domain.roles/inspector/readme.md +7 -0
- package/dist/domain.roles/playwright/boot.yml +37 -0
- package/dist/domain.roles/playwright/briefs/auth/howto.cross-session-auth.md +134 -0
- package/dist/domain.roles/playwright/briefs/debug/howto.capture-state-on-error.md +253 -0
- package/dist/domain.roles/playwright/briefs/debug/howto.debug-movie-frames.md +182 -0
- package/dist/domain.roles/playwright/briefs/diagnosis/howto.browser-diagnosis.md +152 -0
- package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.grep-html-before-selector-guess.md +82 -0
- package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-before-assume.md +73 -0
- package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-before-debug.md +81 -0
- package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-latest-tab.md +84 -0
- package/dist/domain.roles/playwright/briefs/reliability/ref.cdp-reconnection-patterns.md +168 -0
- package/dist/domain.roles/playwright/briefs/reliability/rule.forbid.coordinate-clicks.md +53 -0
- package/dist/domain.roles/playwright/briefs/reliability/rule.require.bounded-timeouts-and-bisection.md +184 -0
- package/dist/domain.roles/playwright/briefs/rule.im_a.bhrowser_lizard.md +68 -0
- package/dist/domain.roles/playwright/briefs/skills/howto.browser-action-playbooks.md +265 -0
- package/dist/domain.roles/playwright/briefs/skills/howto.browser-byhand-work.md +151 -0
- package/dist/domain.roles/playwright/briefs/skills/rule.require.playbooks-over-adhoc.md +90 -0
- package/dist/domain.roles/playwright/briefs/spa/rule.require.spa-navigation-fail-fast.md +109 -0
- package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-content-not-shell.md +128 -0
- package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-react-render.md +127 -0
- package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-target-element.md +123 -0
- package/dist/domain.roles/playwright/briefs/stealth/ref.antibot-escalation.md +120 -0
- package/dist/domain.roles/playwright/briefs/test/howto.test-via-browser.md +285 -0
- package/dist/domain.roles/playwright/briefs/test/rule.forbid.afterall-state-mutation.md +106 -0
- package/dist/domain.roles/playwright/briefs/test/rule.forbid.test-goto.md +77 -0
- package/dist/domain.roles/playwright/briefs/test/rule.require.fast-tests.md +130 -0
- package/dist/domain.roles/playwright/briefs/verification/rule.forbid.unverified-actions.md +95 -0
- package/dist/domain.roles/playwright/briefs/verification/rule.require.action-verification.md +138 -0
- package/dist/domain.roles/playwright/getPlaywrightRole.d.ts +6 -0
- package/dist/domain.roles/playwright/getPlaywrightRole.js +34 -0
- package/dist/domain.roles/playwright/getPlaywrightRole.js.map +1 -0
- package/dist/domain.roles/playwright/readme.md +7 -0
- package/dist/domain.roles/playwright/skills/.test/infra/browser.ts +272 -0
- package/dist/domain.roles/playwright/skills/browser.action.d.ts +23 -0
- package/dist/domain.roles/playwright/skills/browser.action.js +124 -0
- package/dist/domain.roles/playwright/skills/browser.action.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.action.sh +120 -0
- package/dist/domain.roles/playwright/skills/browser.action.ts +148 -0
- package/dist/domain.roles/playwright/skills/browser.describe.d.ts +15 -0
- package/dist/domain.roles/playwright/skills/browser.describe.js +191 -0
- package/dist/domain.roles/playwright/skills/browser.describe.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.describe.sh +45 -0
- package/dist/domain.roles/playwright/skills/browser.describe.ts +255 -0
- package/dist/domain.roles/playwright/skills/browser.lib.sh +725 -0
- package/dist/domain.roles/playwright/skills/browser.session.d.ts +1 -0
- package/dist/domain.roles/playwright/skills/browser.session.js +253 -0
- package/dist/domain.roles/playwright/skills/browser.session.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.session.sh +194 -0
- package/dist/domain.roles/playwright/skills/browser.session.ts +310 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.console.d.ts +33 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.console.js +163 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.console.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.console.sh +57 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.console.ts +212 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.html.d.ts +17 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.html.js +133 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.html.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.html.sh +53 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.html.ts +152 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.meta.d.ts +28 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.meta.js +109 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.meta.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.meta.sh +53 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.meta.ts +136 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.network.d.ts +28 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.network.js +160 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.network.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.network.sh +57 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.network.ts +188 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.screen.d.ts +17 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.screen.js +138 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.screen.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.screen.sh +65 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.screen.ts +187 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.sh +323 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.storage.d.ts +21 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.storage.js +154 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.storage.js.map +1 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.storage.sh +53 -0
- package/dist/domain.roles/playwright/skills/browser.snapshot.storage.ts +172 -0
- package/dist/domain.roles/playwright/skills/browser.start.sh +266 -0
- package/dist/domain.roles/playwright/skills/browser.stop.sh +89 -0
- package/dist/domain.roles/playwright/skills/lib/shared.d.ts +219 -0
- package/dist/domain.roles/playwright/skills/lib/shared.js +416 -0
- package/dist/domain.roles/playwright/skills/lib/shared.js.map +1 -0
- package/dist/domain.roles/playwright/skills/lib/shared.ts +480 -0
- package/dist/domain.roles/readme.md +8 -0
- package/dist/domain.roles/scraper/boot.yml +8 -0
- package/dist/domain.roles/scraper/briefs/cache/howto.cache-browser-scrapes.md +257 -0
- package/dist/domain.roles/scraper/briefs/cache/howto.setup-cache-infrastructure.md +62 -0
- package/dist/domain.roles/scraper/briefs/cache/ref.remote-state-query-cache.md +43 -0
- package/dist/domain.roles/scraper/briefs/cache/rule.require.remote-state-cache.md +119 -0
- package/dist/domain.roles/scraper/getScraperRole.d.ts +6 -0
- package/dist/domain.roles/scraper/getScraperRole.js +34 -0
- package/dist/domain.roles/scraper/getScraperRole.js.map +1 -0
- package/dist/domain.roles/scraper/readme.md +7 -0
- package/dist/domain.roles/scraper/skills/.test/infra/cache.ts +184 -0
- package/dist/domain.roles/scraper/skills/cache.expire.sh +159 -0
- package/dist/domain.roles/scraper/skills/cache.extend.sh +416 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/license.md +21 -0
- package/package.json +116 -0
- package/readme.[seed].md +2 -0
- package/readme.md +65 -0
- package/rhachet.repo.yml +17 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# howto.debug-movie-frames
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
capture sequential snapshots at each step to create a "movie" of automation flow.
|
|
6
|
+
|
|
7
|
+
## .why
|
|
8
|
+
|
|
9
|
+
single snapshots show one moment. movie frames show:
|
|
10
|
+
- state before each action
|
|
11
|
+
- state after each action
|
|
12
|
+
- where exactly flow diverged
|
|
13
|
+
- what changed between steps
|
|
14
|
+
|
|
15
|
+
essential for debug of multi-step flows.
|
|
16
|
+
|
|
17
|
+
## .pattern
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
/**
|
|
21
|
+
* .what = get movie identifier with timestamp fallback if null
|
|
22
|
+
* .why = isolates nullable coalesce + date logic from orchestrator
|
|
23
|
+
*/
|
|
24
|
+
const asMovieId = (input: { movieId: string | null }): string =>
|
|
25
|
+
input.movieId ?? String(Date.now());
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* .what = capture movie frames at each step of automation flow
|
|
29
|
+
* .why = enables frame-by-frame debug of multi-step workflows
|
|
30
|
+
*/
|
|
31
|
+
export const action = async (
|
|
32
|
+
// movieId nullable: caller may not have identifier; defaults to timestamp
|
|
33
|
+
input: { email: string; password: string; movieId: string | null },
|
|
34
|
+
context: { page: Page; session: string },
|
|
35
|
+
) => {
|
|
36
|
+
// determine snapshot directory via named transformer
|
|
37
|
+
const movieId = asMovieId({ movieId: input.movieId });
|
|
38
|
+
const snapshotDir = `.cache/browser.${context.session}/movie.${movieId}`;
|
|
39
|
+
|
|
40
|
+
const frame = async (input: { name: string }) => {
|
|
41
|
+
// capture state at this moment
|
|
42
|
+
await context.page.screenshot({ path: `${snapshotDir}/${input.name}.png` });
|
|
43
|
+
const html = await context.page.content();
|
|
44
|
+
await fs.writeFile(`${snapshotDir}/${input.name}.html`, html);
|
|
45
|
+
const url = context.page.url();
|
|
46
|
+
await fs.writeFile(`${snapshotDir}/${input.name}.url`, url);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// frame 0: initial state
|
|
50
|
+
await frame({ name: '00-initial' });
|
|
51
|
+
|
|
52
|
+
// action 1
|
|
53
|
+
await context.page.fill('#email', input.email);
|
|
54
|
+
await frame({ name: '01-email-filled' });
|
|
55
|
+
|
|
56
|
+
// action 2
|
|
57
|
+
await context.page.fill('#password', input.password);
|
|
58
|
+
await frame({ name: '02-password-filled' });
|
|
59
|
+
|
|
60
|
+
// action 3
|
|
61
|
+
await context.page.click('#submit');
|
|
62
|
+
await frame({ name: '03-submit-clicked' });
|
|
63
|
+
|
|
64
|
+
// wait for result
|
|
65
|
+
await context.page.waitForURL('**/dashboard');
|
|
66
|
+
await frame({ name: '04-dashboard-loaded' });
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## .output structure
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
.cache/browser.$SESSION/movie.$TIMESTAMP/
|
|
74
|
+
├── 00-initial.png
|
|
75
|
+
├── 00-initial.html
|
|
76
|
+
├── 00-initial.url
|
|
77
|
+
├── 01-email-filled.png
|
|
78
|
+
├── 01-email-filled.html
|
|
79
|
+
├── 01-email-filled.url
|
|
80
|
+
├── 02-password-filled.png
|
|
81
|
+
├── 02-password-filled.html
|
|
82
|
+
├── 02-password-filled.url
|
|
83
|
+
├── 03-submit-clicked.png
|
|
84
|
+
├── 03-submit-clicked.html
|
|
85
|
+
├── 03-submit-clicked.url
|
|
86
|
+
├── 04-dashboard-loaded.png
|
|
87
|
+
├── 04-dashboard-loaded.html
|
|
88
|
+
└── 04-dashboard-loaded.url
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## .debug workflow
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
flow failed at step 3
|
|
95
|
+
│
|
|
96
|
+
├─> open 02-password-filled.png
|
|
97
|
+
│ └─ "password field filled correctly"
|
|
98
|
+
│
|
|
99
|
+
├─> open 03-submit-clicked.png
|
|
100
|
+
│ └─ "error: submit button was disabled!"
|
|
101
|
+
│
|
|
102
|
+
└─> root cause: validation error not visible in 02
|
|
103
|
+
└─ check 02-password-filled.html for error message
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## .frame helper
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
/**
|
|
110
|
+
* .what = format frame number as zero-padded prefix
|
|
111
|
+
* .why = ensures consistent two-digit frame numbers for sort order
|
|
112
|
+
*/
|
|
113
|
+
const asFramePrefix = (input: { numFrame: number }): string =>
|
|
114
|
+
String(input.numFrame).padStart(2, '0');
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* .what = persist single frame snapshot with explicit sequence number
|
|
118
|
+
* .why = pure function for frame capture; caller manages sequence
|
|
119
|
+
*/
|
|
120
|
+
const setFrameSnapshot = async (
|
|
121
|
+
input: { label: string; numFrame: number },
|
|
122
|
+
context: { page: Page; dir: string },
|
|
123
|
+
): Promise<string> => {
|
|
124
|
+
// format frame name with sequence prefix
|
|
125
|
+
const prefix = asFramePrefix({ numFrame: input.numFrame });
|
|
126
|
+
const name = `${prefix}-${input.label}`;
|
|
127
|
+
|
|
128
|
+
// capture screenshot
|
|
129
|
+
await context.page.screenshot({ path: `${context.dir}/${name}.png`, fullPage: true });
|
|
130
|
+
|
|
131
|
+
// capture html content
|
|
132
|
+
await fs.writeFile(`${context.dir}/${name}.html`, await context.page.content());
|
|
133
|
+
|
|
134
|
+
// capture metadata
|
|
135
|
+
await fs.writeFile(`${context.dir}/${name}.meta.json`, JSON.stringify({
|
|
136
|
+
url: context.page.url(),
|
|
137
|
+
title: await context.page.title(),
|
|
138
|
+
time: new Date().toJSON(),
|
|
139
|
+
}, null, 2));
|
|
140
|
+
|
|
141
|
+
return name;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// usage in playbook
|
|
145
|
+
/**
|
|
146
|
+
* .what = example playbook with frame capture at each step
|
|
147
|
+
* .why = demonstrates movie frame debug pattern in practice
|
|
148
|
+
*/
|
|
149
|
+
export const action = async (
|
|
150
|
+
// movieId nullable: caller may not have identifier; defaults to timestamp
|
|
151
|
+
input: { movieId: string | null },
|
|
152
|
+
context: { page: Page },
|
|
153
|
+
) => {
|
|
154
|
+
// frame capture context via named transformer (immutable per frame)
|
|
155
|
+
const movieId = asMovieId({ movieId: input.movieId });
|
|
156
|
+
const frameContext = { page: context.page, dir: `.cache/movie.${movieId}` };
|
|
157
|
+
|
|
158
|
+
// capture frames with explicit sequence numbers
|
|
159
|
+
await setFrameSnapshot({ label: 'initial', numFrame: 0 }, frameContext);
|
|
160
|
+
await context.page.click('#start');
|
|
161
|
+
await setFrameSnapshot({ label: 'started', numFrame: 1 }, frameContext);
|
|
162
|
+
// ...
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## .comparison tools
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# view frames in sequence
|
|
170
|
+
ls -la .cache/browser.$SESSION/movie.*/
|
|
171
|
+
|
|
172
|
+
# compare two frames
|
|
173
|
+
diff 02-before.html 03-after.html
|
|
174
|
+
|
|
175
|
+
# image diff (requires imagemagick)
|
|
176
|
+
compare 02-before.png 03-after.png diff.png
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## .see also
|
|
180
|
+
|
|
181
|
+
- `howto.capture-state-on-error.md` — error state capture
|
|
182
|
+
- `howto.browser-diagnosis.md` — diagnosis workflow
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# howto.browser-diagnosis
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
systematic approach to diagnose browser automation issues.
|
|
6
|
+
|
|
7
|
+
## .workflow
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
1. snapshot
|
|
11
|
+
└─> freeze state before it changes
|
|
12
|
+
|
|
13
|
+
2. observe
|
|
14
|
+
├─> screenshot: what's visible?
|
|
15
|
+
├─> html: what's in dom?
|
|
16
|
+
├─> console: any errors?
|
|
17
|
+
└─> network: requests failed?
|
|
18
|
+
|
|
19
|
+
3. hypothesize
|
|
20
|
+
└─> form theory based on evidence
|
|
21
|
+
|
|
22
|
+
4. test
|
|
23
|
+
└─> minimal action to test theory
|
|
24
|
+
|
|
25
|
+
5. iterate
|
|
26
|
+
└─> new snapshot, new observation
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## .step 1: snapshot
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# capture all artifacts
|
|
33
|
+
browser.snapshot --session $SESSION --focused
|
|
34
|
+
|
|
35
|
+
# output location
|
|
36
|
+
.cache/browser.$SESSION/snapshot.$TIMESTAMP/
|
|
37
|
+
├── snapshot.meta.json
|
|
38
|
+
├── snapshot.png
|
|
39
|
+
├── snapshot.html
|
|
40
|
+
├── snapshot.storage.json
|
|
41
|
+
├── snapshot.console.json
|
|
42
|
+
└── snapshot.network.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## .step 2: observe
|
|
46
|
+
|
|
47
|
+
### screenshot analysis
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# view screenshot
|
|
51
|
+
open .cache/browser.$SESSION/snapshot.*/snapshot.png
|
|
52
|
+
|
|
53
|
+
# check:
|
|
54
|
+
# - is the expected page visible?
|
|
55
|
+
# - any overlays that block content?
|
|
56
|
+
# - load indicators present?
|
|
57
|
+
# - error messages displayed?
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### html analysis
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# search for element
|
|
64
|
+
grep -i "submit" snapshot.html
|
|
65
|
+
|
|
66
|
+
# check element visibility
|
|
67
|
+
grep -B5 -A5 "submit" snapshot.html
|
|
68
|
+
# look for: style="display:none", class="hidden", etc.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### console analysis
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# check for errors
|
|
75
|
+
cat snapshot.console.json | jq '.[] | select(.type == "error")'
|
|
76
|
+
|
|
77
|
+
# common issues:
|
|
78
|
+
# - uncaught TypeError (JS error)
|
|
79
|
+
# - CORS errors (blocked requests)
|
|
80
|
+
# - 404 (absent resources)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### network analysis
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# check for failed requests
|
|
87
|
+
cat snapshot.network.json | jq '.[] | select(.status >= 400)'
|
|
88
|
+
|
|
89
|
+
# check for awaited requests
|
|
90
|
+
cat snapshot.network.json | jq '.[] | select(.status == null)'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## .step 3: common diagnoses
|
|
94
|
+
|
|
95
|
+
| symptom | likely cause | verification |
|
|
96
|
+
|---------|--------------|--------------|
|
|
97
|
+
| element not found | not in dom | grep html |
|
|
98
|
+
| element not visible | CSS hidden | grep for display/visibility |
|
|
99
|
+
| timeout | still loads | check network awaited |
|
|
100
|
+
| wrong page | navigation issue | check screenshot url |
|
|
101
|
+
| stale element | dom changed | re-snapshot |
|
|
102
|
+
| click blocked | overlay present | screenshot shows modal |
|
|
103
|
+
|
|
104
|
+
## .step 4: targeted action
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# test hypothesis with minimal playbook
|
|
108
|
+
browser.action --session $SESSION --play test-hypothesis.play.ts
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// test-hypothesis.play.ts
|
|
113
|
+
/**
|
|
114
|
+
* .what = test hypothesis about element visibility
|
|
115
|
+
* .why = validates theory with minimal action before complex fix
|
|
116
|
+
*/
|
|
117
|
+
export const action = async (
|
|
118
|
+
input: Record<string, never>,
|
|
119
|
+
context: { page: Page; log: LogMethods },
|
|
120
|
+
) => {
|
|
121
|
+
// check element state
|
|
122
|
+
const element = await context.page.$('#my-element');
|
|
123
|
+
context.log.info('element exists:', { exists: !!element });
|
|
124
|
+
context.log.info('element visible:', { visible: await element?.isVisible() });
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## .step 5: iterate
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# new snapshot after action
|
|
132
|
+
browser.snapshot --session $SESSION --focused
|
|
133
|
+
|
|
134
|
+
# compare to previous
|
|
135
|
+
diff snapshot.before.html snapshot.after.html
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## .diagnosis checklist
|
|
139
|
+
|
|
140
|
+
- [ ] snapshot captured before debug?
|
|
141
|
+
- [ ] screenshot reviewed?
|
|
142
|
+
- [ ] html searched for target element?
|
|
143
|
+
- [ ] console checked for errors?
|
|
144
|
+
- [ ] network checked for failures?
|
|
145
|
+
- [ ] hypothesis formed from evidence?
|
|
146
|
+
- [ ] minimal test action executed?
|
|
147
|
+
|
|
148
|
+
## .see also
|
|
149
|
+
|
|
150
|
+
- `rule.require.snapshot-before-debug.md` — always snapshot first
|
|
151
|
+
- `rule.require.grep-html-before-selector-guess.md` — search before select
|
|
152
|
+
- `howto.debug-movie-frames.md` — capture action sequences
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# rule.require.grep-html-before-selector-guess
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
search the html snapshot before you guess selectors.
|
|
6
|
+
|
|
7
|
+
## .why
|
|
8
|
+
|
|
9
|
+
guessed selectors fail because:
|
|
10
|
+
- class names differ from what you expect
|
|
11
|
+
- IDs are dynamically generated
|
|
12
|
+
- structure changed since you last saw it
|
|
13
|
+
- framework adds wrapper elements
|
|
14
|
+
|
|
15
|
+
the html snapshot is the source of truth.
|
|
16
|
+
|
|
17
|
+
## .pattern
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 1. capture snapshot
|
|
21
|
+
browser.snapshot --session $SESSION --focused
|
|
22
|
+
|
|
23
|
+
# 2. read the html
|
|
24
|
+
cat .cache/browser.$SESSION/snapshot.*/snapshot.html
|
|
25
|
+
|
|
26
|
+
# 3. search for your target
|
|
27
|
+
grep -i "submit" .cache/browser.$SESSION/snapshot.*/snapshot.html
|
|
28
|
+
grep -i "button" .cache/browser.$SESSION/snapshot.*/snapshot.html
|
|
29
|
+
grep -i "login" .cache/browser.$SESSION/snapshot.*/snapshot.html
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## .selector discovery
|
|
33
|
+
|
|
34
|
+
| look for | grep pattern |
|
|
35
|
+
|----------|--------------|
|
|
36
|
+
| button text | `grep -i "sign in"` |
|
|
37
|
+
| input placeholder | `grep -i "placeholder"` |
|
|
38
|
+
| form action | `grep -i "action="` |
|
|
39
|
+
| data attributes | `grep "data-test"` |
|
|
40
|
+
| aria labels | `grep "aria-label"` |
|
|
41
|
+
|
|
42
|
+
## .examples
|
|
43
|
+
|
|
44
|
+
### good: search first
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# find the login button
|
|
48
|
+
grep -i "sign in\|login\|submit" snapshot.html
|
|
49
|
+
# output: <button data-test="login-btn" class="xyz123">Sign In</button>
|
|
50
|
+
|
|
51
|
+
# use the stable selector
|
|
52
|
+
page.click('[data-test="login-btn"]')
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### bad: guess selector
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 🚫 guess based on common patterns
|
|
59
|
+
page.click('#login') # not found
|
|
60
|
+
page.click('.login-button') # wrong class
|
|
61
|
+
page.click('button[type="submit"]') # multiple matches
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## .selector preference order
|
|
65
|
+
|
|
66
|
+
| priority | selector type | why |
|
|
67
|
+
|----------|---------------|-----|
|
|
68
|
+
| 1 | `data-test="..."` | stable, for test |
|
|
69
|
+
| 2 | `aria-label="..."` | semantic, stable |
|
|
70
|
+
| 3 | `[role="..."]` | semantic |
|
|
71
|
+
| 4 | unique text | `text="Sign In"` |
|
|
72
|
+
| 5 | id | may be dynamic |
|
|
73
|
+
| 6 | class | often minified/dynamic |
|
|
74
|
+
|
|
75
|
+
## .enforcement
|
|
76
|
+
|
|
77
|
+
guessed selector without search of html = blocker
|
|
78
|
+
|
|
79
|
+
## .see also
|
|
80
|
+
|
|
81
|
+
- `rule.require.snapshot-before-assume.md` — snapshot first
|
|
82
|
+
- `browser.snapshot html` — capture html
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# rule.require.snapshot-before-assume
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
take a snapshot before you assume page state. never guess what the browser shows.
|
|
6
|
+
|
|
7
|
+
## .why
|
|
8
|
+
|
|
9
|
+
assumptions about page state cause:
|
|
10
|
+
- wrong selectors (element not visible)
|
|
11
|
+
- wrong time (content not loaded)
|
|
12
|
+
- wrong context (different page than expected)
|
|
13
|
+
|
|
14
|
+
snapshots provide evidence for decisions.
|
|
15
|
+
|
|
16
|
+
## .pattern
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# before any assumption about state
|
|
20
|
+
browser.snapshot --session $SESSION --focused
|
|
21
|
+
|
|
22
|
+
# now you can see:
|
|
23
|
+
# - screenshot: what's visible
|
|
24
|
+
# - html: what's in dom
|
|
25
|
+
# - console: any errors
|
|
26
|
+
# - network: queued requests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## .antipattern
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 🚫 guess selector without view of page
|
|
33
|
+
browser.action --session $SESSION --play click-submit.play.ts
|
|
34
|
+
# fails: "element not found"
|
|
35
|
+
# you: "but it should be there!"
|
|
36
|
+
# reality: page shows login form, not submit button
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## .examples
|
|
40
|
+
|
|
41
|
+
### good: snapshot first
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# see what's actually on page
|
|
45
|
+
browser.snapshot --session $SESSION --focused
|
|
46
|
+
# output: screenshot shows load spinner
|
|
47
|
+
|
|
48
|
+
# now you know: wait for content
|
|
49
|
+
browser.action --session $SESSION --play wait-for-content.play.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### bad: assume then debug
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# assume page is ready
|
|
56
|
+
browser.action --session $SESSION --play submit-form.play.ts
|
|
57
|
+
# error: timeout awaited for #submit-button
|
|
58
|
+
|
|
59
|
+
# now you snapshot to debug
|
|
60
|
+
browser.snapshot --session $SESSION --focused
|
|
61
|
+
# output: page shows captcha challenge
|
|
62
|
+
|
|
63
|
+
# wasted time on wrong assumption
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## .enforcement
|
|
67
|
+
|
|
68
|
+
assumed page state without prior snapshot = blocker
|
|
69
|
+
|
|
70
|
+
## .see also
|
|
71
|
+
|
|
72
|
+
- `rule.require.snapshot-before-debug.md` — snapshot before debug
|
|
73
|
+
- `rule.require.grep-html-before-selector-guess.md` — search before select
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# rule.require.snapshot-before-debug
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
before you debug any browser issue, capture a full snapshot.
|
|
6
|
+
|
|
7
|
+
## .why
|
|
8
|
+
|
|
9
|
+
debug without evidence is a guess:
|
|
10
|
+
- symptoms change between observations
|
|
11
|
+
- state mutates while you investigate
|
|
12
|
+
- memory of what you saw is unreliable
|
|
13
|
+
|
|
14
|
+
a snapshot freezes state for analysis.
|
|
15
|
+
|
|
16
|
+
## .pattern
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# action failed
|
|
20
|
+
browser.action --session $SESSION --play failed-action.play.ts
|
|
21
|
+
# error: could not find element
|
|
22
|
+
|
|
23
|
+
# FIRST: capture state
|
|
24
|
+
browser.snapshot --session $SESSION --focused
|
|
25
|
+
|
|
26
|
+
# NOW: analyze frozen state
|
|
27
|
+
# - screenshot: what user sees
|
|
28
|
+
# - html: what dom contains
|
|
29
|
+
# - console: error messages
|
|
30
|
+
# - network: failed requests
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## .what snapshots capture
|
|
34
|
+
|
|
35
|
+
| artifact | reveals |
|
|
36
|
+
|----------|---------|
|
|
37
|
+
| screenshot | visual state, overlays, modals |
|
|
38
|
+
| html | dom structure, hidden elements |
|
|
39
|
+
| console | js errors, warnings, logs |
|
|
40
|
+
| network | failed requests, awaited loads |
|
|
41
|
+
| storage | cookies, localStorage state |
|
|
42
|
+
| meta | url, title, tab info |
|
|
43
|
+
|
|
44
|
+
## .diagnosis workflow
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
error occurs
|
|
48
|
+
│
|
|
49
|
+
└─> snapshot immediately
|
|
50
|
+
│
|
|
51
|
+
├─> check screenshot
|
|
52
|
+
│ └─ visible state matches expectation?
|
|
53
|
+
│
|
|
54
|
+
├─> check html
|
|
55
|
+
│ └─ element exists in dom?
|
|
56
|
+
│
|
|
57
|
+
├─> check console
|
|
58
|
+
│ └─ js errors that prevent render?
|
|
59
|
+
│
|
|
60
|
+
└─> check network
|
|
61
|
+
└─ request failed or awaited?
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## .antipattern
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# 🚫 debug without snapshot
|
|
68
|
+
# "let me check if the element exists..."
|
|
69
|
+
browser.action --session $SESSION --play check-element.play.ts
|
|
70
|
+
# "hmm, now it says different error..."
|
|
71
|
+
# state has changed, you chase ghosts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## .enforcement
|
|
75
|
+
|
|
76
|
+
debug browser issue without prior snapshot = blocker
|
|
77
|
+
|
|
78
|
+
## .see also
|
|
79
|
+
|
|
80
|
+
- `rule.require.snapshot-before-assume.md` — snapshot before assume
|
|
81
|
+
- `howto.browser-diagnosis.md` — full diagnosis workflow
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# rule.require.snapshot-latest-tab
|
|
2
|
+
|
|
3
|
+
## .what
|
|
4
|
+
|
|
5
|
+
when you diagnose issues, snapshot the latest (most recently opened) tab first.
|
|
6
|
+
|
|
7
|
+
## .why
|
|
8
|
+
|
|
9
|
+
- navigation often opens new tabs
|
|
10
|
+
- popups and oauth flows spawn tabs
|
|
11
|
+
- the action that failed likely happened in the newest tab
|
|
12
|
+
- `--focused` gets the human-focused tab, but automation may be in another
|
|
13
|
+
|
|
14
|
+
## .pattern
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# first: see all tabs
|
|
18
|
+
browser.describe --session $SESSION
|
|
19
|
+
# output shows: tab 0 (original), tab 1 (oauth popup), tab 2 (result)
|
|
20
|
+
|
|
21
|
+
# snapshot latest tab (highest index)
|
|
22
|
+
browser.snapshot --session $SESSION --tab 2 --url 'expected-url.com'
|
|
23
|
+
|
|
24
|
+
# if that's not the issue, work backwards
|
|
25
|
+
browser.snapshot --session $SESSION --tab 1 --url 'oauth-provider.com'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## .tab order
|
|
29
|
+
|
|
30
|
+
| index | typical content |
|
|
31
|
+
|-------|-----------------|
|
|
32
|
+
| 0 | original/start page |
|
|
33
|
+
| 1 | first popup/redirect |
|
|
34
|
+
| -1 | most recent (alias for highest) |
|
|
35
|
+
|
|
36
|
+
## .common scenarios
|
|
37
|
+
|
|
38
|
+
### oauth flow
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
user clicks "login with google"
|
|
42
|
+
│
|
|
43
|
+
├─ tab 0: original site (awaited)
|
|
44
|
+
└─ tab 1: google oauth (active)
|
|
45
|
+
│
|
|
46
|
+
└─ tab 2: callback page (final result)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
snapshot tab 2 first — that's where the result is.
|
|
50
|
+
|
|
51
|
+
### file download
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
user clicks "download"
|
|
55
|
+
│
|
|
56
|
+
├─ tab 0: original page
|
|
57
|
+
└─ tab 1: download initiated (may close)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### popup blocked
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
action opens popup
|
|
64
|
+
│
|
|
65
|
+
└─ tab 0: popup blocked notification
|
|
66
|
+
(no new tab created)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## .antipattern
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# 🚫 always snapshot tab 0
|
|
73
|
+
browser.snapshot --session $SESSION --tab 0
|
|
74
|
+
# shows original page, but error is in popup tab
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## .enforcement
|
|
78
|
+
|
|
79
|
+
diagnose navigation issue without check of latest tab = blocker
|
|
80
|
+
|
|
81
|
+
## .see also
|
|
82
|
+
|
|
83
|
+
- `browser.describe.sh` — list all tabs
|
|
84
|
+
- `rule.require.snapshot-before-debug.md` — snapshot first
|