@rettangoli/vt 1.0.2 → 1.0.3

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/README.md CHANGED
@@ -142,15 +142,15 @@ Screenshot naming:
142
142
  A pre-built Docker image with `rtgl` and Playwright browsers is available:
143
143
 
144
144
  ```bash
145
- docker pull han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.0-rc27
145
+ docker pull han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.10
146
146
  ```
147
147
 
148
148
  Run commands against a local project:
149
149
 
150
150
  ```bash
151
- docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.0-rc27 rtgl vt screenshot
152
- docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.0-rc27 rtgl vt report
153
- docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.0-rc27 rtgl vt accept
151
+ docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.10 rtgl vt screenshot
152
+ docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.10 rtgl vt report
153
+ docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.10 rtgl vt accept
154
154
  ```
155
155
 
156
156
  Note:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/vt",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Rettangoli Visual Testing",
5
5
  "type": "module",
6
6
  "repository": {
@@ -60,8 +60,8 @@
60
60
  {% for file in files %}
61
61
  <rtgl-view w="f">
62
62
  <a style="display: contents; text-decoration: none; color: inherit;"
63
- href="#{{ file.frontMatter.title | slug }}">
64
- <rtgl-text id="{{ file.frontMatter.title | slug }}" s="h3">{{ file.frontMatter.title | default: file.path
63
+ href="#{{ file.anchorId }}">
64
+ <rtgl-text id="{{ file.anchorId }}" s="h3">{{ file.frontMatter.title | default: file.path
65
65
  }}</rtgl-text>
66
66
  </a>
67
67
 
package/src/common.js CHANGED
@@ -15,7 +15,7 @@ import path from "path";
15
15
  import { validateFiniteNumber, validateFrontMatter } from "./validation.js";
16
16
  import { createCaptureTasks } from "./capture/spec-loader.js";
17
17
  import { runCaptureScheduler } from "./capture/capture-scheduler.js";
18
- import { deriveSectionPageKey } from "./section-page-key.js";
18
+ import { deriveAnchorId, deriveSectionPageKey } from "./section-page-key.js";
19
19
 
20
20
  const removeExtension = (filePath) => filePath.replace(/\.[^/.]+$/, "");
21
21
 
@@ -44,10 +44,9 @@ async function readYaml(filePath) {
44
44
  }
45
45
  }
46
46
 
47
- // Add custom filter to convert string to lowercase and replace spaces with hyphens
48
- engine.registerFilter("slug", (value) => {
49
- if (typeof value !== "string") return "";
50
- return value.toLowerCase().replace(/\s+/g, "-");
47
+ // Add custom filter to derive slug-safe ids for URLs and anchors.
48
+ engine.registerFilter("slug", (value, fallbackValue) => {
49
+ return deriveAnchorId(value, fallbackValue);
51
50
  });
52
51
 
53
52
  // Add custom filter to remove file extension
@@ -416,13 +415,18 @@ function generateOverview(data, templatePath, outputPath, configData) {
416
415
  try {
417
416
  renderedContent = engine.parseAndRenderSync(templateContent, {
418
417
  ...configData,
419
- files: data.filter((file) => {
420
- const filePath = path.normalize(file.path);
421
- const sectionPath = path.normalize(section.files);
422
- // Check if file is in the folder or any subfolder
423
- const fileDir = path.dirname(filePath);
424
- return fileDir === sectionPath || fileDir.startsWith(sectionPath + path.sep);
425
- }),
418
+ files: data
419
+ .filter((file) => {
420
+ const filePath = path.normalize(file.path);
421
+ const sectionPath = path.normalize(section.files);
422
+ // Check if file is in the folder or any subfolder
423
+ const fileDir = path.dirname(filePath);
424
+ return fileDir === sectionPath || fileDir.startsWith(sectionPath + path.sep);
425
+ })
426
+ .map((file) => ({
427
+ ...file,
428
+ anchorId: deriveAnchorId(file.frontMatter?.title, removeExtension(file.path)),
429
+ })),
426
430
  currentSection: section,
427
431
  sidebarItems: encodeURIComponent(JSON.stringify(sidebarItems)),
428
432
  });
@@ -1,11 +1,11 @@
1
1
  import fs from "fs";
2
2
  import { Liquid } from "liquidjs";
3
+ import { deriveAnchorId } from "../section-page-key.js";
3
4
 
4
5
  const engine = new Liquid();
5
6
 
6
- engine.registerFilter("slug", (value) => {
7
- if (typeof value !== "string") return "";
8
- return value.toLowerCase().replace(/\s+/g, "-");
7
+ engine.registerFilter("slug", (value, fallbackValue) => {
8
+ return deriveAnchorId(value, fallbackValue);
9
9
  });
10
10
 
11
11
  export async function renderHtmlReport({ results, templatePath, outputPath }) {
@@ -5,10 +5,18 @@ function normalizeString(value) {
5
5
  return value.trim();
6
6
  }
7
7
 
8
- export function deriveSectionPageKey(sectionLike) {
9
- return normalizeString(sectionLike?.title)
8
+ export function derivePageKey(value) {
9
+ return normalizeString(value)
10
10
  .toLowerCase()
11
11
  .replace(/[^a-z0-9]+/g, "-")
12
12
  .replace(/-+/g, "-")
13
13
  .replace(/^-+|-+$/g, "");
14
14
  }
15
+
16
+ export function deriveSectionPageKey(sectionLike) {
17
+ return derivePageKey(sectionLike?.title) || derivePageKey(sectionLike?.files);
18
+ }
19
+
20
+ export function deriveAnchorId(value, fallbackValue = "") {
21
+ return derivePageKey(value) || derivePageKey(fallbackValue);
22
+ }
package/src/validation.js CHANGED
@@ -136,7 +136,7 @@ function assertDerivableSectionPageKey(sectionLike, path) {
136
136
  const pageKey = deriveSectionPageKey(sectionLike);
137
137
  assert(
138
138
  pageKey.length > 0,
139
- `"${path}" must contain at least one letter or number.`,
139
+ `"${path}" must produce a page key from title or files.`,
140
140
  );
141
141
  }
142
142
 
@@ -169,14 +169,14 @@ function validateSection(section, index) {
169
169
 
170
170
  assert(typeof item.title === "string" && item.title.trim().length > 0, `"${itemPath}.title" is required.`);
171
171
  assert(typeof item.files === "string" && item.files.trim().length > 0, `"${itemPath}.files" is required.`);
172
- assertDerivableSectionPageKey(item, `${itemPath}.title`);
172
+ assertDerivableSectionPageKey(item, itemPath);
173
173
  });
174
174
  return;
175
175
  }
176
176
 
177
177
  validateOptionalString(section.files, `${sectionPath}.files`);
178
178
  assert(typeof section.files === "string" && section.files.trim().length > 0, `"${sectionPath}.files" is required.`);
179
- assertDerivableSectionPageKey(section, `${sectionPath}.title`);
179
+ assertDerivableSectionPageKey(section, sectionPath);
180
180
  }
181
181
 
182
182
  function collectSectionPageKeys(vtConfig) {