document360-capture 0.1.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/README.md +80 -0
- package/dist/annotate/compose.d.ts +7 -0
- package/dist/annotate/compose.js +65 -0
- package/dist/annotate/compose.js.map +1 -0
- package/dist/annotate/svg.d.ts +5 -0
- package/dist/annotate/svg.js +42 -0
- package/dist/annotate/svg.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.js +52 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/capture.d.ts +5 -0
- package/dist/commands/capture.js +77 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +88 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +97 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +33 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +24 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/authState.d.ts +10 -0
- package/dist/config/authState.js +27 -0
- package/dist/config/authState.js.map +1 -0
- package/dist/config/projectConfig.d.ts +27 -0
- package/dist/config/projectConfig.js +43 -0
- package/dist/config/projectConfig.js.map +1 -0
- package/dist/config/userConfig.d.ts +6 -0
- package/dist/config/userConfig.js +24 -0
- package/dist/config/userConfig.js.map +1 -0
- package/dist/helpers/dumpAnnotations.d.ts +3 -0
- package/dist/helpers/dumpAnnotations.js +39 -0
- package/dist/helpers/dumpAnnotations.js.map +1 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/types.d.ts +37 -0
- package/dist/helpers/types.js +2 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/helpers/waitPastLogin.d.ts +2 -0
- package/dist/helpers/waitPastLogin.js +7 -0
- package/dist/helpers/waitPastLogin.js.map +1 -0
- package/dist/runner/expiryDetect.d.ts +1 -0
- package/dist/runner/expiryDetect.js +19 -0
- package/dist/runner/expiryDetect.js.map +1 -0
- package/dist/runner/playwrightConfig.d.ts +8 -0
- package/dist/runner/playwrightConfig.js +33 -0
- package/dist/runner/playwrightConfig.js.map +1 -0
- package/dist/runner/runSpecs.d.ts +14 -0
- package/dist/runner/runSpecs.js +62 -0
- package/dist/runner/runSpecs.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +1 -0
- package/dist/scripts/postinstall.js +18 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saravana Kumar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# document360-capture
|
|
2
|
+
|
|
3
|
+
CLI that captures product screenshots from Playwright specs. Login once, capture many. Companion to [document360-writer](../writer/).
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
Doc-screenshot pipelines hit two walls in practice:
|
|
8
|
+
|
|
9
|
+
1. **MFA + per-run login.** Spinning up Chromium for every capture and asking the human to log in each time burns time and rules out repeatable runs.
|
|
10
|
+
2. **Annotation by hand.** Adding arrows, numbered callouts, and redactions in an image editor is the slowest part of doc maintenance.
|
|
11
|
+
|
|
12
|
+
This CLI solves both: `d360-capture auth` opens Chromium once, the human logs in (MFA at their own pace), and a Playwright `storageState` is saved. Every `d360-capture capture` after that reuses the session. Specs declare what to highlight/redact/annotate; the CLI composites SVG overlays via `sharp` after the raw capture lands.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g document360-capture
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Postinstall downloads the Chromium binary via `npx playwright install chromium`.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd <your-repo>
|
|
26
|
+
d360-capture init # one-time per project — writes .d360-capture.json
|
|
27
|
+
d360-capture auth --env staging # one-time per machine — log in, press Enter when done
|
|
28
|
+
d360-capture capture # run every spec in captureDir
|
|
29
|
+
d360-capture capture <spec-id> # run one spec
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
| Command | Description |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `d360-capture init` | Interactive setup. Writes `.d360-capture.json` at repo root. |
|
|
37
|
+
| `d360-capture auth [--env <name>]` | Open Chromium; log in at your own pace; press Enter to save session. |
|
|
38
|
+
| `d360-capture capture [<spec-id>] [--env <name>] [--no-annotate]` | Run one or all specs. Default-on annotation overlay. |
|
|
39
|
+
| `d360-capture list` | List discovered specs and last-capture times. |
|
|
40
|
+
| `d360-capture doctor` | Validate config, auth-state, browser, and capture dir. |
|
|
41
|
+
| `d360-capture status` | Print resolved config (secrets redacted). |
|
|
42
|
+
|
|
43
|
+
## Spec helpers
|
|
44
|
+
|
|
45
|
+
Specs in your `captureDir` import three helpers:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { test } from '@playwright/test';
|
|
49
|
+
import { waitPastLogin, dumpAnnotations, type Placeholder } from 'document360-capture/helpers';
|
|
50
|
+
|
|
51
|
+
const PLACEHOLDER: Placeholder = {
|
|
52
|
+
id: 'settings-variables-add-modal',
|
|
53
|
+
saveTo: 'user-docs/_screenshots/settings-variables-add-modal.raw.png',
|
|
54
|
+
highlight: ['button:has-text("+ Add")'],
|
|
55
|
+
annotations: [{ target: 'input[name="key"]', label: '1', text: 'Use proj.* prefix' }],
|
|
56
|
+
redact: ['[data-testid="topbar-user-email"]'],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
test(PLACEHOLDER.id, async ({ page }) => {
|
|
60
|
+
await page.goto(process.env.CAPTURE_START_URL!);
|
|
61
|
+
await waitPastLogin(page, new RegExp(process.env.CAPTURE_AUTH_BOUNDARY!));
|
|
62
|
+
|
|
63
|
+
await page.click('[aria-label="Open Settings"]');
|
|
64
|
+
await page.click('a:has-text("Variables")');
|
|
65
|
+
await page.click('button:has-text("+ Add")');
|
|
66
|
+
await page.waitForSelector('[role="dialog"]', { state: 'visible' });
|
|
67
|
+
|
|
68
|
+
await page.locator('[role="dialog"]').first().screenshot({ path: PLACEHOLDER.saveTo });
|
|
69
|
+
await dumpAnnotations(page, PLACEHOLDER);
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Config files
|
|
74
|
+
|
|
75
|
+
- **`<repo>/.d360-capture.json`** (committed): paths, viewport, environments (startUrl + authBoundaryUrlPattern per env), annotation toggle.
|
|
76
|
+
- **`~/.document360-capture/auth-states/<projectId>/<env>.json`** (per-machine, never commit): Playwright `storageState`.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, existsSync, copyFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import { highlightSvg, redactSvg, annotationSvg } from './svg.js';
|
|
5
|
+
async function annotateOne(jsonPath) {
|
|
6
|
+
const data = JSON.parse(readFileSync(jsonPath, 'utf8'));
|
|
7
|
+
if (!existsSync(data.rawPath))
|
|
8
|
+
return { id: data.id, ok: false, reason: `raw missing: ${data.rawPath}` };
|
|
9
|
+
const meta = await sharp(data.rawPath).metadata();
|
|
10
|
+
const width = meta.width ?? 1440;
|
|
11
|
+
const height = meta.height ?? 900;
|
|
12
|
+
const parts = [];
|
|
13
|
+
for (const h of data.highlight)
|
|
14
|
+
parts.push(highlightSvg(h.bbox));
|
|
15
|
+
for (const r of data.redact)
|
|
16
|
+
parts.push(redactSvg(r.bbox));
|
|
17
|
+
for (const a of data.annotations)
|
|
18
|
+
parts.push(annotationSvg(a.label, a.text, a.bbox, width));
|
|
19
|
+
if (parts.length === 0) {
|
|
20
|
+
await sharp(data.rawPath).toFile(data.finalPath);
|
|
21
|
+
return { id: data.id, ok: true, finalPath: data.finalPath };
|
|
22
|
+
}
|
|
23
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">${parts.join('')}</svg>`;
|
|
24
|
+
await sharp(data.rawPath)
|
|
25
|
+
.composite([{ input: Buffer.from(svg), top: 0, left: 0 }])
|
|
26
|
+
.toFile(data.finalPath);
|
|
27
|
+
return { id: data.id, ok: true, finalPath: data.finalPath };
|
|
28
|
+
}
|
|
29
|
+
async function copyRawAsFinal(outputDir) {
|
|
30
|
+
const files = readdirSync(outputDir).filter(f => f.endsWith('.raw.png'));
|
|
31
|
+
const results = [];
|
|
32
|
+
for (const f of files) {
|
|
33
|
+
const rawPath = join(outputDir, f);
|
|
34
|
+
const finalPath = rawPath.replace(/\.raw\.png$/, '.png');
|
|
35
|
+
try {
|
|
36
|
+
copyFileSync(rawPath, finalPath);
|
|
37
|
+
results.push({ id: f.replace(/\.raw\.png$/, ''), ok: true, finalPath });
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
results.push({ id: f, ok: false, reason: String(err) });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
export async function composeAnnotations(outputDir, enabled) {
|
|
46
|
+
if (!existsSync(outputDir))
|
|
47
|
+
return [];
|
|
48
|
+
if (!enabled)
|
|
49
|
+
return copyRawAsFinal(outputDir);
|
|
50
|
+
const files = readdirSync(outputDir).filter(f => f.endsWith('.annotations.json'));
|
|
51
|
+
if (files.length === 0)
|
|
52
|
+
return copyRawAsFinal(outputDir);
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const f of files) {
|
|
55
|
+
const jsonPath = join(outputDir, f);
|
|
56
|
+
try {
|
|
57
|
+
results.push(await annotateOne(jsonPath));
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
results.push({ id: f, ok: false, reason: String(err) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/annotate/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Bbox } from '../helpers/types.js';
|
|
2
|
+
export declare function escapeXml(s: string): string;
|
|
3
|
+
export declare function highlightSvg(bbox: Bbox): string;
|
|
4
|
+
export declare function redactSvg(bbox: Bbox): string;
|
|
5
|
+
export declare function annotationSvg(label: string, text: string, bbox: Bbox, canvasW: number): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function escapeXml(s) {
|
|
2
|
+
return s.replace(/[<>&"']/g, c => ({ '<': '<', '>': '>', '&': '&', '"': '"', "'": ''' }[c]));
|
|
3
|
+
}
|
|
4
|
+
export function highlightSvg(bbox) {
|
|
5
|
+
const pad = 4;
|
|
6
|
+
const x = Math.max(0, bbox.x - pad);
|
|
7
|
+
const y = Math.max(0, bbox.y - pad);
|
|
8
|
+
const w = bbox.width + pad * 2;
|
|
9
|
+
const h = bbox.height + pad * 2;
|
|
10
|
+
return `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="6" ry="6" fill="rgba(255,213,0,0.18)" stroke="#f5a623" stroke-width="2.5"/>`;
|
|
11
|
+
}
|
|
12
|
+
export function redactSvg(bbox) {
|
|
13
|
+
return `<rect x="${bbox.x}" y="${bbox.y}" width="${bbox.width}" height="${bbox.height}" fill="#1f2328"/>`;
|
|
14
|
+
}
|
|
15
|
+
export function annotationSvg(label, text, bbox, canvasW) {
|
|
16
|
+
const r = 14;
|
|
17
|
+
const cx = bbox.x + bbox.width + r + 4;
|
|
18
|
+
const cy = bbox.y + bbox.height / 2;
|
|
19
|
+
const textPad = 8;
|
|
20
|
+
const fontSize = 13;
|
|
21
|
+
const approxCharWidth = 7.2;
|
|
22
|
+
const textWidth = text.length * approxCharWidth + textPad * 2;
|
|
23
|
+
const textHeight = fontSize + textPad * 2;
|
|
24
|
+
let textX = cx + r + 6;
|
|
25
|
+
let textY = cy - textHeight / 2;
|
|
26
|
+
if (textX + textWidth > canvasW) {
|
|
27
|
+
textX = bbox.x - textWidth - r - 6;
|
|
28
|
+
if (textX < 0) {
|
|
29
|
+
textX = bbox.x;
|
|
30
|
+
textY = bbox.y - textHeight - 6;
|
|
31
|
+
if (textY < 0)
|
|
32
|
+
textY = bbox.y + bbox.height + 6;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
`<circle cx="${cx}" cy="${cy}" r="${r}" fill="#d1242f" stroke="#fff" stroke-width="2"/>`,
|
|
37
|
+
`<text x="${cx}" y="${cy + 5}" font-family="-apple-system,Segoe UI,sans-serif" font-size="14" font-weight="700" fill="#fff" text-anchor="middle">${escapeXml(label)}</text>`,
|
|
38
|
+
`<rect x="${textX}" y="${textY}" width="${textWidth}" height="${textHeight}" rx="4" ry="4" fill="#1f2328" stroke="#fff" stroke-width="1.5"/>`,
|
|
39
|
+
`<text x="${textX + textPad}" y="${textY + textPad + fontSize - 2}" font-family="-apple-system,Segoe UI,sans-serif" font-size="${fontSize}" fill="#fff">${escapeXml(text)}</text>`,
|
|
40
|
+
].join('');
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=svg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svg.js","sourceRoot":"","sources":["../../src/annotate/svg.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { initCommand } from './commands/init.js';
|
|
5
|
+
import { authCommand } from './commands/auth.js';
|
|
6
|
+
import { captureCommand } from './commands/capture.js';
|
|
7
|
+
import { listCommand } from './commands/list.js';
|
|
8
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
9
|
+
import { statusCommand } from './commands/status.js';
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const pkg = require('../package.json');
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name('d360-capture')
|
|
15
|
+
.description('Capture product screenshots from Playwright specs. Login once, capture many.')
|
|
16
|
+
.version(pkg.version);
|
|
17
|
+
program
|
|
18
|
+
.command('init')
|
|
19
|
+
.description('Interactive setup. Writes .d360-capture.json at repo root.')
|
|
20
|
+
.action(async () => {
|
|
21
|
+
await initCommand();
|
|
22
|
+
});
|
|
23
|
+
program
|
|
24
|
+
.command('auth')
|
|
25
|
+
.description('Open Chromium for interactive login. Press ENTER when done to save the session.')
|
|
26
|
+
.option('-e, --env <name>', 'Environment name (default: defaultEnvironment from config)')
|
|
27
|
+
.action(async (opts) => {
|
|
28
|
+
await authCommand(opts);
|
|
29
|
+
});
|
|
30
|
+
program
|
|
31
|
+
.command('capture [spec-id]')
|
|
32
|
+
.description('Run one spec by id, or all specs if omitted.')
|
|
33
|
+
.option('-e, --env <name>', 'Environment name (default: defaultEnvironment from config)')
|
|
34
|
+
.option('--no-annotate', 'Skip annotation overlay; emit raw screenshots only')
|
|
35
|
+
.action(async (specId, opts) => {
|
|
36
|
+
const exit = await captureCommand(specId, opts);
|
|
37
|
+
if (exit !== 0)
|
|
38
|
+
process.exit(exit);
|
|
39
|
+
});
|
|
40
|
+
program
|
|
41
|
+
.command('list')
|
|
42
|
+
.description('List discovered specs and their last-capture times.')
|
|
43
|
+
.action(async () => {
|
|
44
|
+
await listCommand();
|
|
45
|
+
});
|
|
46
|
+
program
|
|
47
|
+
.command('doctor')
|
|
48
|
+
.description('Validate config, auth-state, and browser install.')
|
|
49
|
+
.option('-e, --env <name>', 'Environment to check')
|
|
50
|
+
.action(async (opts) => {
|
|
51
|
+
const exit = await doctorCommand(opts);
|
|
52
|
+
if (exit !== 0)
|
|
53
|
+
process.exit(exit);
|
|
54
|
+
});
|
|
55
|
+
program
|
|
56
|
+
.command('status')
|
|
57
|
+
.description('Print resolved config and auth-state freshness.')
|
|
58
|
+
.action(async () => {
|
|
59
|
+
await statusCommand();
|
|
60
|
+
});
|
|
61
|
+
program.parseAsync(process.argv).catch(err => {
|
|
62
|
+
console.error('');
|
|
63
|
+
console.error(`✗ ${err.message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
import { findProjectConfigPath, readProjectConfig, resolveEnv, } from '../config/projectConfig.js';
|
|
3
|
+
import { authStatePath, ensureAuthStateDir } from '../config/authState.js';
|
|
4
|
+
export async function authCommand(opts) {
|
|
5
|
+
const cfg = readProjectConfig(findProjectConfigPath());
|
|
6
|
+
const { name: envName, env } = resolveEnv(cfg, opts.env);
|
|
7
|
+
ensureAuthStateDir(cfg.projectId);
|
|
8
|
+
const savePath = authStatePath(cfg.projectId, envName);
|
|
9
|
+
console.log('');
|
|
10
|
+
console.log(`Opening Chromium against ${env.startUrl}`);
|
|
11
|
+
console.log(`Auth-state will save to: ${savePath}`);
|
|
12
|
+
console.log('');
|
|
13
|
+
const browser = await chromium.launch({
|
|
14
|
+
headless: false,
|
|
15
|
+
args: ['--start-maximized'],
|
|
16
|
+
});
|
|
17
|
+
const context = await browser.newContext({ viewport: null });
|
|
18
|
+
const page = await context.newPage();
|
|
19
|
+
await page.goto(env.startUrl, { waitUntil: 'domcontentloaded' });
|
|
20
|
+
console.log('────────────────────────────────────────────────────────────');
|
|
21
|
+
console.log(' Log in fully (MFA included) at your own pace.');
|
|
22
|
+
console.log(' When you reach the post-login app, come back to this');
|
|
23
|
+
console.log(' terminal and press ENTER to save the session.');
|
|
24
|
+
console.log('────────────────────────────────────────────────────────────');
|
|
25
|
+
console.log('');
|
|
26
|
+
const safetyInterval = setInterval(() => {
|
|
27
|
+
void context.storageState({ path: savePath }).catch(() => { });
|
|
28
|
+
}, 30_000);
|
|
29
|
+
try {
|
|
30
|
+
await waitForEnter();
|
|
31
|
+
await context.storageState({ path: savePath });
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(`✓ Session saved to ${savePath}`);
|
|
34
|
+
console.log(' This file contains session cookies — never commit it to git.');
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
clearInterval(safetyInterval);
|
|
38
|
+
await browser.close().catch(() => { });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function waitForEnter() {
|
|
42
|
+
return new Promise(resolve => {
|
|
43
|
+
const onData = () => {
|
|
44
|
+
process.stdin.off('data', onData);
|
|
45
|
+
process.stdin.pause();
|
|
46
|
+
resolve();
|
|
47
|
+
};
|
|
48
|
+
process.stdin.resume();
|
|
49
|
+
process.stdin.once('data', onData);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { findProjectConfigPath, readProjectConfig, resolveEnv, } from '../config/projectConfig.js';
|
|
4
|
+
import { authStatePath, getAuthStateInfo } from '../config/authState.js';
|
|
5
|
+
import { writeRuntimePlaywrightConfig } from '../runner/playwrightConfig.js';
|
|
6
|
+
import { runSpecs } from '../runner/runSpecs.js';
|
|
7
|
+
import { looksLikeSessionExpiry } from '../runner/expiryDetect.js';
|
|
8
|
+
import { composeAnnotations } from '../annotate/compose.js';
|
|
9
|
+
export async function captureCommand(specId, opts) {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const cfg = readProjectConfig(findProjectConfigPath(cwd));
|
|
12
|
+
const { name: envName, env } = resolveEnv(cfg, opts.env);
|
|
13
|
+
const info = getAuthStateInfo(cfg.projectId, envName);
|
|
14
|
+
if (!info.exists) {
|
|
15
|
+
console.error('');
|
|
16
|
+
console.error(`✗ No saved session for env "${envName}".`);
|
|
17
|
+
console.error(` Expected at: ${info.path}`);
|
|
18
|
+
console.error(` Run: d360-capture auth --env ${envName}`);
|
|
19
|
+
return 2;
|
|
20
|
+
}
|
|
21
|
+
const captureDirAbs = isAbsolute(cfg.captureDir) ? cfg.captureDir : resolve(cwd, cfg.captureDir);
|
|
22
|
+
if (!existsSync(captureDirAbs)) {
|
|
23
|
+
console.error(`✗ Capture directory missing: ${captureDirAbs}`);
|
|
24
|
+
console.error(' Generate at least one spec there before running capture.');
|
|
25
|
+
return 2;
|
|
26
|
+
}
|
|
27
|
+
const configPath = writeRuntimePlaywrightConfig({
|
|
28
|
+
cwd,
|
|
29
|
+
cfg,
|
|
30
|
+
envName,
|
|
31
|
+
storageStatePath: authStatePath(cfg.projectId, envName),
|
|
32
|
+
});
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log(`▶ Running specs against ${envName} (${env.startUrl})`);
|
|
35
|
+
if (specId)
|
|
36
|
+
console.log(` Single spec: ${specId}`);
|
|
37
|
+
else
|
|
38
|
+
console.log(` All specs in ${cfg.captureDir}`);
|
|
39
|
+
console.log('');
|
|
40
|
+
const result = await runSpecs({
|
|
41
|
+
cwd,
|
|
42
|
+
configPath,
|
|
43
|
+
startUrl: env.startUrl,
|
|
44
|
+
authBoundaryUrlPattern: env.authBoundaryUrlPattern,
|
|
45
|
+
specId,
|
|
46
|
+
captureDirAbs,
|
|
47
|
+
});
|
|
48
|
+
if (result.exitCode !== 0) {
|
|
49
|
+
const combined = result.stdout + '\n' + result.stderr;
|
|
50
|
+
if (looksLikeSessionExpiry(combined)) {
|
|
51
|
+
console.error('');
|
|
52
|
+
console.error(`✗ Session expired or invalid for env "${envName}".`);
|
|
53
|
+
console.error(` Run: d360-capture auth --env ${envName}`);
|
|
54
|
+
console.error(' Then re-run d360-capture capture.');
|
|
55
|
+
return 2;
|
|
56
|
+
}
|
|
57
|
+
console.error('');
|
|
58
|
+
console.error(`✗ Playwright exited with code ${result.exitCode}`);
|
|
59
|
+
return result.exitCode;
|
|
60
|
+
}
|
|
61
|
+
const annotateEnabled = !opts.noAnnotate && cfg.annotation.enabled;
|
|
62
|
+
const outputDirAbs = isAbsolute(cfg.outputDir) ? cfg.outputDir : resolve(cwd, cfg.outputDir);
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(annotateEnabled ? '▶ Annotating screenshots...' : '▶ Skipping annotation (--no-annotate or config off)');
|
|
65
|
+
const annotated = await composeAnnotations(outputDirAbs, annotateEnabled);
|
|
66
|
+
const ok = annotated.filter(r => r.ok).length;
|
|
67
|
+
const failed = annotated.filter(r => !r.ok);
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(`✓ ${ok} screenshot${ok === 1 ? '' : 's'} ready in ${cfg.outputDir}`);
|
|
70
|
+
if (failed.length > 0) {
|
|
71
|
+
console.log(`✗ ${failed.length} annotation failure${failed.length === 1 ? '' : 's'}:`);
|
|
72
|
+
for (const f of failed)
|
|
73
|
+
console.log(` - ${f.id}: ${f.reason}`);
|
|
74
|
+
}
|
|
75
|
+
return failed.length === 0 ? 0 : 1;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/commands/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { findProjectConfigPath, readProjectConfig, resolveEnv, } from '../config/projectConfig.js';
|
|
5
|
+
import { getAuthStateInfo } from '../config/authState.js';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
export async function doctorCommand(opts) {
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
let failures = 0;
|
|
10
|
+
const ok = (msg) => console.log(` ✓ ${msg}`);
|
|
11
|
+
const fail = (msg) => {
|
|
12
|
+
console.log(` ✗ ${msg}`);
|
|
13
|
+
failures++;
|
|
14
|
+
};
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log('Checking document360-capture setup...');
|
|
17
|
+
console.log('');
|
|
18
|
+
const cfgPath = findProjectConfigPath(cwd);
|
|
19
|
+
if (!existsSync(cfgPath)) {
|
|
20
|
+
fail(`Project config missing at ${cfgPath} — run \`d360-capture init\``);
|
|
21
|
+
return 2;
|
|
22
|
+
}
|
|
23
|
+
ok(`Project config: ${cfgPath}`);
|
|
24
|
+
let cfg;
|
|
25
|
+
try {
|
|
26
|
+
cfg = readProjectConfig(cfgPath);
|
|
27
|
+
ok(`Config parses; projectId="${cfg.projectId}"`);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
fail(`Config invalid: ${err.message}`);
|
|
31
|
+
return 2;
|
|
32
|
+
}
|
|
33
|
+
const captureDirAbs = isAbsolute(cfg.captureDir) ? cfg.captureDir : resolve(cwd, cfg.captureDir);
|
|
34
|
+
if (existsSync(captureDirAbs))
|
|
35
|
+
ok(`Capture dir exists: ${cfg.captureDir}`);
|
|
36
|
+
else
|
|
37
|
+
fail(`Capture dir missing: ${cfg.captureDir}`);
|
|
38
|
+
let env;
|
|
39
|
+
try {
|
|
40
|
+
env = resolveEnv(cfg, opts.env);
|
|
41
|
+
ok(`Environment "${env.name}" resolved (startUrl=${env.env.startUrl})`);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
fail(`Environment resolve failed: ${err.message}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const info = getAuthStateInfo(cfg.projectId, env.name);
|
|
48
|
+
if (info.exists) {
|
|
49
|
+
const ageDays = info.ageMs != null ? Math.floor(info.ageMs / 86_400_000) : null;
|
|
50
|
+
ok(`Auth-state present (${ageDays}d old): ${info.path}`);
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(readFileSync(info.path, 'utf8'));
|
|
53
|
+
if (parsed && typeof parsed === 'object')
|
|
54
|
+
ok('Auth-state parses as JSON');
|
|
55
|
+
else
|
|
56
|
+
fail('Auth-state is not a valid object');
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
fail(`Auth-state unreadable: ${err.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
fail(`Auth-state missing for "${env.name}" — run \`d360-capture auth --env ${env.name}\``);
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const playwrightPkg = require.resolve('playwright/package.json');
|
|
67
|
+
const pkg = JSON.parse(readFileSync(playwrightPkg, 'utf8'));
|
|
68
|
+
ok(`playwright@${pkg.version} installed`);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
fail('playwright not resolvable — reinstall document360-capture');
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
require.resolve('@playwright/test/cli.js');
|
|
75
|
+
ok('@playwright/test CLI resolvable');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
fail('@playwright/test CLI not resolvable — reinstall document360-capture');
|
|
79
|
+
}
|
|
80
|
+
console.log('');
|
|
81
|
+
if (failures === 0)
|
|
82
|
+
console.log('All checks passed.');
|
|
83
|
+
else
|
|
84
|
+
console.log(`${failures} check${failures === 1 ? '' : 's'} failed.`);
|
|
85
|
+
console.log('');
|
|
86
|
+
return failures === 0 ? 0 : 1;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCommand(): Promise<void>;
|