@zenuml/core 3.43.2 → 3.44.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/.mcp.json +16 -0
- package/AGENTS.md +26 -0
- package/CLAUDE.md +5 -4
- package/TUTORIAL.md +1 -1
- package/bun.lock +6 -0
- package/bunfig.toml +1 -1
- package/cy/demo1.html +28 -0
- package/cy/demo3.html +28 -0
- package/cy/demo4.html +28 -0
- package/cy/smoke-fragment.html +1 -2
- package/cy/vertical-1.html +25 -0
- package/cy/vertical-10.html +33 -0
- package/cy/vertical-11.html +29 -0
- package/cy/vertical-2.html +23 -0
- package/cy/vertical-3.html +37 -0
- package/cy/vertical-4.html +42 -0
- package/cy/vertical-5.html +40 -0
- package/cy/vertical-6.html +29 -0
- package/cy/vertical-7.html +27 -0
- package/cy/vertical-8.html +32 -0
- package/cy/vertical-9.html +25 -0
- package/dist/stats.html +1 -1
- package/dist/zenuml.esm.mjs +19203 -18751
- package/dist/zenuml.js +570 -569
- package/docs/asciidoc/contributor.adoc +4 -4
- package/docs/osx-unsupported-arm64-node12.md +2 -2
- package/docs/watch.md +1 -1
- package/index.html +53 -28
- package/package.json +7 -9
- package/playwright.config.ts +6 -3
- package/scripts/snapshot-dual.js +173 -0
|
@@ -9,9 +9,9 @@ This project is built with vue cli. It has two types of target: the web applicat
|
|
|
9
9
|
|
|
10
10
|
....
|
|
11
11
|
# to build the library
|
|
12
|
-
|
|
12
|
+
bun run build
|
|
13
13
|
# to build the web application
|
|
14
|
-
|
|
14
|
+
bun run build:site
|
|
15
15
|
....
|
|
16
16
|
|
|
17
17
|
== Demo pages
|
|
@@ -42,11 +42,11 @@ We use antlr4 to parse the ZenUML DSL code. The grammar files are:
|
|
|
42
42
|
|
|
43
43
|
=== How to generate the parser and lexer
|
|
44
44
|
|
|
45
|
-
Run `
|
|
45
|
+
Run `bun run antlr` to generate the parser and lexer.
|
|
46
46
|
|
|
47
47
|
=== Setup local development environment
|
|
48
48
|
|
|
49
|
-
Run `
|
|
49
|
+
Run `bun run antlr:setup` (`python3 -m pip install antlr4-tools`) to install the antlr4 command and the runtime.
|
|
50
50
|
|
|
51
51
|
To test the setup, go to `src/g4-unit/hello-world` folder, and run:
|
|
52
52
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Error when running '
|
|
1
|
+
Error when running 'bun run build'
|
|
2
2
|
|
|
3
3
|
```shell
|
|
4
4
|
ERROR Failed to compile with 1 error 10:15:37 AM
|
|
@@ -10,4 +10,4 @@ Missing binding /Users/pengxiao/workspaces/zenuml/vue-sequence/node_modules/node
|
|
|
10
10
|
Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 14.x
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Remove 'node_modules' and re-run '
|
|
13
|
+
Remove 'node_modules' and re-run 'bun run build' under node 14.
|
package/docs/watch.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
watch '
|
|
1
|
+
watch 'bun run antlr' src/g4
|
package/index.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</style>
|
|
16
16
|
<link
|
|
17
17
|
rel="stylesheet"
|
|
18
|
-
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/
|
|
18
|
+
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css"
|
|
19
19
|
crossorigin="anonymous"
|
|
20
20
|
/>
|
|
21
21
|
<script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.1/lib/codemirror.min.js"></script>
|
|
@@ -34,11 +34,18 @@
|
|
|
34
34
|
<title>ZenUML - Local Development</title>
|
|
35
35
|
<style>
|
|
36
36
|
* {
|
|
37
|
-
font-family:
|
|
37
|
+
font-family:
|
|
38
|
+
"Inter",
|
|
39
|
+
-apple-system,
|
|
40
|
+
BlinkMacSystemFont,
|
|
41
|
+
"Segoe UI",
|
|
42
|
+
"Roboto",
|
|
43
|
+
sans-serif;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
code,
|
|
41
|
-
|
|
46
|
+
code,
|
|
47
|
+
.CodeMirror {
|
|
48
|
+
font-family: "JetBrains Mono", "Monaco", "Consolas", monospace;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
.CodeMirror {
|
|
@@ -63,9 +70,10 @@
|
|
|
63
70
|
background-clip: text;
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
|
|
67
73
|
.editor-shadow {
|
|
68
|
-
box-shadow:
|
|
74
|
+
box-shadow:
|
|
75
|
+
0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
|
76
|
+
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
69
77
|
}
|
|
70
78
|
</style>
|
|
71
79
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
@@ -73,44 +81,61 @@
|
|
|
73
81
|
<body class="bg-gray-50">
|
|
74
82
|
<!-- Main Editor Section -->
|
|
75
83
|
<div class="w-full h-screen p-4">
|
|
76
|
-
<div class="grid grid-cols-
|
|
84
|
+
<div class="grid grid-cols-3 gap-4 h-full" id="diagram1">
|
|
77
85
|
<!-- Editor Panel -->
|
|
78
|
-
<div
|
|
79
|
-
|
|
86
|
+
<div
|
|
87
|
+
class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full"
|
|
88
|
+
>
|
|
89
|
+
<div
|
|
90
|
+
class="bg-gray-800 text-white px-4 py-3 flex items-center justify-between"
|
|
91
|
+
>
|
|
80
92
|
<h3 class="font-medium">Editor</h3>
|
|
81
93
|
<div class="flex space-x-2">
|
|
82
|
-
<button
|
|
94
|
+
<button
|
|
95
|
+
onclick="loadExample('basic')"
|
|
96
|
+
class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
|
|
97
|
+
>
|
|
83
98
|
Basic
|
|
84
99
|
</button>
|
|
85
|
-
<button
|
|
100
|
+
<button
|
|
101
|
+
onclick="loadExample('advanced')"
|
|
102
|
+
class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
|
|
103
|
+
>
|
|
86
104
|
Advanced
|
|
87
105
|
</button>
|
|
88
|
-
<button
|
|
106
|
+
<button
|
|
107
|
+
onclick="clearEditor()"
|
|
108
|
+
class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
|
|
109
|
+
>
|
|
89
110
|
Clear
|
|
90
111
|
</button>
|
|
91
|
-
<button
|
|
112
|
+
<button
|
|
113
|
+
onclick="exportDiagram()"
|
|
114
|
+
class="text-sm bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition-colors"
|
|
115
|
+
>
|
|
92
116
|
Export PNG
|
|
93
117
|
</button>
|
|
94
118
|
</div>
|
|
95
119
|
</div>
|
|
96
|
-
<div style="height: calc(100% - 52px)
|
|
97
|
-
<textarea id="text" style="display: none
|
|
120
|
+
<div style="height: calc(100% - 52px)">
|
|
121
|
+
<textarea id="text" style="display: none"></textarea>
|
|
98
122
|
</div>
|
|
99
123
|
</div>
|
|
100
124
|
|
|
101
125
|
<!-- Preview Panel -->
|
|
102
|
-
<div
|
|
126
|
+
<div
|
|
127
|
+
class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full col-span-2"
|
|
128
|
+
>
|
|
103
129
|
<div class="bg-gray-800 text-white px-4 py-3">
|
|
104
130
|
<h3 class="font-medium">Preview</h3>
|
|
105
131
|
</div>
|
|
106
|
-
<div style="height: calc(100% - 52px); overflow: auto
|
|
132
|
+
<div style="height: calc(100% - 52px); overflow: auto" class="p-4">
|
|
107
133
|
<pre class="zenuml" style="margin: 0"></pre>
|
|
108
134
|
</div>
|
|
109
135
|
</div>
|
|
110
136
|
</div>
|
|
111
137
|
</div>
|
|
112
138
|
|
|
113
|
-
|
|
114
139
|
<script type="module">
|
|
115
140
|
import { waitUntil, debounce } from "./src/utils.ts";
|
|
116
141
|
import { createConfig } from "./src/config.ts";
|
|
@@ -172,29 +197,29 @@ WebApp -> PaymentService: Process payment
|
|
|
172
197
|
PaymentService --> WebApp: Payment confirmed
|
|
173
198
|
WebApp -> Database: Create order
|
|
174
199
|
Database --> WebApp: Order created
|
|
175
|
-
WebApp --> Customer: Order confirmation
|
|
200
|
+
WebApp --> Customer: Order confirmation`,
|
|
176
201
|
};
|
|
177
202
|
|
|
178
203
|
// Global functions
|
|
179
|
-
window.loadExample = function(type) {
|
|
204
|
+
window.loadExample = function (type) {
|
|
180
205
|
editor.setValue(examples[type] || examples.basic);
|
|
181
206
|
};
|
|
182
207
|
|
|
183
|
-
window.clearEditor = function() {
|
|
184
|
-
editor.setValue(
|
|
208
|
+
window.clearEditor = function () {
|
|
209
|
+
editor.setValue("");
|
|
185
210
|
};
|
|
186
211
|
|
|
187
|
-
window.exportDiagram = async function() {
|
|
188
|
-
const element = document.querySelector(
|
|
212
|
+
window.exportDiagram = async function () {
|
|
213
|
+
const element = document.querySelector(".zenuml");
|
|
189
214
|
if (element) {
|
|
190
215
|
try {
|
|
191
216
|
const dataUrl = await toPng(element);
|
|
192
|
-
const link = document.createElement(
|
|
193
|
-
link.download =
|
|
217
|
+
const link = document.createElement("a");
|
|
218
|
+
link.download = "sequence-diagram.png";
|
|
194
219
|
link.href = dataUrl;
|
|
195
220
|
link.click();
|
|
196
221
|
} catch (error) {
|
|
197
|
-
console.error(
|
|
222
|
+
console.error("Failed to export diagram:", error);
|
|
198
223
|
}
|
|
199
224
|
}
|
|
200
225
|
};
|
|
@@ -209,4 +234,4 @@ WebApp --> Customer: Order confirmation`
|
|
|
209
234
|
</script>
|
|
210
235
|
<script type="module" src="/src/main.tsx"></script>
|
|
211
236
|
</body>
|
|
212
|
-
</html>
|
|
237
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenuml/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.44.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
"build:gh-pages": "bun run --bun vite build --mode gh-pages",
|
|
14
14
|
"build": "bun run --bun vite build -c vite.config.lib.ts",
|
|
15
15
|
"test": "bun test src test/unit",
|
|
16
|
-
"pw": "playwright test",
|
|
16
|
+
"pw": "playwright test --reporter=list",
|
|
17
17
|
"pw:ci": "playwright test",
|
|
18
|
-
"
|
|
18
|
+
"measure:layout": "COLLECT_LAYOUT_METRICS=1 playwright test tests/layout-metrics.spec.ts --reporter=line --project=chromium",
|
|
19
|
+
"snapshots:dual": "node scripts/snapshot-dual.js",
|
|
20
|
+
"pw:update": "playwright test --update-snapshots --reporter=list",
|
|
19
21
|
"pw:update-ci": "playwright test --update-snapshots --reporter=github",
|
|
20
22
|
"pw:ui": "playwright test --ui",
|
|
21
23
|
"pw:smoke": "playwright test smoke",
|
|
@@ -53,12 +55,6 @@
|
|
|
53
55
|
"autoprefixer": {}
|
|
54
56
|
}
|
|
55
57
|
},
|
|
56
|
-
"pnpm": {
|
|
57
|
-
"overrides": {
|
|
58
|
-
"react": "^19.0.0",
|
|
59
|
-
"react-dom": "^19.0.0"
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
58
|
"dependencies": {
|
|
63
59
|
"@floating-ui/react": "^0.27.8",
|
|
64
60
|
"@headlessui/react": "^2.2.1",
|
|
@@ -112,6 +108,8 @@
|
|
|
112
108
|
"happy-dom": "^18.0.1",
|
|
113
109
|
"jsdom": "^26.1.0",
|
|
114
110
|
"less": "^4.3.0",
|
|
111
|
+
"pixelmatch": "^7.1.0",
|
|
112
|
+
"pngjs": "^7.0.0",
|
|
115
113
|
"postcss": "^8.5.3",
|
|
116
114
|
"prettier": "3.5.3",
|
|
117
115
|
"rollup-plugin-visualizer": "^6.0.5",
|
package/playwright.config.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { defineConfig, devices } from "@playwright/test";
|
|
2
2
|
|
|
3
|
+
const PORT = Number(process.env.PORT) || 8080;
|
|
4
|
+
const BASE_URL = `http://127.0.0.1:${PORT}`;
|
|
5
|
+
|
|
3
6
|
export default defineConfig({
|
|
4
7
|
testDir: "./tests",
|
|
5
8
|
fullyParallel: true,
|
|
@@ -11,7 +14,7 @@ export default defineConfig({
|
|
|
11
14
|
? [["github"], ["html", { open: "never", outputFolder: "playwright-report" }]]
|
|
12
15
|
: [["html", { outputFolder: "playwright-report" }]],
|
|
13
16
|
use: {
|
|
14
|
-
baseURL:
|
|
17
|
+
baseURL: BASE_URL,
|
|
15
18
|
trace: "on-first-retry",
|
|
16
19
|
screenshot: "only-on-failure",
|
|
17
20
|
},
|
|
@@ -29,8 +32,8 @@ export default defineConfig({
|
|
|
29
32
|
},
|
|
30
33
|
],
|
|
31
34
|
webServer: {
|
|
32
|
-
command:
|
|
33
|
-
url:
|
|
35
|
+
command: `bun run dev -- --port=${PORT} --strictPort`,
|
|
36
|
+
url: BASE_URL,
|
|
34
37
|
reuseExistingServer: !process.env.CI,
|
|
35
38
|
timeout: 120 * 1000,
|
|
36
39
|
},
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generate two sets of Playwright snapshots (server & browser modes) and a diff overlay.
|
|
3
|
+
// Usage: node scripts/snapshot-dual.js -- tests/creation.spec.ts
|
|
4
|
+
|
|
5
|
+
const { spawnSync } = require("node:child_process");
|
|
6
|
+
const fs = require("node:fs/promises");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const { PNG } = require("pngjs");
|
|
9
|
+
const pixelmatchModule = require("pixelmatch");
|
|
10
|
+
const pixelmatch = pixelmatchModule.default || pixelmatchModule;
|
|
11
|
+
|
|
12
|
+
const repoRoot = path.resolve(__dirname, "..");
|
|
13
|
+
const testsArg = process.argv.slice(2);
|
|
14
|
+
const tmpRoot = path.join(repoRoot, "tmp", "snapshots-dual");
|
|
15
|
+
const paths = {
|
|
16
|
+
original: path.join(tmpRoot, "original"),
|
|
17
|
+
server: path.join(tmpRoot, "server"),
|
|
18
|
+
browser: path.join(tmpRoot, "browser"),
|
|
19
|
+
diff: path.join(tmpRoot, "diff"),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function snapshotsDirToSpecPath(dirName) {
|
|
23
|
+
return path.join("tests", dirName.replace(/-snapshots$/, ""));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function fileNameToTestName(file) {
|
|
27
|
+
return file.replace(/\.png$/, "").replace(/-chromium.*$/, "");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function ensureDir(dir) {
|
|
31
|
+
await fs.mkdir(dir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function listSnapshotDirs() {
|
|
35
|
+
const testsDir = path.join(repoRoot, "tests");
|
|
36
|
+
const entries = await fs.readdir(testsDir, { withFileTypes: true });
|
|
37
|
+
return entries
|
|
38
|
+
.filter((e) => e.isDirectory() && e.name.endsWith("-snapshots"))
|
|
39
|
+
.map((e) => path.join(testsDir, e.name));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function copySnapshotDirs(destRoot) {
|
|
43
|
+
const dirs = await listSnapshotDirs();
|
|
44
|
+
await ensureDir(destRoot);
|
|
45
|
+
for (const dir of dirs) {
|
|
46
|
+
const target = path.join(destRoot, path.basename(dir));
|
|
47
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
48
|
+
await fs.cp(dir, target, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function runPlaywright(mode) {
|
|
53
|
+
const env = { ...process.env, VERTICAL_MODE: mode };
|
|
54
|
+
const args = ["playwright", "test", "--update-snapshots", ...testsArg];
|
|
55
|
+
const result = spawnSync("npx", args, {
|
|
56
|
+
cwd: repoRoot,
|
|
57
|
+
env,
|
|
58
|
+
stdio: "inherit",
|
|
59
|
+
});
|
|
60
|
+
if (result.status !== 0) {
|
|
61
|
+
throw new Error(`Playwright failed in ${mode} mode`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function diffImages() {
|
|
66
|
+
const differences = [];
|
|
67
|
+
await ensureDir(paths.diff);
|
|
68
|
+
const serverDirs = await fs.readdir(paths.server, { withFileTypes: true });
|
|
69
|
+
for (const dirent of serverDirs) {
|
|
70
|
+
if (!dirent.isDirectory()) continue;
|
|
71
|
+
const baseName = dirent.name;
|
|
72
|
+
const serverDir = path.join(paths.server, baseName);
|
|
73
|
+
const browserDir = path.join(paths.browser, baseName);
|
|
74
|
+
const files = await fs.readdir(serverDir);
|
|
75
|
+
for (const file of files) {
|
|
76
|
+
if (!file.endsWith(".png")) continue;
|
|
77
|
+
const serverPngPath = path.join(serverDir, file);
|
|
78
|
+
const browserPngPath = path.join(browserDir, file);
|
|
79
|
+
try {
|
|
80
|
+
const [serverBuf, browserBuf] = await Promise.all([
|
|
81
|
+
fs.readFile(serverPngPath),
|
|
82
|
+
fs.readFile(browserPngPath),
|
|
83
|
+
]);
|
|
84
|
+
const serverImg = PNG.sync.read(serverBuf);
|
|
85
|
+
const browserImg = PNG.sync.read(browserBuf);
|
|
86
|
+
if (
|
|
87
|
+
serverImg.width !== browserImg.width ||
|
|
88
|
+
serverImg.height !== browserImg.height
|
|
89
|
+
) {
|
|
90
|
+
console.warn(`Skipping ${file}: dimension mismatch`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const diff = new PNG({
|
|
94
|
+
width: serverImg.width,
|
|
95
|
+
height: serverImg.height,
|
|
96
|
+
});
|
|
97
|
+
const diffPixels = pixelmatch(
|
|
98
|
+
serverImg.data,
|
|
99
|
+
browserImg.data,
|
|
100
|
+
diff.data,
|
|
101
|
+
serverImg.width,
|
|
102
|
+
serverImg.height,
|
|
103
|
+
{ threshold: 0.1 },
|
|
104
|
+
);
|
|
105
|
+
const outDir = path.join(paths.diff, baseName);
|
|
106
|
+
await ensureDir(outDir);
|
|
107
|
+
const diffFileName = file.replace(/\.png$/, ".diff.png");
|
|
108
|
+
await fs.writeFile(
|
|
109
|
+
path.join(outDir, diffFileName),
|
|
110
|
+
PNG.sync.write(diff),
|
|
111
|
+
);
|
|
112
|
+
if (diffPixels > 0) {
|
|
113
|
+
differences.push({
|
|
114
|
+
snapshotDir: baseName,
|
|
115
|
+
file,
|
|
116
|
+
diffPixels,
|
|
117
|
+
specPath: snapshotsDirToSpecPath(baseName),
|
|
118
|
+
testName: fileNameToTestName(file),
|
|
119
|
+
diffPath: path.join(paths.diff, baseName, diffFileName),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.warn(`Diff failed for ${baseName}/${file}: ${err.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return differences;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
await ensureDir(tmpRoot);
|
|
132
|
+
// const snapshotDirs = await listSnapshotDirs();
|
|
133
|
+
// await fs.rm(paths.original, { recursive: true, force: true });
|
|
134
|
+
// await ensureDir(paths.original);
|
|
135
|
+
// backup existing snapshots
|
|
136
|
+
// await copySnapshotDirs(paths.original);
|
|
137
|
+
|
|
138
|
+
// browser mode
|
|
139
|
+
runPlaywright("browser");
|
|
140
|
+
await fs.rm(paths.browser, { recursive: true, force: true });
|
|
141
|
+
await copySnapshotDirs(paths.browser);
|
|
142
|
+
|
|
143
|
+
// server mode
|
|
144
|
+
runPlaywright("server");
|
|
145
|
+
await fs.rm(paths.server, { recursive: true, force: true });
|
|
146
|
+
await copySnapshotDirs(paths.server);
|
|
147
|
+
|
|
148
|
+
// generate diffs
|
|
149
|
+
const differences = await diffImages();
|
|
150
|
+
console.log(`Snapshots saved under ${paths.server} and ${paths.browser}`);
|
|
151
|
+
console.log(`Diff overlays saved under ${paths.diff}`);
|
|
152
|
+
if (differences.length === 0) {
|
|
153
|
+
console.log("All snapshots match between server and browser modes.");
|
|
154
|
+
} else {
|
|
155
|
+
console.log("Snapshots with differences:");
|
|
156
|
+
const sorted = differences.sort((a, b) => {
|
|
157
|
+
if (a.specPath === b.specPath)
|
|
158
|
+
return a.testName.localeCompare(b.testName);
|
|
159
|
+
return a.specPath.localeCompare(b.specPath);
|
|
160
|
+
});
|
|
161
|
+
for (const diff of sorted) {
|
|
162
|
+
const relDiffPath = path.relative(repoRoot, diff.diffPath);
|
|
163
|
+
console.log(
|
|
164
|
+
`- ${diff.specPath} (test \"${diff.testName}\"): ${diff.snapshotDir}/${diff.file} -> ${diff.diffPixels} differing pixels [${relDiffPath}]`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main().catch((err) => {
|
|
171
|
+
console.error(err);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
});
|