astro-better-declarative-screenshots 0.1.0 → 0.2.1

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/index.mjs CHANGED
@@ -1,56 +1,3 @@
1
- // Astro integration entry point.
2
- // usage in astro.config.mjs:
3
- // import screenshots from 'astro-better-declarative-screenshots';
4
- // export default defineConfig({ integrations: [screenshots()] });
5
-
6
- import { loadConfig } from './src/config.mjs';
7
- import path from 'path';
8
-
9
- /**
10
- * @param {Partial<import('./src/config.mjs').ConfigSchema['_type']>} [integrationConfig]
11
- * @returns {import('astro').AstroIntegration}
12
- */
13
- export default function screenshotsIntegration(integrationConfig = {}) {
14
- let resolvedConfig;
15
-
16
- return {
17
- name: 'astro-better-declarative-screenshots',
18
-
19
- hooks: {
20
- 'astro:config:setup': async ({ config: astroConfig, addWatchFile, logger }) => {
21
- const projectRoot = astroConfig.root
22
- ? new URL(astroConfig.root).pathname
23
- : process.cwd();
24
-
25
- // load and validate the screenshot.config.mjs
26
- try {
27
- resolvedConfig = await loadConfig(projectRoot);
28
- } catch (e) {
29
- if (integrationConfig.strict === false) {
30
- logger.warn(`[astro-better-declarative-screenshots] ${e.message}`);
31
- resolvedConfig = null;
32
- } else {
33
- throw e;
34
- }
35
- }
36
-
37
- // watch the config file so the dev server restarts when it changes
38
- const configFile = path.join(projectRoot, 'screenshot.config.mjs');
39
- addWatchFile(configFile);
40
- },
41
-
42
- 'astro:build:done': async ({ logger }) => {
43
- if (!resolvedConfig) return;
44
- logger.info(
45
- '[astro-better-declarative-screenshots] build done. ' +
46
- 'Run `take-screenshots` to update screenshots.'
47
- );
48
- },
49
- },
50
- };
51
- }
52
-
53
- // re-export utilities consumers may want
54
- export { loadConfig } from './src/config.mjs';
55
- export { discoverScreenshots } from './src/discover.mjs';
56
1
  export { deriveName } from './src/naming.mjs';
2
+ export { loadConfig, ConfigSchema, DockerSchema } from './src/config.mjs';
3
+ export { injectHighlights, scrollIntoView } from './src/highlight.mjs';
package/package.json CHANGED
@@ -1,36 +1,16 @@
1
1
  {
2
2
  "name": "astro-better-declarative-screenshots",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
- "description": "Declarative, Docker-backed screenshots for Astro docs. Define the page and the elements to highlight, and this produces a screenshot.",
6
5
  "exports": {
7
6
  ".": "./index.mjs",
8
7
  "./Screenshot.astro": "./Screenshot.astro",
9
8
  "./Highlight.astro": "./Highlight.astro"
10
9
  },
11
- "bin": {
12
- "take-screenshots": "./scripts/take-screenshots.mjs",
13
- "check-screenshots": "./scripts/check-screenshots.mjs"
14
- },
15
- "scripts": {
16
- "test": "vitest run",
17
- "test:watch": "vitest"
18
- },
19
10
  "peerDependencies": {
20
11
  "astro": ">=4.0.0"
21
12
  },
22
13
  "dependencies": {
23
- "playwright": "^1.45.0",
24
- "sharp": "^0.33.0",
25
- "pixelmatch": "^6.0.0",
26
- "pngjs": "^7.0.0",
27
- "glob": "^11.0.0",
28
14
  "zod": "^3.23.0"
29
- },
30
- "devDependencies": {
31
- "vitest": "^2.0.0",
32
- "astro": "^4.0.0"
33
- },
34
- "keywords": ["astro", "screenshots", "declarative", "docs", "playwright", "integration"],
35
- "license": "MIT"
15
+ }
36
16
  }
package/src/config.mjs CHANGED
@@ -82,13 +82,13 @@ const ConfigSchema = z.object({
82
82
  strict: z.boolean().default(process.env.SCREENSHOTS_STRICT === 'true'),
83
83
 
84
84
  // called once before all screenshots are taken (good for login flows)
85
- beforeAll: z.function().args(z.any()).returns(z.promise(z.any())).optional(),
85
+ beforeAll: z.function().optional(),
86
86
 
87
87
  // called before each individual screenshot
88
- beforeScreenshot: z.function().args(z.any(), z.any()).returns(z.promise(z.any())).optional(),
88
+ beforeScreenshot: z.function().optional(),
89
89
 
90
90
  // called once after all screenshots are taken
91
- afterAll: z.function().returns(z.promise(z.any())).optional(),
91
+ afterAll: z.function().optional(),
92
92
  });
93
93
 
94
94
  export async function loadConfig(projectRoot) {
@@ -1,68 +0,0 @@
1
- name: Check screenshots
2
-
3
- on:
4
- schedule:
5
- # every Monday at 9am UTC -- catches visual drift from app updates
6
- - cron: '0 9 * * 1'
7
- workflow_dispatch:
8
- inputs:
9
- filter:
10
- description: 'Filter: only check screenshots matching this string'
11
- required: false
12
-
13
- jobs:
14
- check:
15
- runs-on: ubuntu-latest
16
-
17
- steps:
18
- - uses: actions/checkout@v4
19
-
20
- - uses: actions/setup-node@v4
21
- with:
22
- node-version: '20'
23
- cache: npm
24
-
25
- - name: Install dependencies
26
- run: npm ci
27
-
28
- - name: Install Playwright browsers
29
- run: npx playwright install --with-deps webkit
30
-
31
- - name: Check screenshots
32
- run: |
33
- FILTER="${{ github.event.inputs.filter }}"
34
- if [ -n "$FILTER" ]; then
35
- npx check-screenshots --filter="$FILTER" --diff-dir=.screenshot-diffs
36
- else
37
- npx check-screenshots --diff-dir=.screenshot-diffs
38
- fi
39
-
40
- - name: Upload diff artifacts
41
- if: failure()
42
- uses: actions/upload-artifact@v4
43
- with:
44
- name: screenshot-diffs
45
- path: .screenshot-diffs/
46
- retention-days: 14
47
-
48
- - name: Comment on PR with diff summary
49
- if: failure() && github.event_name == 'pull_request'
50
- uses: actions/github-script@v7
51
- with:
52
- script: |
53
- const fs = require('fs');
54
- const diffs = fs.readdirSync('.screenshot-diffs').filter(f => f.endsWith('.diff.png'));
55
- const body = [
56
- '## Screenshot changes detected',
57
- '',
58
- `${diffs.length} screenshot(s) changed:`,
59
- ...diffs.map(f => `- \`${f.replace('.diff.png', '')}\``),
60
- '',
61
- 'Download the `screenshot-diffs` artifact to review the changes.',
62
- 'If they are intentional, run `take-screenshots` locally to update the reference files.',
63
- ].join('\n');
64
- github.rest.issues.createComment({
65
- ...context.repo,
66
- issue_number: context.issue.number,
67
- body,
68
- });
package/CONFIGURATION.md DELETED
@@ -1,251 +0,0 @@
1
- # Configuration reference
2
-
3
- All configuration lives in `screenshot.config.mjs` at your Astro project root.
4
-
5
- ```js
6
- export default {
7
- // ...
8
- };
9
- ```
10
-
11
- ---
12
-
13
- ## Top-level options
14
-
15
- ### `baseUrl` (required)
16
-
17
- The base URL of the running app. Screenshot URLs are resolved relative to this.
18
-
19
- ```js
20
- baseUrl: 'http://localhost:9011'
21
- ```
22
-
23
- ### `outputDir`
24
-
25
- Where to write screenshot PNGs, relative to the project root.
26
-
27
- Default: `'./src/assets/screenshots'`
28
-
29
- ```js
30
- outputDir: 'public/screenshots'
31
- ```
32
-
33
- Note: if you store screenshots in `public/`, they are served as static files without Astro's asset pipeline. If you store them in `src/assets/`, Astro can optimize them. Pick based on whether you want image optimization.
34
-
35
- ### `browser`
36
-
37
- Playwright browser engine.
38
-
39
- Default: `'webkit'`
40
-
41
- Options: `'webkit'` | `'chromium'` | `'firefox'`
42
-
43
- WebKit gives the most accurate rendering for macOS Safari-style chrome. Use Chromium for CI environments where WebKit may not be available.
44
-
45
- ### `colorScheme`
46
-
47
- Default color scheme for captured pages.
48
-
49
- Default: `'light'`
50
-
51
- Options: `'light'` | `'dark'`
52
-
53
- ### `strict`
54
-
55
- When `true`, `<Screenshot>` throws at build time if the PNG file is missing. Defaults to the value of the `SCREENSHOTS_STRICT` environment variable.
56
-
57
- Default: `false`
58
-
59
- ---
60
-
61
- ## `window`
62
-
63
- Default viewport dimensions.
64
-
65
- ```js
66
- window: { width: 1280, height: 800 }
67
- ```
68
-
69
- Per-screenshot overrides are available via the `<Screenshot width height />` props.
70
-
71
- ### `minWindow` / `maxWindow`
72
-
73
- Viewport size guardrails. Any per-screenshot `width`/`height` values are clamped to these bounds.
74
-
75
- ```js
76
- minWindow: { width: 640, height: 400 },
77
- maxWindow: { width: 2560, height: 1600 },
78
- ```
79
-
80
- ### `maxFullPage`
81
-
82
- Maximum dimensions for full-page captures.
83
-
84
- ```js
85
- maxFullPage: { width: 2560, height: 8000 }
86
- ```
87
-
88
- ---
89
-
90
- ## `chrome`
91
-
92
- Window chrome composited over each screenshot.
93
-
94
- ```js
95
- chrome: {
96
- style: 'safari-macos', // or 'none'
97
- showUrl: true,
98
- dark: false,
99
- shadowBlur: 40,
100
- shadowPadding: 48,
101
- }
102
- ```
103
-
104
- ### `chrome.style`
105
-
106
- `'safari-macos'` renders a macOS Safari-style title bar with traffic lights and a URL bar.
107
- `'none'` skips chrome entirely.
108
-
109
- ### `chrome.showUrl`
110
-
111
- Whether to render the URL bar inside the chrome.
112
-
113
- ### `chrome.dark`
114
-
115
- Use dark chrome regardless of page color scheme.
116
-
117
- ### `chrome.shadowBlur`
118
-
119
- Drop shadow blur radius in pixels. Set to `0` to disable the shadow.
120
-
121
- ### `chrome.shadowPadding`
122
-
123
- Extra padding added around the window to accommodate the shadow. Increase this if the shadow is clipped.
124
-
125
- ---
126
-
127
- ## `docker`
128
-
129
- Docker service configuration. The CLI boots this service before capturing screenshots and shuts it down when done.
130
-
131
- ```js
132
- docker: {
133
- compose: 'docker-compose.yml',
134
- service: 'app',
135
- kickstart: 'kickstart/bootstrap.json',
136
- healthcheck: {
137
- url: 'http://localhost:9011/api/status',
138
- timeout: 60000,
139
- interval: 2000,
140
- },
141
- env: {
142
- DATABASE_PASSWORD: 'change-in-production',
143
- },
144
- },
145
- ```
146
-
147
- ### `docker.compose`
148
-
149
- Path to a `docker-compose.yml` file, relative to the project root. The CLI runs `docker compose -f <compose> up -d [service]`.
150
-
151
- ### `docker.service`
152
-
153
- The specific Docker Compose service to start. If omitted, all services in the file are started.
154
-
155
- ### `docker.bootstrap`
156
-
157
- Path to a seed/bootstrap file to pass into the container, relative to the project root. Two environment variables become available inside `docker-compose.yml`:
158
-
159
- - `SCREENSHOT_BOOTSTRAP_PATH` -- absolute path to the file on the host
160
- - `SCREENSHOT_BOOTSTRAP_CONTENT` -- the file's full text content
161
-
162
- Wire these into your container however your app needs them. Examples:
163
-
164
- **FusionAuth** (kickstart):
165
- ```yaml
166
- environment:
167
- FUSIONAUTH_APP_KICKSTART_FILE: ${SCREENSHOT_BOOTSTRAP_PATH}
168
- ```
169
-
170
- **Node.js app** (seed script via bind mount):
171
- ```yaml
172
- volumes:
173
- - ${SCREENSHOT_BOOTSTRAP_PATH}:/app/seed.json:ro
174
- ```
175
-
176
- **Generic** (pass as env var for a custom entrypoint):
177
- ```yaml
178
- environment:
179
- APP_SEED_DATA: ${SCREENSHOT_BOOTSTRAP_CONTENT}
180
- ```
181
-
182
- `kickstart` is accepted as a legacy alias for `bootstrap`.
183
-
184
- ### `docker.postStart`
185
-
186
- Optional shell command to run after the health check passes and before `beforeAll`. Runs in the project root with the project's environment. Use for database migrations, extra seeding, or any setup that can't be bundled into the container's startup.
187
-
188
- ```js
189
- postStart: 'node scripts/seed-extra.mjs'
190
- // or: 'docker exec myapp-1 npm run db:migrate'
191
- ```
192
-
193
- ### `docker.healthcheck.url`
194
-
195
- URL polled to determine when the app is ready. The CLI waits for an HTTP 200 response before proceeding.
196
-
197
- ### `docker.healthcheck.timeout`
198
-
199
- Maximum time in milliseconds to wait for the health check. Default: `60000` (1 minute).
200
-
201
- ### `docker.healthcheck.interval`
202
-
203
- Polling interval in milliseconds. Default: `2000`.
204
-
205
- ### `docker.env`
206
-
207
- Extra environment variables merged into the container environment when running `docker compose up`.
208
-
209
- ---
210
-
211
- ## Hooks
212
-
213
- Hooks are async functions that let you run code at key points in the capture lifecycle.
214
-
215
- ### `beforeAll`
216
-
217
- Called once before any screenshots are taken. Receives a Playwright `BrowserContext`. Use this for login flows or global app state setup.
218
-
219
- ```js
220
- beforeAll: async (context) => {
221
- const page = await context.newPage();
222
- await page.goto('http://localhost:9011/admin/login');
223
- await page.fill('#loginId', 'admin@example.com');
224
- await page.fill('#password', 'supersecret');
225
- await page.click('[type=submit]');
226
- await page.waitForURL('**/admin/**');
227
- await page.close();
228
- },
229
- ```
230
-
231
- ### `beforeScreenshot`
232
-
233
- Called before each individual screenshot. Receives the Playwright `Page` (already navigated to the target URL) and a spec object `{ url, name, highlights }`. Use this to dismiss notifications, close modals, or set up per-page state.
234
-
235
- ```js
236
- beforeScreenshot: async (page, { url, name }) => {
237
- await page.evaluate(() => {
238
- document.querySelectorAll('.notification').forEach(el => el.remove());
239
- });
240
- },
241
- ```
242
-
243
- ### `afterAll`
244
-
245
- Called once after all screenshots are taken. Use for cleanup.
246
-
247
- ```js
248
- afterAll: async () => {
249
- console.log('done');
250
- },
251
- ```
package/README.md DELETED
@@ -1,141 +0,0 @@
1
- # astro-better-declarative-screenshots
2
-
3
- Declarative, Docker-backed screenshots for Astro documentation sites.
4
-
5
- Define which pages to screenshot and which elements to highlight directly in your MDX. The package handles starting Docker, capturing pages with Playwright WebKit, compositing a macOS Safari-style window chrome, and writing PNGs. A CI workflow detects visual regressions by diffing new captures against committed references.
6
-
7
- ## How it works
8
-
9
- 1. You declare screenshots in MDX using `<Screenshot>` and `<Highlight>` components.
10
- 2. Running `take-screenshots` scans your source files, boots Docker, opens each URL in Playwright WebKit, injects highlights, composites chrome, and writes PNGs to your output directory.
11
- 3. You commit the PNG files. The `<Screenshot>` component renders them as `<img>` tags at build time.
12
- 4. In CI, `check-screenshots` recaptures every page and diffs against the committed files. It exits non-zero if anything changed beyond the configured pixel threshold, and uploads diff images as build artifacts.
13
-
14
- ## Quick start
15
-
16
- ```sh
17
- npm install astro-better-declarative-screenshots
18
- npx playwright install --with-deps webkit
19
- ```
20
-
21
- Add the integration to `astro.config.mjs`:
22
-
23
- ```js
24
- import { defineConfig } from 'astro/config';
25
- import screenshots from 'astro-better-declarative-screenshots';
26
-
27
- export default defineConfig({
28
- integrations: [screenshots()],
29
- });
30
- ```
31
-
32
- Create `screenshot.config.mjs` at your project root (see [CONFIGURATION.md](./CONFIGURATION.md) for all options):
33
-
34
- ```js
35
- export default {
36
- baseUrl: 'http://localhost:9011',
37
- outputDir: 'public/screenshots',
38
- docker: {
39
- compose: 'docker-compose.yml',
40
- service: 'app',
41
- kickstart: 'kickstart/bootstrap.json',
42
- healthcheck: {
43
- url: 'http://localhost:9011/api/status',
44
- timeout: 60000,
45
- },
46
- },
47
- };
48
- ```
49
-
50
- Use `<Screenshot>` in your MDX files:
51
-
52
- ```mdx
53
- import Screenshot from 'astro-better-declarative-screenshots/Screenshot.astro';
54
- import Highlight from 'astro-better-declarative-screenshots/Highlight.astro';
55
-
56
- <Screenshot url="/admin/group/add">
57
- <Highlight selector="#name" style="border" color="#f60" label="Group name" />
58
- </Screenshot>
59
- ```
60
-
61
- Take screenshots:
62
-
63
- ```sh
64
- npx take-screenshots
65
- ```
66
-
67
- ## Components
68
-
69
- ### `<Screenshot>`
70
-
71
- | Prop | Type | Default | Description |
72
- |------|------|---------|-------------|
73
- | `url` | `string` | required | URL path (relative) or full URL to capture |
74
- | `id` | `string` | auto-derived | Explicit output filename (without extension) |
75
- | `alt` | `string` | derived from name | Alt text for the rendered image |
76
- | `width` | `number` | from config | Viewport width for this screenshot |
77
- | `height` | `number` | from config | Viewport height for this screenshot |
78
- | `fullPage` | `boolean` | `false` | Capture full scrollable page height |
79
-
80
- ### `<Highlight>`
81
-
82
- Must be a direct child of `<Screenshot>`.
83
-
84
- | Prop | Type | Default | Description |
85
- |------|------|---------|-------------|
86
- | `selector` | `string` | required | CSS selector for the element to highlight |
87
- | `style` | `'border' \| 'arrow' \| 'both'` | `'border'` | How to draw the highlight |
88
- | `color` | `string` | `'#f60'` | Highlight color (any CSS color) |
89
- | `label` | `string` | `''` | Text label drawn near the highlighted element |
90
- | `borderWidth` | `number` | `3` | Border thickness in pixels |
91
-
92
- ## Filename derivation
93
-
94
- Screenshot filenames are derived from the URL path and highlight selectors:
95
-
96
- - `/admin/group/add` with no highlights -> `admin-group-add.png`
97
- - `/admin/group` with `#name` highlighted -> `admin-group-name.png`
98
- - Same URL, same selectors, second occurrence -> `admin-group-1.png`
99
-
100
- To avoid collision issues, set an explicit `id` prop when the same URL appears multiple times with the same highlights.
101
-
102
- ## CLI reference
103
-
104
- ### `take-screenshots`
105
-
106
- Captures all screenshots and writes them to `outputDir`.
107
-
108
- ```sh
109
- npx take-screenshots [--filter <string>] [--strict]
110
- ```
111
-
112
- Options:
113
- - `--filter <string>` -- only capture screenshots whose name or URL contains this string
114
- - `--strict` -- exit non-zero if any screenshot fails to capture
115
-
116
- ### `check-screenshots`
117
-
118
- Captures all screenshots, diffs against committed references, and exits non-zero if anything changed.
119
-
120
- ```sh
121
- npx check-screenshots [--filter <string>] [--diff-dir <path>] [--threshold <ratio>] [--fail-on-missing]
122
- ```
123
-
124
- Options:
125
- - `--filter <string>` -- only check screenshots matching this string
126
- - `--diff-dir <path>` -- where to write diff images (default: `.screenshot-diffs`)
127
- - `--threshold <ratio>` -- fraction of pixels that may differ before failure (default: `0.001` = 0.1%)
128
- - `--fail-on-missing` -- exit non-zero if any reference file is missing
129
-
130
- ## Environment variables
131
-
132
- - `SCREENSHOTS_STRICT=true` -- strict mode: the `<Screenshot>` component throws at build time if the PNG is missing
133
- - `SCREENSHOTS_DIR` -- override the output directory (useful for CI artifact staging)
134
-
135
- ## CI setup
136
-
137
- See [`.github/workflows/check-screenshots.yml`](./.github/workflows/check-screenshots.yml) for an example weekly workflow that runs `check-screenshots` and uploads diff images as artifacts.
138
-
139
- ## License
140
-
141
- MIT
@@ -1,33 +0,0 @@
1
- ---
2
- title: Groups
3
- description: Learn how FusionAuth groups work.
4
- ---
5
-
6
- import Screenshot from 'astro-better-declarative-screenshots/Screenshot.astro';
7
- import Highlight from 'astro-better-declarative-screenshots/Highlight.astro';
8
-
9
- ## Creating a group
10
-
11
- Navigate to **Groups** in the FusionAuth Admin UI to manage your groups.
12
-
13
- {/* Basic screenshot -- no highlights */}
14
- <Screenshot url="/admin/group/" />
15
-
16
- {/* Screenshot with a highlighted field */}
17
- <Screenshot url="/admin/group/add">
18
- <Highlight selector="#name" style="border" color="#f60" label="Group name field" />
19
- </Screenshot>
20
-
21
- {/* Multiple highlights on one page */}
22
- <Screenshot url="/admin/group/add">
23
- <Highlight selector="#name" style="border" color="#f60" label="Name" />
24
- <Highlight selector="#description" style="arrow" color="#07c" label="Description" />
25
- </Screenshot>
26
-
27
- {/* Full-page capture with explicit window size */}
28
- <Screenshot
29
- url="/admin/group/members"
30
- width={1440}
31
- height={900}
32
- fullPage={true}
33
- />
@@ -1,72 +0,0 @@
1
- // example screenshot.config.mjs for the FusionAuth docs site
2
- // place at your Astro project root
3
-
4
- export default {
5
- // base URL of the running app (local or in Docker)
6
- baseUrl: 'http://localhost:9011',
7
-
8
- // where to write screenshots; relative to project root
9
- outputDir: 'public/screenshots',
10
-
11
- // default viewport
12
- window: { width: 1280, height: 800 },
13
-
14
- // Playwright browser engine
15
- browser: 'webkit',
16
-
17
- // macOS Safari-style chrome with drop shadow
18
- chrome: {
19
- style: 'safari-macos',
20
- showUrl: true,
21
- dark: false,
22
- shadowBlur: 40,
23
- shadowPadding: 48,
24
- },
25
-
26
- // docker setup -- start the app and wait for it to be healthy
27
- docker: {
28
- compose: 'screenshots/docker-compose.yml',
29
- service: 'fusionauth',
30
-
31
- // bootstrap seeds initial data. the file path is passed to docker-compose.yml
32
- // as SCREENSHOT_BOOTSTRAP_PATH and its content as SCREENSHOT_BOOTSTRAP_CONTENT.
33
- // wire those env vars into your container however it needs.
34
- // for FusionAuth: FUSIONAUTH_APP_KICKSTART_FILE: ${SCREENSHOT_BOOTSTRAP_PATH}
35
- // for other apps: mount the path as a volume or use the content as a seed script
36
- bootstrap: 'screenshots/kickstart/kickstart.json',
37
-
38
- healthcheck: {
39
- url: 'http://localhost:9011/api/status',
40
- timeout: 120000,
41
- interval: 3000,
42
- },
43
-
44
- // optional shell command to run after health check passes.
45
- // runs in the project root. use for migrations, extra seeding, etc.
46
- // postStart: 'node scripts/extra-seed.mjs',
47
-
48
- env: {
49
- DATABASE_PASSWORD: 'change-in-production',
50
- },
51
- },
52
-
53
- // called once before any screenshot is taken -- log in, set up app state, etc.
54
- beforeAll: async (context) => {
55
- const page = await context.newPage();
56
- await page.goto('http://localhost:9011/admin/login');
57
- await page.fill('#loginId', 'admin@example.com');
58
- await page.fill('#password', 'password');
59
- await page.click('[type=submit]');
60
- await page.waitForURL('**/admin/**');
61
- await page.close();
62
- },
63
-
64
- // called before each individual screenshot -- navigate or set up state
65
- beforeScreenshot: async (page, { url, name }) => {
66
- // page is already navigated to url by this point
67
- // close any open modals, dismiss notifications, etc.
68
- await page.evaluate(() => {
69
- document.querySelectorAll('.notification, .toast').forEach(el => el.remove());
70
- });
71
- },
72
- };