@rettangoli/vt 0.0.3 → 0.0.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/common.js +52 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/vt",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Rettangoli Visual Testing",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/common.js CHANGED
@@ -16,12 +16,14 @@ import { codeToHtml } from "shiki";
16
16
  import sharp from "sharp";
17
17
  import path from "path";
18
18
 
19
+ const removeExtension = (filePath) => filePath.replace(/\.[^/.]+$/, "");
20
+
19
21
  const convertToHtmlExtension = (filePath) => {
20
22
  if (filePath.endsWith(".html")) {
21
23
  return filePath;
22
24
  }
23
25
  // Remove existing extension and add .html
24
- const baseName = filePath.replace(/\.[^/.]+$/, "");
26
+ const baseName = removeExtension(filePath);
25
27
  return baseName + ".html";
26
28
  };
27
29
 
@@ -50,7 +52,7 @@ engine.registerFilter("slug", (value) => {
50
52
  // Add custom filter to remove file extension
51
53
  engine.registerFilter("remove_ext", (value) => {
52
54
  if (typeof value !== "string") return "";
53
- return value.replace(/\.[^/.]+$/, "");
55
+ return removeExtension(value);
54
56
  });
55
57
 
56
58
  /**
@@ -136,10 +138,10 @@ async function generateHtml(specsDir, templatePath, outputDir, templateConfig) {
136
138
  const relativePath = path.relative(specsDir, filePath);
137
139
 
138
140
  let templateToUse = defaultTemplateContent;
139
- const resolvedTemplatePath = frontMatterObj.template ?
141
+ const resolvedTemplatePath = frontMatterObj?.template ?
140
142
  join(templateConfig.vtPath, "templates", frontMatterObj.template) :
141
143
  templateConfig.defaultTemplate;
142
- templateToUse = readFileSync(resolvedTemplatePath, "utf8");
144
+ templateToUse = readFileSync(resolvedTemplatePath, "utf8");
143
145
 
144
146
  // Render template
145
147
  const renderedContent = engine.parseAndRenderSync(templateToUse, {
@@ -214,7 +216,7 @@ function startWebServer(artifactsDir, staticDir, port) {
214
216
  server.listen(port, () => {
215
217
  console.log(`Server started at http://localhost:${port}`);
216
218
  });
217
-
219
+
218
220
  return server;
219
221
  }
220
222
 
@@ -255,9 +257,23 @@ async function takeScreenshots(
255
257
  console.log("Launching browser to take screenshots...");
256
258
  const browser = await chromium.launch();
257
259
 
260
+ const takeAndSaveScreenshot = async (page, basePath, suffix = '') => {
261
+ const finalPath = suffix ? `${basePath}-${suffix}` : basePath;
262
+ const tempPngPath = join(screenshotsDir, `${finalPath}.png`);
263
+ const screenshotPath = join(screenshotsDir, `${finalPath}.webp`);
264
+ ensureDirectoryExists(dirname(screenshotPath));
265
+
266
+ await page.screenshot({ path: tempPngPath, fullPage: true, animations: "disabled" });
267
+ await sharp(tempPngPath).webp({ quality: 85 }).toFile(screenshotPath);
268
+
269
+ if (existsSync(tempPngPath)) {
270
+ unlinkSync(tempPngPath);
271
+ }
272
+ return screenshotPath;
273
+ };
274
+
258
275
  try {
259
- // Process files in parallel with limited concurrency
260
- const files = [...generatedFiles]; // Create a copy to work with
276
+ const files = [...generatedFiles];
261
277
  const total = files.length;
262
278
  let completed = 0;
263
279
 
@@ -268,95 +284,52 @@ async function takeScreenshots(
268
284
  // Create a new context and page for each file (for parallelism)
269
285
  const context = await browser.newContext();
270
286
  const page = await context.newPage();
287
+ let screenshotIndex = 0;
271
288
 
272
289
  try {
273
290
  // Construct URL from file path (add /candidate prefix since server serves from parent)
274
291
  const fileUrl = convertToHtmlExtension(
275
292
  `${serverUrl}/candidate/${file.path.replace(/\\/g, '/')}`
276
293
  );
277
- console.log(`Taking screenshot of ${fileUrl}`);
278
-
279
- // Navigate to the page
294
+ console.log(`Navigating to ${fileUrl}`);
280
295
  await page.goto(fileUrl, { waitUntil: "networkidle" });
281
-
282
- // Create screenshot output path (remove extension and add .webp)
283
- const baseName = file.path.replace(/\.[^/.]+$/, "");
284
- const tempPngPath = join(screenshotsDir, `${baseName}.png`);
285
- const screenshotPath = join(screenshotsDir, `${baseName}.webp`);
286
- ensureDirectoryExists(dirname(screenshotPath));
287
-
288
296
  if (waitTime > 0) {
289
- await new Promise((resolve) => setTimeout(resolve, waitTime));
297
+ await page.waitForTimeout(waitTime);
290
298
  }
299
+ const baseName = removeExtension(file.path);
291
300
 
292
- // Take screenshot as PNG first (Playwright doesn't support WebP)
293
- await page.screenshot({
294
- path: tempPngPath,
295
- fullPage: true
296
- });
301
+ const initialScreenshotPath = await takeAndSaveScreenshot(page, baseName);
302
+ console.log(`Initial screenshot saved: ${initialScreenshotPath}`);
297
303
 
298
- // Convert PNG to WebP using Sharp
299
- await sharp(tempPngPath)
300
- .webp({ quality: 85 })
301
- .toFile(screenshotPath);
302
-
303
- // Remove temporary PNG file
304
- if (existsSync(tempPngPath)) {
305
- unlinkSync(tempPngPath);
306
- }
307
-
308
- // example instructions:
309
- for (const instruction of file.frontMatter?.instructions || []) {
310
- const [command, ...args] = instruction.split(" ");
304
+ for (const step of file.frontMatter?.steps || []) {
305
+ const [command, ...args] = step.split(" ");
311
306
  switch (command) {
312
- case "lc":
313
- const x = Number(args[0]);
314
- const y = Number(args[1]);
315
- await page.mouse.click(x, y, {
316
- button: "left",
317
- });
307
+ case "move":
308
+ await page.mouse.move(Number(args[0]), Number(args[1]));
309
+ break;
310
+ case "click":
311
+ await page.mouse.click(Number(args[0]), Number(args[1]), { button: "left" });
312
+ break;
313
+ case "rclick":
314
+ await page.mouse.click(Number(args[0]), Number(args[1]), { button: "right" });
318
315
  break;
319
- case "ss":
320
- const baseName = file.path.replace(/\.[^/.]+$/, "");
321
- const tempAdditionalPngPath = join(
322
- screenshotsDir,
323
- `${baseName}-${args[0]}.png`
324
- );
325
- const additonalScreenshotPath = join(
326
- screenshotsDir,
327
- `${baseName}-${args[0]}.webp`
328
- );
329
- console.log(`Taking additional screenshot at ${additonalScreenshotPath}`);
330
-
331
- // Take screenshot as PNG first
332
- await page.screenshot({
333
- path: tempAdditionalPngPath,
334
- fullPage: true
335
- });
336
-
337
- // Convert PNG to WebP using Sharp
338
- await sharp(tempAdditionalPngPath)
339
- .webp({ quality: 85 })
340
- .toFile(additonalScreenshotPath);
341
-
342
- // Remove temporary PNG file
343
- if (existsSync(tempAdditionalPngPath)) {
344
- unlinkSync(tempAdditionalPngPath);
345
- }
346
-
347
- console.log(
348
- `Additional screenshot taken at ${additonalScreenshotPath}`
349
- );
316
+ case "keypress":
317
+ await page.keyboard.press(args[0]);
318
+ break;
319
+ case "wait":
320
+ await page.waitForTimeout(Number(args[0]));
321
+ break;
322
+ case "screenshot":
323
+ screenshotIndex++;
324
+ const screenshotPath = await takeAndSaveScreenshot(page, `${baseName}-${screenshotIndex}`);
325
+ console.log(`Screenshot saved: ${screenshotPath}`);
350
326
  break;
351
327
  }
352
328
  }
353
-
354
329
  completed++;
355
- console.log(
356
- `Screenshot saved: ${screenshotPath} (${completed}/${total})`
357
- );
330
+ console.log(`Finished processing ${file.path} (${completed}/${total})`);
358
331
  } catch (error) {
359
- console.error(`Error taking screenshot for ${file.path}:`, error);
332
+ console.error(`Error processing instructions for ${file.path}:`, error);
360
333
  } finally {
361
334
  // Close the context when done
362
335
  await context.close();
@@ -467,4 +440,4 @@ export {
467
440
  takeScreenshots,
468
441
  generateOverview,
469
442
  readYaml,
470
- };
443
+ };