web-corders-vrt 0.1.1

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.
@@ -0,0 +1,147 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { PNG } from "pngjs";
3
+ import { detectDiffRegions } from "../src/core/region-detector.js";
4
+
5
+ /**
6
+ * pixelmatchが出力するような diff画像を手動で生成する。
7
+ * 指定領域を赤 (255, 0, 0) にし、残りを半透明にする。
8
+ */
9
+ function createDiffImage(
10
+ width: number,
11
+ height: number,
12
+ regions: Array<{ x: number; y: number; w: number; h: number }>,
13
+ ): Buffer {
14
+ const png = new PNG({ width, height });
15
+
16
+ // 背景を半透明に
17
+ for (let i = 0; i < png.data.length; i += 4) {
18
+ png.data[i] = 0;
19
+ png.data[i + 1] = 0;
20
+ png.data[i + 2] = 0;
21
+ png.data[i + 3] = 25; // alpha 0.1 * 255 ≈ 25
22
+ }
23
+
24
+ // 差分領域を赤にする
25
+ for (const region of regions) {
26
+ for (let y = region.y; y < region.y + region.h && y < height; y++) {
27
+ for (let x = region.x; x < region.x + region.w && x < width; x++) {
28
+ const idx = (y * width + x) * 4;
29
+ png.data[idx] = 255; // R
30
+ png.data[idx + 1] = 0; // G
31
+ png.data[idx + 2] = 0; // B
32
+ png.data[idx + 3] = 255; // A
33
+ }
34
+ }
35
+ }
36
+
37
+ return PNG.sync.write(png);
38
+ }
39
+
40
+ describe("detectDiffRegions", () => {
41
+ it("差分がない画像からは領域を検出しない", () => {
42
+ const png = new PNG({ width: 100, height: 100 });
43
+ for (let i = 0; i < png.data.length; i += 4) {
44
+ png.data[i] = 0;
45
+ png.data[i + 1] = 0;
46
+ png.data[i + 2] = 0;
47
+ png.data[i + 3] = 25;
48
+ }
49
+ const buffer = PNG.sync.write(png);
50
+
51
+ const regions = detectDiffRegions(buffer);
52
+ expect(regions).toHaveLength(0);
53
+ });
54
+
55
+ it("単一の差分領域を正しく検出する", () => {
56
+ const diffImage = createDiffImage(200, 200, [
57
+ { x: 50, y: 50, w: 40, h: 30 },
58
+ ]);
59
+
60
+ const regions = detectDiffRegions(diffImage, { minRegionSize: 1 });
61
+
62
+ expect(regions.length).toBeGreaterThanOrEqual(1);
63
+ const region = regions[0];
64
+
65
+ // bounding box が概ね正しい
66
+ expect(region.boundingBox.x).toBeGreaterThanOrEqual(50);
67
+ expect(region.boundingBox.y).toBeGreaterThanOrEqual(50);
68
+ expect(region.boundingBox.x + region.boundingBox.width).toBeLessThanOrEqual(
69
+ 100,
70
+ );
71
+ expect(
72
+ region.boundingBox.y + region.boundingBox.height,
73
+ ).toBeLessThanOrEqual(90);
74
+ });
75
+
76
+ it("離れた2つの差分領域を別々に検出する", () => {
77
+ const diffImage = createDiffImage(400, 400, [
78
+ { x: 10, y: 10, w: 30, h: 30 },
79
+ { x: 350, y: 350, w: 30, h: 30 },
80
+ ]);
81
+
82
+ const regions = detectDiffRegions(diffImage, {
83
+ minRegionSize: 1,
84
+ mergingDistance: 20, // 離れてるのでマージされない
85
+ });
86
+
87
+ expect(regions.length).toBe(2);
88
+ });
89
+
90
+ it("近い差分領域はマージされる", () => {
91
+ const diffImage = createDiffImage(200, 200, [
92
+ { x: 10, y: 10, w: 20, h: 20 },
93
+ { x: 40, y: 10, w: 20, h: 20 }, // 10px しか離れてない
94
+ ]);
95
+
96
+ const regions = detectDiffRegions(diffImage, {
97
+ minRegionSize: 1,
98
+ mergingDistance: 50, // 50px 以下はマージ
99
+ });
100
+
101
+ expect(regions.length).toBe(1);
102
+ });
103
+
104
+ it("locationHint が正しく設定される", () => {
105
+ // ページ上部にフル幅の差分
106
+ const diffImage = createDiffImage(1440, 900, [
107
+ { x: 0, y: 10, w: 1440, h: 60 },
108
+ ]);
109
+
110
+ const regions = detectDiffRegions(diffImage, { minRegionSize: 1 });
111
+
112
+ expect(regions.length).toBeGreaterThanOrEqual(1);
113
+ const hint = regions[0].locationHint;
114
+ expect(hint.verticalPosition).toBe("top");
115
+ expect(hint.horizontalPosition).toBe("full-width");
116
+ expect(hint.estimatedElement).toContain("header");
117
+ });
118
+
119
+ it("小さすぎる差分はminRegionSizeでフィルタされる", () => {
120
+ const diffImage = createDiffImage(200, 200, [
121
+ { x: 50, y: 50, w: 2, h: 2 }, // 4ピクセルしかない
122
+ ]);
123
+
124
+ const regions = detectDiffRegions(diffImage, { minRegionSize: 10 });
125
+ expect(regions).toHaveLength(0);
126
+ });
127
+
128
+ it("IDが大きい差分順に振られる", () => {
129
+ const diffImage = createDiffImage(400, 400, [
130
+ { x: 10, y: 10, w: 10, h: 10 }, // 100px
131
+ { x: 300, y: 300, w: 50, h: 50 }, // 2500px
132
+ ]);
133
+
134
+ const regions = detectDiffRegions(diffImage, {
135
+ minRegionSize: 1,
136
+ mergingDistance: 20,
137
+ });
138
+
139
+ expect(regions.length).toBe(2);
140
+ // 大きい差分が先(id=1)
141
+ expect(regions[0].diffPixelCount).toBeGreaterThan(
142
+ regions[1].diffPixelCount,
143
+ );
144
+ expect(regions[0].id).toBe(1);
145
+ expect(regions[1].id).toBe(2);
146
+ });
147
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": ".",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true
17
+ },
18
+ "include": ["src/**/*.ts", "src/types/**/*.d.ts", "bin/**/*.ts"],
19
+ "exclude": ["node_modules", "dist", "test"]
20
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ "bin/vrt": "bin/vrt.ts",
6
+ },
7
+ format: ["esm"],
8
+ target: "node18",
9
+ sourcemap: true,
10
+ clean: true,
11
+ splitting: false,
12
+ banner: {
13
+ js: "#!/usr/bin/env node",
14
+ },
15
+ });