@reshotdev/screenshot 0.0.1-beta.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 +190 -0
- package/README.md +388 -0
- package/package.json +64 -0
- package/src/commands/auth.js +259 -0
- package/src/commands/chrome.js +140 -0
- package/src/commands/ci-run.js +123 -0
- package/src/commands/ci-setup.js +288 -0
- package/src/commands/drifts.js +423 -0
- package/src/commands/import-tests.js +309 -0
- package/src/commands/ingest.js +458 -0
- package/src/commands/init.js +633 -0
- package/src/commands/publish.js +1721 -0
- package/src/commands/pull.js +303 -0
- package/src/commands/record.js +94 -0
- package/src/commands/run.js +476 -0
- package/src/commands/setup-wizard.js +740 -0
- package/src/commands/setup.js +137 -0
- package/src/commands/status.js +275 -0
- package/src/commands/sync.js +621 -0
- package/src/commands/ui.js +248 -0
- package/src/commands/validate-docs.js +529 -0
- package/src/index.js +462 -0
- package/src/lib/api-client.js +815 -0
- package/src/lib/capture-engine.js +1623 -0
- package/src/lib/capture-script-runner.js +3120 -0
- package/src/lib/ci-detect.js +137 -0
- package/src/lib/config.js +1240 -0
- package/src/lib/diff-engine.js +642 -0
- package/src/lib/hash.js +74 -0
- package/src/lib/image-crop.js +396 -0
- package/src/lib/matrix.js +89 -0
- package/src/lib/output-path-template.js +318 -0
- package/src/lib/playwright-runner.js +252 -0
- package/src/lib/polished-clip.js +553 -0
- package/src/lib/privacy-engine.js +408 -0
- package/src/lib/progress-tracker.js +142 -0
- package/src/lib/record-browser-injection.js +654 -0
- package/src/lib/record-cdp.js +612 -0
- package/src/lib/record-clip.js +343 -0
- package/src/lib/record-config.js +623 -0
- package/src/lib/record-screenshot.js +360 -0
- package/src/lib/record-terminal.js +123 -0
- package/src/lib/recorder-service.js +781 -0
- package/src/lib/secrets.js +51 -0
- package/src/lib/selector-strategies.js +859 -0
- package/src/lib/standalone-mode.js +400 -0
- package/src/lib/storage-providers.js +569 -0
- package/src/lib/style-engine.js +684 -0
- package/src/lib/ui-api.js +4677 -0
- package/src/lib/ui-assets.js +373 -0
- package/src/lib/ui-executor.js +587 -0
- package/src/lib/variant-injector.js +591 -0
- package/src/lib/viewport-presets.js +454 -0
- package/src/lib/worker-pool.js +118 -0
- package/web/cropper/index.html +436 -0
- package/web/manager/dist/assets/index--ZgioErz.js +507 -0
- package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
- package/web/manager/dist/index.html +27 -0
- package/web/subtitle-editor/index.html +295 -0
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
// setup-wizard.js - Comprehensive interactive setup wizard
|
|
2
|
+
// Consolidates the initial setup flow: auth + init + feature configuration
|
|
3
|
+
// Individual commands (auth, init) still exist for power users
|
|
4
|
+
|
|
5
|
+
const inquirer = require("inquirer");
|
|
6
|
+
const chalk = require("chalk");
|
|
7
|
+
const fs = require("fs-extra");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const config = require("../lib/config");
|
|
10
|
+
const {
|
|
11
|
+
validateStorageConfig,
|
|
12
|
+
getStorageSetupHelp,
|
|
13
|
+
} = require("../lib/storage-providers");
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Auto-detect documentation directories
|
|
17
|
+
*/
|
|
18
|
+
function detectDocumentationRoot() {
|
|
19
|
+
const commonPaths = [
|
|
20
|
+
"docs",
|
|
21
|
+
"documentation",
|
|
22
|
+
"content",
|
|
23
|
+
"doc",
|
|
24
|
+
"guides",
|
|
25
|
+
".vitepress/content",
|
|
26
|
+
".docusaurus/docs",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const dir of commonPaths) {
|
|
30
|
+
const fullPath = path.join(process.cwd(), dir);
|
|
31
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
32
|
+
const files = fs.readdirSync(fullPath);
|
|
33
|
+
if (files.some((f) => f.endsWith(".md") || f.endsWith(".mdx"))) {
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Detect if this is a Git repository and if it's GitHub
|
|
43
|
+
*/
|
|
44
|
+
function detectGitInfo() {
|
|
45
|
+
const isGitRepo = fs.existsSync(path.join(process.cwd(), ".git"));
|
|
46
|
+
let isGitHub = false;
|
|
47
|
+
let remoteUrl = null;
|
|
48
|
+
|
|
49
|
+
if (isGitRepo) {
|
|
50
|
+
try {
|
|
51
|
+
const { execSync } = require("child_process");
|
|
52
|
+
remoteUrl = execSync("git remote get-url origin", {
|
|
53
|
+
encoding: "utf-8",
|
|
54
|
+
}).trim();
|
|
55
|
+
isGitHub = remoteUrl.includes("github.com");
|
|
56
|
+
} catch {
|
|
57
|
+
// No remote configured
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { isGitRepo, isGitHub, remoteUrl };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Detect Playwright configuration
|
|
66
|
+
*/
|
|
67
|
+
function detectPlaywright() {
|
|
68
|
+
const configFiles = [
|
|
69
|
+
"playwright.config.ts",
|
|
70
|
+
"playwright.config.js",
|
|
71
|
+
"playwright.config.mjs",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (const file of configFiles) {
|
|
75
|
+
if (fs.existsSync(path.join(process.cwd(), file))) {
|
|
76
|
+
return { hasPlaywright: true, configFile: file };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check package.json for playwright dependency
|
|
81
|
+
try {
|
|
82
|
+
const pkg = fs.readJsonSync(path.join(process.cwd(), "package.json"));
|
|
83
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
84
|
+
if (deps["@playwright/test"] || deps["playwright"]) {
|
|
85
|
+
return { hasPlaywright: true, configFile: null };
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
// No package.json
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { hasPlaywright: false, configFile: null };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generate documentation block configuration
|
|
96
|
+
*/
|
|
97
|
+
function generateDocumentationConfig(root = "./docs", strategy = "git_pr") {
|
|
98
|
+
return {
|
|
99
|
+
root,
|
|
100
|
+
include: ["**/*.md", "**/*.mdx"],
|
|
101
|
+
exclude: ["**/_*.mdx", "**/node_modules/**", "**/.next/**"],
|
|
102
|
+
strategy,
|
|
103
|
+
assetFormat: "markdown",
|
|
104
|
+
mappings: {},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Scaffold a basic documentation structure
|
|
110
|
+
*/
|
|
111
|
+
async function scaffoldDocumentation(template = "basic") {
|
|
112
|
+
const docsDir = path.join(process.cwd(), "docs");
|
|
113
|
+
fs.ensureDirSync(docsDir);
|
|
114
|
+
|
|
115
|
+
const readmeContent = `# Documentation
|
|
116
|
+
|
|
117
|
+
This directory contains documentation for your project.
|
|
118
|
+
|
|
119
|
+
## Linking Docs to Visual Journeys
|
|
120
|
+
|
|
121
|
+
Add \`reshot_journey\` frontmatter to your markdown files to link them to Playwright test captures:
|
|
122
|
+
|
|
123
|
+
\`\`\`markdown
|
|
124
|
+
---
|
|
125
|
+
title: "Feature Name"
|
|
126
|
+
reshot_journey: "feature/workflow-name"
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
# Feature Documentation
|
|
130
|
+
|
|
131
|
+
Your content here...
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
The journey key should match your Playwright test structure. For example:
|
|
135
|
+
- Test file: \`tests/01-auth-flows.spec.ts\` with "Login Flow" describe block
|
|
136
|
+
- Journey key: \`auth-flows/login-flow\`
|
|
137
|
+
|
|
138
|
+
## Commands
|
|
139
|
+
|
|
140
|
+
- \`reshot import-tests\` - Import existing Playwright tests
|
|
141
|
+
- \`reshot sync\` - Sync visuals and documentation
|
|
142
|
+
- \`reshot status\` - View sync status and drifts
|
|
143
|
+
- \`reshot validate\` - Validate configuration
|
|
144
|
+
- \`reshot studio\` - Open visual management UI
|
|
145
|
+
|
|
146
|
+
For more information, visit: https://docs.reshot.dev
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
fs.writeFileSync(path.join(docsDir, "README.md"), readmeContent);
|
|
150
|
+
|
|
151
|
+
const exampleContent = `---
|
|
152
|
+
title: "Getting Started"
|
|
153
|
+
reshot_journey: "onboarding/first-steps"
|
|
154
|
+
description: "Quick start guide for new users"
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
# Getting Started
|
|
158
|
+
|
|
159
|
+
Welcome! This is an example documentation file linked to the \`onboarding/first-steps\` journey.
|
|
160
|
+
|
|
161
|
+
## Overview
|
|
162
|
+
|
|
163
|
+
When Reshot detects UI changes in your Playwright tests, it will automatically propose updates to this document with new screenshots and descriptions.
|
|
164
|
+
|
|
165
|
+
## Steps
|
|
166
|
+
|
|
167
|
+
1. **Step 1**: Description of first step
|
|
168
|
+
|
|
169
|
+
<!-- Reshot will inject screenshots here when visual drift is detected -->
|
|
170
|
+
|
|
171
|
+
2. **Step 2**: Description of second step
|
|
172
|
+
|
|
173
|
+
3. **Step 3**: Description of third step
|
|
174
|
+
|
|
175
|
+
## Next Steps
|
|
176
|
+
|
|
177
|
+
- Link more documents to journeys
|
|
178
|
+
- Run \`reshot sync\` to upload traces
|
|
179
|
+
- Check \`reshot status\` for drift notifications
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
fs.writeFileSync(path.join(docsDir, "getting-started.md"), exampleContent);
|
|
183
|
+
return "docs";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if a documentation framework is already set up
|
|
188
|
+
*/
|
|
189
|
+
function detectDocumentationFramework() {
|
|
190
|
+
const frameworks = [
|
|
191
|
+
{
|
|
192
|
+
name: "Astro/Starlight",
|
|
193
|
+
files: ["astro.config.mjs", "astro.config.ts"],
|
|
194
|
+
deps: ["@astrojs/starlight"],
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "Docusaurus",
|
|
198
|
+
files: ["docusaurus.config.js", "docusaurus.config.ts"],
|
|
199
|
+
deps: ["@docusaurus/core"],
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "VitePress",
|
|
203
|
+
files: [".vitepress/config.js", ".vitepress/config.ts"],
|
|
204
|
+
deps: ["vitepress"],
|
|
205
|
+
},
|
|
206
|
+
{ name: "Mintlify", files: ["mint.json"], deps: [] },
|
|
207
|
+
{ name: "GitBook", files: [".gitbook.yaml"], deps: [] },
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
// Check for config files
|
|
211
|
+
for (const fw of frameworks) {
|
|
212
|
+
for (const file of fw.files) {
|
|
213
|
+
if (fs.existsSync(path.join(process.cwd(), file))) {
|
|
214
|
+
return { framework: fw.name, detected: true };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check package.json for deps
|
|
220
|
+
try {
|
|
221
|
+
const pkg = fs.readJsonSync(path.join(process.cwd(), "package.json"));
|
|
222
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
223
|
+
|
|
224
|
+
for (const fw of frameworks) {
|
|
225
|
+
for (const dep of fw.deps) {
|
|
226
|
+
if (allDeps[dep]) {
|
|
227
|
+
return { framework: fw.name, detected: true };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
// No package.json
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { framework: null, detected: false };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Count documentation files in a directory
|
|
240
|
+
*/
|
|
241
|
+
function countDocFiles(dir) {
|
|
242
|
+
if (!fs.existsSync(dir)) return 0;
|
|
243
|
+
|
|
244
|
+
let count = 0;
|
|
245
|
+
function walk(d) {
|
|
246
|
+
for (const item of fs.readdirSync(d)) {
|
|
247
|
+
const full = path.join(d, item);
|
|
248
|
+
if (fs.statSync(full).isDirectory()) {
|
|
249
|
+
if (!item.startsWith(".") && item !== "node_modules") walk(full);
|
|
250
|
+
} else if (item.endsWith(".md") || item.endsWith(".mdx")) {
|
|
251
|
+
count++;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
walk(dir);
|
|
256
|
+
return count;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Main setup wizard
|
|
261
|
+
*/
|
|
262
|
+
async function setupWizard(options = {}) {
|
|
263
|
+
const { offline = false, force = false } = options;
|
|
264
|
+
|
|
265
|
+
console.log(chalk.cyan.bold("\n🚀 Reshot Setup Wizard\n"));
|
|
266
|
+
console.log(chalk.gray("Let's configure Reshot for your project.\n"));
|
|
267
|
+
|
|
268
|
+
// Detect project context
|
|
269
|
+
const gitInfo = detectGitInfo();
|
|
270
|
+
const playwrightInfo = detectPlaywright();
|
|
271
|
+
const existingDocsRoot = detectDocumentationRoot();
|
|
272
|
+
|
|
273
|
+
// Check existing setup
|
|
274
|
+
let existingSettings = null;
|
|
275
|
+
let existingConfig = null;
|
|
276
|
+
let isAlreadyAuthed = false;
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
existingSettings = config.readSettings();
|
|
280
|
+
isAlreadyAuthed = !!(
|
|
281
|
+
existingSettings?.apiKey && existingSettings?.projectId
|
|
282
|
+
);
|
|
283
|
+
} catch {
|
|
284
|
+
// No existing settings
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
if (config.configExists()) {
|
|
289
|
+
existingConfig = config.readConfig();
|
|
290
|
+
}
|
|
291
|
+
} catch {
|
|
292
|
+
// No existing config
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// If already set up and not forcing, confirm continue
|
|
296
|
+
if ((isAlreadyAuthed || existingConfig) && !force) {
|
|
297
|
+
console.log(
|
|
298
|
+
chalk.yellow("⚠ Reshot is already configured in this project.\n"),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (isAlreadyAuthed) {
|
|
302
|
+
console.log(
|
|
303
|
+
chalk.green(" ✔ Authenticated:"),
|
|
304
|
+
chalk.cyan(existingSettings.projectName || existingSettings.projectId),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (existingConfig) {
|
|
308
|
+
console.log(
|
|
309
|
+
chalk.green(" ✔ Config found:"),
|
|
310
|
+
chalk.cyan("docsync.config.json"),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const { continueSetup } = await inquirer.prompt([
|
|
315
|
+
{
|
|
316
|
+
type: "confirm",
|
|
317
|
+
name: "continueSetup",
|
|
318
|
+
message: "Would you like to reconfigure?",
|
|
319
|
+
default: false,
|
|
320
|
+
},
|
|
321
|
+
]);
|
|
322
|
+
|
|
323
|
+
if (!continueSetup) {
|
|
324
|
+
console.log(
|
|
325
|
+
chalk.gray("\nRun"),
|
|
326
|
+
chalk.cyan("reshot studio"),
|
|
327
|
+
chalk.gray("to manage your visuals.\n"),
|
|
328
|
+
);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ========================================
|
|
334
|
+
// STEP 1: Platform Connection
|
|
335
|
+
// ========================================
|
|
336
|
+
console.log(chalk.cyan("\n━━━ Step 1: Platform Connection ━━━\n"));
|
|
337
|
+
|
|
338
|
+
let useCloud = false;
|
|
339
|
+
let projectId = existingSettings?.projectId;
|
|
340
|
+
let apiKey = existingSettings?.apiKey;
|
|
341
|
+
let projectName = existingSettings?.projectName;
|
|
342
|
+
|
|
343
|
+
if (!offline) {
|
|
344
|
+
const { connectToCloud } = await inquirer.prompt([
|
|
345
|
+
{
|
|
346
|
+
type: "list",
|
|
347
|
+
name: "connectToCloud",
|
|
348
|
+
message: "How would you like to use Reshot?",
|
|
349
|
+
choices: [
|
|
350
|
+
{
|
|
351
|
+
name: `${chalk.cyan("Connect to Reshot Cloud")} - Governance, CDN hosting, team collaboration`,
|
|
352
|
+
value: true,
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: `${chalk.gray("Offline mode")} - Local-only visuals (no cloud sync)`,
|
|
356
|
+
value: false,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
default: isAlreadyAuthed ? 0 : 1,
|
|
360
|
+
},
|
|
361
|
+
]);
|
|
362
|
+
|
|
363
|
+
useCloud = connectToCloud;
|
|
364
|
+
|
|
365
|
+
if (useCloud && !isAlreadyAuthed) {
|
|
366
|
+
console.log(chalk.gray("\nOpening browser for authentication...\n"));
|
|
367
|
+
|
|
368
|
+
const authCommand = require("./auth");
|
|
369
|
+
await authCommand();
|
|
370
|
+
|
|
371
|
+
// Re-read settings after auth
|
|
372
|
+
try {
|
|
373
|
+
existingSettings = config.readSettings();
|
|
374
|
+
projectId = existingSettings?.projectId;
|
|
375
|
+
apiKey = existingSettings?.apiKey;
|
|
376
|
+
projectName = existingSettings?.projectName;
|
|
377
|
+
isAlreadyAuthed = !!(apiKey && projectId);
|
|
378
|
+
} catch {
|
|
379
|
+
throw new Error("Authentication failed. Please try again.");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!isAlreadyAuthed) {
|
|
383
|
+
throw new Error("Authentication was not completed.");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
console.log(
|
|
387
|
+
chalk.green("\n✔ Connected to"),
|
|
388
|
+
chalk.cyan(projectName || projectId),
|
|
389
|
+
);
|
|
390
|
+
} else if (useCloud && isAlreadyAuthed) {
|
|
391
|
+
console.log(
|
|
392
|
+
chalk.green("✔ Already connected to"),
|
|
393
|
+
chalk.cyan(projectName || projectId),
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
console.log(chalk.gray("Running in offline mode - no cloud sync."));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ========================================
|
|
401
|
+
// STEP 2: Feature Selection
|
|
402
|
+
// ========================================
|
|
403
|
+
console.log(chalk.cyan("\n━━━ Step 2: Features ━━━\n"));
|
|
404
|
+
|
|
405
|
+
const { features } = await inquirer.prompt([
|
|
406
|
+
{
|
|
407
|
+
type: "checkbox",
|
|
408
|
+
name: "features",
|
|
409
|
+
message: "Which features would you like to enable?",
|
|
410
|
+
choices: [
|
|
411
|
+
{
|
|
412
|
+
name: "Visual capture from Playwright tests",
|
|
413
|
+
value: "visuals",
|
|
414
|
+
checked: playwrightInfo.hasPlaywright,
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "Documentation sync (detect drift, auto-update docs)",
|
|
418
|
+
value: "docsync",
|
|
419
|
+
checked: !!existingDocsRoot,
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
validate: (answers) => {
|
|
423
|
+
if (answers.length === 0) {
|
|
424
|
+
return "Please select at least one feature";
|
|
425
|
+
}
|
|
426
|
+
return true;
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
]);
|
|
430
|
+
|
|
431
|
+
const enableVisuals = features.includes("visuals");
|
|
432
|
+
const enableDocSync = features.includes("docsync");
|
|
433
|
+
|
|
434
|
+
// ========================================
|
|
435
|
+
// STEP 3: Visuals Configuration (if enabled)
|
|
436
|
+
// ========================================
|
|
437
|
+
let traceDir = "./test-results";
|
|
438
|
+
let journeyMappings = {};
|
|
439
|
+
|
|
440
|
+
if (enableVisuals) {
|
|
441
|
+
console.log(chalk.cyan("\n━━━ Step 3: Visual Capture ━━━\n"));
|
|
442
|
+
|
|
443
|
+
if (playwrightInfo.hasPlaywright) {
|
|
444
|
+
console.log(chalk.green("✔ Playwright detected"));
|
|
445
|
+
if (playwrightInfo.configFile) {
|
|
446
|
+
console.log(chalk.gray(` Config: ${playwrightInfo.configFile}`));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Offer to import existing tests
|
|
450
|
+
const { importTests } = await inquirer.prompt([
|
|
451
|
+
{
|
|
452
|
+
type: "confirm",
|
|
453
|
+
name: "importTests",
|
|
454
|
+
message:
|
|
455
|
+
"Import existing Playwright tests to create journey mappings?",
|
|
456
|
+
default: true,
|
|
457
|
+
},
|
|
458
|
+
]);
|
|
459
|
+
|
|
460
|
+
if (importTests) {
|
|
461
|
+
console.log(chalk.gray("\nScanning Playwright tests..."));
|
|
462
|
+
try {
|
|
463
|
+
const importTestsCommand = require("./import-tests");
|
|
464
|
+
const result = await importTestsCommand({
|
|
465
|
+
interactive: false,
|
|
466
|
+
dryRun: false,
|
|
467
|
+
});
|
|
468
|
+
if (result?.journeyMappings) {
|
|
469
|
+
journeyMappings = result.journeyMappings;
|
|
470
|
+
console.log(
|
|
471
|
+
chalk.green(
|
|
472
|
+
`✔ Imported ${Object.keys(journeyMappings).length} journey mapping(s)`,
|
|
473
|
+
),
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
} catch (err) {
|
|
477
|
+
console.log(chalk.yellow(`⚠ Could not import tests: ${err.message}`));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
console.log(chalk.yellow("⚠ Playwright not detected"));
|
|
482
|
+
console.log(
|
|
483
|
+
chalk.gray(" Install with: npm install -D @playwright/test"),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const { customTraceDir } = await inquirer.prompt([
|
|
488
|
+
{
|
|
489
|
+
type: "input",
|
|
490
|
+
name: "customTraceDir",
|
|
491
|
+
message: "Playwright test-results directory:",
|
|
492
|
+
default: "./test-results",
|
|
493
|
+
},
|
|
494
|
+
]);
|
|
495
|
+
|
|
496
|
+
traceDir = customTraceDir;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// ========================================
|
|
500
|
+
// STEP 4: Documentation Configuration (if enabled)
|
|
501
|
+
// ========================================
|
|
502
|
+
let docConfig = null;
|
|
503
|
+
|
|
504
|
+
if (enableDocSync) {
|
|
505
|
+
console.log(chalk.cyan("\n━━━ Step 4: Documentation Sync ━━━\n"));
|
|
506
|
+
|
|
507
|
+
// Check for existing documentation framework
|
|
508
|
+
const docFramework = detectDocumentationFramework();
|
|
509
|
+
let docsRoot = existingDocsRoot;
|
|
510
|
+
|
|
511
|
+
if (docFramework.detected) {
|
|
512
|
+
console.log(chalk.green(`✔ ${docFramework.framework} detected`));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (docsRoot) {
|
|
516
|
+
const docCount = countDocFiles(path.join(process.cwd(), docsRoot));
|
|
517
|
+
console.log(
|
|
518
|
+
chalk.green(
|
|
519
|
+
`✔ Found documentation at: ${docsRoot}/ (${docCount} files)`,
|
|
520
|
+
),
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
const { useDetected } = await inquirer.prompt([
|
|
524
|
+
{
|
|
525
|
+
type: "confirm",
|
|
526
|
+
name: "useDetected",
|
|
527
|
+
message: `Use ${docsRoot}/ for documentation sync?`,
|
|
528
|
+
default: true,
|
|
529
|
+
},
|
|
530
|
+
]);
|
|
531
|
+
|
|
532
|
+
if (!useDetected) {
|
|
533
|
+
docsRoot = null;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (!docsRoot) {
|
|
538
|
+
const choices = [
|
|
539
|
+
{ name: "Create docs/ directory with examples", value: "scaffold" },
|
|
540
|
+
{ name: "Specify existing directory path", value: "specify" },
|
|
541
|
+
];
|
|
542
|
+
|
|
543
|
+
// Add Astro Starlight option if not already a docs framework
|
|
544
|
+
if (!docFramework.detected) {
|
|
545
|
+
choices.push({
|
|
546
|
+
name:
|
|
547
|
+
chalk.cyan("Create Astro Starlight project") +
|
|
548
|
+
chalk.gray(" (recommended for new docs)"),
|
|
549
|
+
value: "starlight",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const { docAction } = await inquirer.prompt([
|
|
554
|
+
{
|
|
555
|
+
type: "list",
|
|
556
|
+
name: "docAction",
|
|
557
|
+
message: "Documentation setup:",
|
|
558
|
+
choices,
|
|
559
|
+
},
|
|
560
|
+
]);
|
|
561
|
+
|
|
562
|
+
if (docAction === "scaffold") {
|
|
563
|
+
console.log(chalk.gray("\nCreating docs/ directory..."));
|
|
564
|
+
docsRoot = await scaffoldDocumentation();
|
|
565
|
+
console.log(chalk.green("✔ Created docs/ with example files"));
|
|
566
|
+
} else if (docAction === "starlight") {
|
|
567
|
+
console.log(chalk.cyan("\nTo create an Astro Starlight project:\n"));
|
|
568
|
+
console.log(
|
|
569
|
+
chalk.white(" npm create astro@latest -- --template starlight"),
|
|
570
|
+
);
|
|
571
|
+
console.log(
|
|
572
|
+
chalk.gray(
|
|
573
|
+
"\nRun that command, then re-run reshot setup to link it.\n",
|
|
574
|
+
),
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
// Still create a basic docs folder as fallback
|
|
578
|
+
const { createBasic } = await inquirer.prompt([
|
|
579
|
+
{
|
|
580
|
+
type: "confirm",
|
|
581
|
+
name: "createBasic",
|
|
582
|
+
message: "Create a basic docs/ folder for now?",
|
|
583
|
+
default: true,
|
|
584
|
+
},
|
|
585
|
+
]);
|
|
586
|
+
|
|
587
|
+
if (createBasic) {
|
|
588
|
+
docsRoot = await scaffoldDocumentation();
|
|
589
|
+
console.log(chalk.green("✔ Created docs/ with example files"));
|
|
590
|
+
}
|
|
591
|
+
} else {
|
|
592
|
+
const { customPath } = await inquirer.prompt([
|
|
593
|
+
{
|
|
594
|
+
type: "input",
|
|
595
|
+
name: "customPath",
|
|
596
|
+
message: "Documentation directory path:",
|
|
597
|
+
default: "./docs",
|
|
598
|
+
validate: (input) => {
|
|
599
|
+
const fullPath = path.join(process.cwd(), input);
|
|
600
|
+
if (!fs.existsSync(fullPath)) {
|
|
601
|
+
return `Directory ${input} does not exist. Create it first or use scaffolding.`;
|
|
602
|
+
}
|
|
603
|
+
return true;
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
]);
|
|
607
|
+
docsRoot = customPath;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Determine strategy
|
|
612
|
+
let strategy = "git_pr";
|
|
613
|
+
|
|
614
|
+
if (useCloud && docsRoot) {
|
|
615
|
+
if (gitInfo.isGitHub) {
|
|
616
|
+
console.log(chalk.green("\n✔ GitHub repository detected"));
|
|
617
|
+
const { useGitPR } = await inquirer.prompt([
|
|
618
|
+
{
|
|
619
|
+
type: "confirm",
|
|
620
|
+
name: "useGitPR",
|
|
621
|
+
message: "Auto-create Pull Requests for documentation updates?",
|
|
622
|
+
default: true,
|
|
623
|
+
},
|
|
624
|
+
]);
|
|
625
|
+
strategy = useGitPR ? "git_pr" : "external_host";
|
|
626
|
+
} else {
|
|
627
|
+
const { strategyChoice } = await inquirer.prompt([
|
|
628
|
+
{
|
|
629
|
+
type: "list",
|
|
630
|
+
name: "strategyChoice",
|
|
631
|
+
message: "How should documentation updates be delivered?",
|
|
632
|
+
choices: [
|
|
633
|
+
{ name: "Git Pull Requests", value: "git_pr" },
|
|
634
|
+
{
|
|
635
|
+
name: "Notifications only (external CMS)",
|
|
636
|
+
value: "external_host",
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
},
|
|
640
|
+
]);
|
|
641
|
+
strategy = strategyChoice;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
docConfig = generateDocumentationConfig(docsRoot, strategy);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// ========================================
|
|
649
|
+
// STEP 5: Generate Configuration
|
|
650
|
+
// ========================================
|
|
651
|
+
console.log(chalk.cyan("\n━━━ Generating Configuration ━━━\n"));
|
|
652
|
+
|
|
653
|
+
const newConfig = {
|
|
654
|
+
$schema: "https://reshot.dev/schemas/docsync-config.json",
|
|
655
|
+
version: "2.0",
|
|
656
|
+
baseUrl: existingConfig?.baseUrl || "http://localhost:3000",
|
|
657
|
+
viewport: existingConfig?.viewport || { width: 1280, height: 720 },
|
|
658
|
+
_metadata: {
|
|
659
|
+
features: {
|
|
660
|
+
visuals: enableVisuals,
|
|
661
|
+
docsync: enableDocSync,
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
if (useCloud && projectId) {
|
|
667
|
+
newConfig.projectId = projectId;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (enableVisuals) {
|
|
671
|
+
newConfig.visuals = {
|
|
672
|
+
traceDir,
|
|
673
|
+
journeyMappings:
|
|
674
|
+
Object.keys(journeyMappings).length > 0 ? journeyMappings : undefined,
|
|
675
|
+
};
|
|
676
|
+
newConfig.assetDir = ".reshot/output";
|
|
677
|
+
newConfig.scenarios = existingConfig?.scenarios || [];
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (enableDocSync && docConfig) {
|
|
681
|
+
newConfig.documentation = docConfig;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Write configuration
|
|
685
|
+
config.writeConfig(newConfig);
|
|
686
|
+
console.log(chalk.green("✔ Created docsync.config.json"));
|
|
687
|
+
|
|
688
|
+
// ========================================
|
|
689
|
+
// STEP 6: Success & Next Steps
|
|
690
|
+
// ========================================
|
|
691
|
+
console.log(chalk.green("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
|
692
|
+
console.log(chalk.green.bold("✔ Reshot setup complete!"));
|
|
693
|
+
console.log(chalk.green("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"));
|
|
694
|
+
|
|
695
|
+
console.log(chalk.cyan("Next steps:\n"));
|
|
696
|
+
|
|
697
|
+
console.log(
|
|
698
|
+
` 1. ${chalk.gray("Review")} ${chalk.cyan("docsync.config.json")} ${chalk.gray("and commit to your repo")}`,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
if (enableVisuals) {
|
|
702
|
+
console.log(
|
|
703
|
+
` 2. ${chalk.gray("Run Playwright tests to generate traces:")}`,
|
|
704
|
+
);
|
|
705
|
+
console.log(` ${chalk.cyan("npx playwright test")}`);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (enableDocSync) {
|
|
709
|
+
console.log(
|
|
710
|
+
` ${enableVisuals ? "3" : "2"}. ${chalk.gray("Add")} ${chalk.cyan("reshot_journey")} ${chalk.gray("frontmatter to your markdown files")}`,
|
|
711
|
+
);
|
|
712
|
+
console.log(
|
|
713
|
+
` ${chalk.gray('Example: reshot_journey: "auth/login-flow"')}`,
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
console.log(`\n ${chalk.gray("Sync visuals and docs to Reshot:")}`);
|
|
718
|
+
console.log(` ${chalk.cyan("reshot sync")}`);
|
|
719
|
+
|
|
720
|
+
console.log(`\n ${chalk.gray("Launch the visual management UI:")}`);
|
|
721
|
+
console.log(` ${chalk.cyan("reshot studio")}\n`);
|
|
722
|
+
|
|
723
|
+
// Offer to launch studio
|
|
724
|
+
const { launchStudio } = await inquirer.prompt([
|
|
725
|
+
{
|
|
726
|
+
type: "confirm",
|
|
727
|
+
name: "launchStudio",
|
|
728
|
+
message: "Launch Reshot Studio now?",
|
|
729
|
+
default: true,
|
|
730
|
+
},
|
|
731
|
+
]);
|
|
732
|
+
|
|
733
|
+
if (launchStudio) {
|
|
734
|
+
console.log(chalk.cyan("\n🎬 Launching Reshot Studio...\n"));
|
|
735
|
+
const uiCommand = require("./ui");
|
|
736
|
+
await uiCommand({ open: true });
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
module.exports = setupWizard;
|