@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.
Files changed (59) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +388 -0
  3. package/package.json +64 -0
  4. package/src/commands/auth.js +259 -0
  5. package/src/commands/chrome.js +140 -0
  6. package/src/commands/ci-run.js +123 -0
  7. package/src/commands/ci-setup.js +288 -0
  8. package/src/commands/drifts.js +423 -0
  9. package/src/commands/import-tests.js +309 -0
  10. package/src/commands/ingest.js +458 -0
  11. package/src/commands/init.js +633 -0
  12. package/src/commands/publish.js +1721 -0
  13. package/src/commands/pull.js +303 -0
  14. package/src/commands/record.js +94 -0
  15. package/src/commands/run.js +476 -0
  16. package/src/commands/setup-wizard.js +740 -0
  17. package/src/commands/setup.js +137 -0
  18. package/src/commands/status.js +275 -0
  19. package/src/commands/sync.js +621 -0
  20. package/src/commands/ui.js +248 -0
  21. package/src/commands/validate-docs.js +529 -0
  22. package/src/index.js +462 -0
  23. package/src/lib/api-client.js +815 -0
  24. package/src/lib/capture-engine.js +1623 -0
  25. package/src/lib/capture-script-runner.js +3120 -0
  26. package/src/lib/ci-detect.js +137 -0
  27. package/src/lib/config.js +1240 -0
  28. package/src/lib/diff-engine.js +642 -0
  29. package/src/lib/hash.js +74 -0
  30. package/src/lib/image-crop.js +396 -0
  31. package/src/lib/matrix.js +89 -0
  32. package/src/lib/output-path-template.js +318 -0
  33. package/src/lib/playwright-runner.js +252 -0
  34. package/src/lib/polished-clip.js +553 -0
  35. package/src/lib/privacy-engine.js +408 -0
  36. package/src/lib/progress-tracker.js +142 -0
  37. package/src/lib/record-browser-injection.js +654 -0
  38. package/src/lib/record-cdp.js +612 -0
  39. package/src/lib/record-clip.js +343 -0
  40. package/src/lib/record-config.js +623 -0
  41. package/src/lib/record-screenshot.js +360 -0
  42. package/src/lib/record-terminal.js +123 -0
  43. package/src/lib/recorder-service.js +781 -0
  44. package/src/lib/secrets.js +51 -0
  45. package/src/lib/selector-strategies.js +859 -0
  46. package/src/lib/standalone-mode.js +400 -0
  47. package/src/lib/storage-providers.js +569 -0
  48. package/src/lib/style-engine.js +684 -0
  49. package/src/lib/ui-api.js +4677 -0
  50. package/src/lib/ui-assets.js +373 -0
  51. package/src/lib/ui-executor.js +587 -0
  52. package/src/lib/variant-injector.js +591 -0
  53. package/src/lib/viewport-presets.js +454 -0
  54. package/src/lib/worker-pool.js +118 -0
  55. package/web/cropper/index.html +436 -0
  56. package/web/manager/dist/assets/index--ZgioErz.js +507 -0
  57. package/web/manager/dist/assets/index-n468W0Wr.css +1 -0
  58. package/web/manager/dist/index.html +27 -0
  59. package/web/subtitle-editor/index.html +295 -0
@@ -0,0 +1,309 @@
1
+ // import-tests.js - Import existing Playwright tests into Reshot configuration
2
+ // This command scans Playwright tests and creates journey mappings
3
+
4
+ const chalk = require("chalk");
5
+ const fs = require("fs-extra");
6
+ const path = require("path");
7
+ const inquirer = require("inquirer");
8
+ const config = require("../lib/config");
9
+
10
+ /**
11
+ * Parse Playwright test file to extract test structure
12
+ */
13
+ function parsePlaywrightTestFile(filePath) {
14
+ const content = fs.readFileSync(filePath, "utf-8");
15
+ const tests = [];
16
+
17
+ // Extract journey key from file header comment if present
18
+ const journeyMatch = content.match(/Journey\s*Key:\s*["']?([^"'\n]+)["']?/i);
19
+ const fileJourneyKey = journeyMatch ? journeyMatch[1].trim() : null;
20
+
21
+ // Extract test.describe blocks
22
+ const describeRegex = /test\.describe\s*\(\s*["'`]([^"'`]+)["'`]/g;
23
+ let describeMatch;
24
+ while ((describeMatch = describeRegex.exec(content)) !== null) {
25
+ tests.push({
26
+ type: "describe",
27
+ name: describeMatch[1],
28
+ index: describeMatch.index,
29
+ });
30
+ }
31
+
32
+ // Extract individual tests
33
+ const testRegex = /(?<!\.describe\s*\()test\s*\(\s*["'`]([^"'`]+)["'`]/g;
34
+ let testMatch;
35
+ while ((testMatch = testRegex.exec(content)) !== null) {
36
+ tests.push({
37
+ type: "test",
38
+ name: testMatch[1],
39
+ index: testMatch.index,
40
+ });
41
+ }
42
+
43
+ // Sort by position in file
44
+ tests.sort((a, b) => a.index - b.index);
45
+
46
+ return {
47
+ filePath,
48
+ fileJourneyKey,
49
+ tests,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Generate a journey key from test/describe name
55
+ */
56
+ function generateJourneyKey(fileName, testName, describeName = null) {
57
+ // Clean up the file name to get a prefix
58
+ const filePrefix = fileName
59
+ .replace(/^\d+-/, "") // Remove leading numbers like "01-"
60
+ .replace(/\.spec\.(ts|js)$/, "")
61
+ .replace(/\.test\.(ts|js)$/, "")
62
+ .replace(/[^a-z0-9]+/gi, "-")
63
+ .toLowerCase();
64
+
65
+ // Clean up test name
66
+ const testSuffix = testName
67
+ .replace(/^should\s+/i, "")
68
+ .replace(/[^a-z0-9]+/gi, "-")
69
+ .toLowerCase()
70
+ .slice(0, 30);
71
+
72
+ return `${filePrefix}/${testSuffix}`
73
+ .replace(/-+/g, "-")
74
+ .replace(/^-|-$/g, "");
75
+ }
76
+
77
+ /**
78
+ * Discover all Playwright test files
79
+ */
80
+ function discoverTestFiles(testDir) {
81
+ const testFiles = [];
82
+
83
+ if (!fs.existsSync(testDir)) {
84
+ return testFiles;
85
+ }
86
+
87
+ function walkDir(dir) {
88
+ const items = fs.readdirSync(dir);
89
+
90
+ for (const item of items) {
91
+ const fullPath = path.join(dir, item);
92
+ const stat = fs.statSync(fullPath);
93
+
94
+ if (stat.isDirectory()) {
95
+ if (item !== "node_modules" && !item.startsWith(".")) {
96
+ walkDir(fullPath);
97
+ }
98
+ } else if (
99
+ item.endsWith(".spec.ts") ||
100
+ item.endsWith(".spec.js") ||
101
+ item.endsWith(".test.ts") ||
102
+ item.endsWith(".test.js")
103
+ ) {
104
+ testFiles.push(fullPath);
105
+ }
106
+ }
107
+ }
108
+
109
+ walkDir(testDir);
110
+ return testFiles;
111
+ }
112
+
113
+ /**
114
+ * Find Playwright config and extract test directory
115
+ */
116
+ function findPlaywrightConfig() {
117
+ const configFiles = [
118
+ "playwright.config.ts",
119
+ "playwright.config.js",
120
+ "playwright.config.mjs",
121
+ ];
122
+
123
+ for (const file of configFiles) {
124
+ const fullPath = path.join(process.cwd(), file);
125
+ if (fs.existsSync(fullPath)) {
126
+ const content = fs.readFileSync(fullPath, "utf-8");
127
+
128
+ // Try to extract testDir
129
+ const testDirMatch = content.match(/testDir:\s*["'`]([^"'`]+)["'`]/);
130
+ const testDir = testDirMatch ? testDirMatch[1] : "./tests";
131
+
132
+ // Try to extract output directory
133
+ const outputDirMatch = content.match(/outputDir:\s*["'`]([^"'`]+)["'`]/);
134
+ const outputDir = outputDirMatch ? outputDirMatch[1] : "./test-results";
135
+
136
+ return {
137
+ configFile: file,
138
+ testDir,
139
+ outputDir,
140
+ };
141
+ }
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ /**
148
+ * Main import-tests command
149
+ */
150
+ async function importTestsCommand(options = {}) {
151
+ const { interactive = true, dryRun = false } = options;
152
+
153
+ console.log(chalk.cyan.bold("\n📋 Import Playwright Tests\n"));
154
+
155
+ // Find Playwright configuration
156
+ const pwConfig = findPlaywrightConfig();
157
+
158
+ if (!pwConfig) {
159
+ console.error(chalk.red("✖ No Playwright configuration found."));
160
+ console.log(
161
+ chalk.gray(
162
+ " Make sure you have a playwright.config.ts or playwright.config.js file.",
163
+ ),
164
+ );
165
+ process.exit(1);
166
+ }
167
+
168
+ console.log(chalk.gray(`Found: ${pwConfig.configFile}`));
169
+ console.log(chalk.gray(`Test directory: ${pwConfig.testDir}`));
170
+ console.log(chalk.gray(`Output directory: ${pwConfig.outputDir}\n`));
171
+
172
+ // Discover test files
173
+ const testDir = path.resolve(process.cwd(), pwConfig.testDir);
174
+ const testFiles = discoverTestFiles(testDir);
175
+
176
+ if (testFiles.length === 0) {
177
+ console.log(
178
+ chalk.yellow("⚠ No test files found in"),
179
+ chalk.cyan(pwConfig.testDir),
180
+ );
181
+ process.exit(0);
182
+ }
183
+
184
+ console.log(chalk.green(`Found ${testFiles.length} test file(s):\n`));
185
+
186
+ // Parse all test files
187
+ const allTests = [];
188
+ for (const filePath of testFiles) {
189
+ const relativePath = path.relative(process.cwd(), filePath);
190
+ const parsed = parsePlaywrightTestFile(filePath);
191
+
192
+ console.log(chalk.white(` ${relativePath}`));
193
+ if (parsed.fileJourneyKey) {
194
+ console.log(chalk.gray(` └─ Journey Key: ${parsed.fileJourneyKey}`));
195
+ }
196
+
197
+ for (const test of parsed.tests) {
198
+ if (test.type === "describe") {
199
+ console.log(chalk.gray(` ├─ describe: "${test.name}"`));
200
+ } else {
201
+ console.log(chalk.gray(` │ └─ test: "${test.name}"`));
202
+
203
+ allTests.push({
204
+ file: relativePath,
205
+ fileName: path.basename(filePath),
206
+ describeName: null, // TODO: Track describe context
207
+ testName: test.name,
208
+ fileJourneyKey: parsed.fileJourneyKey,
209
+ });
210
+ }
211
+ }
212
+ }
213
+
214
+ console.log();
215
+
216
+ // Generate journey mappings
217
+ const journeyMappings = {};
218
+
219
+ for (const test of allTests) {
220
+ // Use file-level journey key if available, otherwise generate
221
+ const journeyKey =
222
+ test.fileJourneyKey || generateJourneyKey(test.fileName, test.testName);
223
+
224
+ // Create a test-results path pattern for this test
225
+ // Playwright creates folders like: test-results/test-name-chromium/
226
+ const testResultPattern = test.testName
227
+ .toLowerCase()
228
+ .replace(/[^a-z0-9]+/g, "-")
229
+ .slice(0, 50);
230
+
231
+ journeyMappings[testResultPattern] = journeyKey;
232
+ }
233
+
234
+ // Show proposed mappings
235
+ console.log(chalk.cyan("━━━ Proposed Journey Mappings ━━━\n"));
236
+
237
+ for (const [pattern, journeyKey] of Object.entries(journeyMappings)) {
238
+ console.log(chalk.white(` ${pattern}`));
239
+ console.log(chalk.gray(` → ${journeyKey}\n`));
240
+ }
241
+
242
+ if (dryRun) {
243
+ console.log(chalk.yellow("\n✓ Dry run complete. No changes made.\n"));
244
+ return { journeyMappings };
245
+ }
246
+
247
+ // Confirm save
248
+ if (interactive) {
249
+ const { confirm } = await inquirer.prompt([
250
+ {
251
+ type: "confirm",
252
+ name: "confirm",
253
+ message: "Save these mappings to docsync.config.json?",
254
+ default: true,
255
+ },
256
+ ]);
257
+
258
+ if (!confirm) {
259
+ console.log(chalk.gray("\nCancelled."));
260
+ return;
261
+ }
262
+ }
263
+
264
+ // Update docsync.config.json
265
+ let docSyncConfig;
266
+ try {
267
+ // Try to read existing config (use readDocSyncConfig which is less strict)
268
+ docSyncConfig = config.readDocSyncConfig();
269
+ } catch {
270
+ // Create new config with minimal required fields
271
+ docSyncConfig = {
272
+ $schema: "https://reshot.dev/schemas/docsync-config.json",
273
+ version: "2.0",
274
+ scenarios: [],
275
+ };
276
+ }
277
+
278
+ // Add visuals section with trace mappings
279
+ docSyncConfig.visuals = {
280
+ ...docSyncConfig.visuals,
281
+ traceDir: pwConfig.outputDir,
282
+ journeyMappings,
283
+ };
284
+
285
+ // Ensure scenarios array exists for config.readConfig() compatibility
286
+ if (!docSyncConfig.scenarios) {
287
+ docSyncConfig.scenarios = [];
288
+ }
289
+
290
+ config.writeConfig(docSyncConfig);
291
+
292
+ console.log(
293
+ chalk.green("\n✓ Updated docsync.config.json with journey mappings"),
294
+ );
295
+ console.log(chalk.gray("\nNext steps:"));
296
+ console.log(
297
+ chalk.gray(" 1. Run your Playwright tests:"),
298
+ chalk.cyan("npx playwright test"),
299
+ );
300
+ console.log(
301
+ chalk.gray(" 2. Sync traces to Reshot:"),
302
+ chalk.cyan("reshot sync"),
303
+ );
304
+ console.log(chalk.gray(" 3. View results:"), chalk.cyan("reshot status\n"));
305
+
306
+ return { journeyMappings };
307
+ }
308
+
309
+ module.exports = importTestsCommand;