flexysnap 0.1.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/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # flexysnap
2
+
3
+ Structural wireframe snapshot testing for Playwright — built for e-commerce.
4
+
5
+ Traditional pixel-diffing tools flag every layout shift as a failure, even a 2px nudge from a font render or a shifting badge. Real webshops also have content that changes on purpose: cross-sell blocks, promo banners, stock counters. `flexysnap` takes a different approach. Instead of comparing raw pixels, it extracts a structural wireframe from the page — bounding boxes, text content, and image color histograms — then compares that wireframe against a baseline. You test what actually matters: is the layout intact, is the text correct, did an image visually change — without drowning in false positives from anti-aliasing noise.
6
+
7
+ ## Why flexysnap
8
+
9
+ - **Structural diffing** — captures element bounding boxes, text nodes, and image histograms instead of raw pixels, so sub-pixel font rendering and anti-aliasing never fail your suite.
10
+ - **Position tolerance** — element groups can be marked `strictPosition: false` to check size only, ignoring exact placement for content that legitimately moves.
11
+ - **Stability detection** — wireframes are re-captured until the layout settles, so lazy-loaded images, animations, and reflow don't produce flaky baselines.
12
+ - **Built on Playwright** — works with your existing Playwright config, fixtures, and test runner. No new browser automation layer to learn.
13
+ - **Rich HTML reports** — generate an interactive report with baseline/current image sliders, annotated wireframe overlays, and categorized differences (text, image, layout).
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install --save-dev flexysnap
19
+ npx playwright install
20
+ ```
21
+
22
+ ## Concepts
23
+
24
+ ### Element groups
25
+
26
+ You describe the page as a list of **element groups**. Each group targets a CSS selector and declares a type:
27
+
28
+ | Type | What it captures |
29
+ |---------|--------------------------------------------------------------|
30
+ | `box` | Bounding box only (layout/size) |
31
+ | `image` | Bounding box plus an RGB color histogram for visual comparison |
32
+ | `text` | Bounding box plus every visible direct text node inside it |
33
+
34
+ ```js
35
+ const elementGroups = [
36
+ { selector: '.hero-banner', type: 'image' },
37
+ { selector: '.product-title', type: 'text' },
38
+ { selector: '.cross-sell-carousel', type: 'box', strictPosition: false },
39
+ { selector: '.price', type: 'text', textIgnoreClasses: ['screen-reader-text'] }
40
+ ];
41
+ ```
42
+
43
+ - `strictPosition: false` — compare element size only, not exact position.
44
+ - `textIgnoreClasses` — skip text found inside elements carrying these class names.
45
+
46
+ ## Quick start
47
+
48
+ ### Capturing a wireframe
49
+
50
+ ```js
51
+ import { test } from '@playwright/test';
52
+ import { expectWireframe } from 'flexysnap/wireframeUtils.js';
53
+
54
+ const elementGroups = [
55
+ { selector: '.hero-banner', type: 'image' },
56
+ { selector: '.product-title', type: 'text' },
57
+ { selector: '.cross-sell-carousel', type: 'box', strictPosition: false }
58
+ ];
59
+
60
+ test('product page wireframe', async ({ page }) => {
61
+ await page.goto('https://example-shop.com/products/example-item');
62
+
63
+ await expectWireframe(
64
+ page,
65
+ elementGroups,
66
+ 'product-config', // config name
67
+ 'product-page', // output file basename
68
+ 'Product Page' // human-readable name
69
+ );
70
+ });
71
+ ```
72
+
73
+ `expectWireframe` captures a stable wireframe (re-sampling until the layout settles), writes a JSON wireframe and a PNG screenshot into:
74
+
75
+ ```
76
+ wireframes/test/<TEST_TYPE>/<config>/<DEVICE_TYPE>/<USER_TYPE>/
77
+ ```
78
+
79
+ ## How it works
80
+
81
+ 1. **Wireframe extraction** — `flexysnap` walks the DOM in the browser, collecting bounding boxes for each matched element, text nodes for `text` groups, and RGB histograms (via `sharp`) for `image` groups.
82
+ 2. **Stability loop** — the wireframe is captured repeatedly until consecutive captures are stable (element counts match, text is unchanged, and total bounding-box area drift stays under 3%). This defeats lazy loading and animation flakiness.
83
+ 3. **Baseline comparison** — element groups from the current run are paired with the baseline using nearest bounding-box matching. Each pair is diffed for layout shifts, text mismatches, and histogram (image) differences. Unmatched elements are reported as extra or missing.
84
+
85
+ ## Environment variables
86
+
87
+ Wireframe paths are namespaced by environment variables so the same tests can run across devices and user roles:
88
+
89
+ | Variable | Example | Purpose |
90
+ |---------------|--------------|----------------------------------|
91
+ | `TEST_TYPE` | `smoke` | Top-level test grouping |
92
+ | `TEST_CONFIG` | `production` | Configuration name |
93
+ | `DEVICE_TYPE` | `mobile` | Device / viewport identifier |
94
+ | `USER_TYPE` | `guest` | User role identifier |
95
+
96
+ ## Comparing against a baseline
97
+
98
+ The comparison suite reads baseline and current wireframes and asserts they match:
99
+
100
+ ```bash
101
+ TEST_TYPE=smoke TEST_CONFIG=production DEVICE_TYPE=mobile USER_TYPE=guest \
102
+ npx playwright test compare-wireframes.spec.js
103
+ ```
104
+
105
+ It reads from:
106
+
107
+ ```
108
+ wireframes/baseline/<TEST_TYPE>/<TEST_CONFIG>/<DEVICE_TYPE>/<USER_TYPE>/
109
+ wireframes/test/<TEST_TYPE>/<TEST_CONFIG>/<DEVICE_TYPE>/<USER_TYPE>/
110
+ ```
111
+
112
+ Each matched wireframe is diffed and re-written with annotated `differences`, then every difference is asserted as a soft failure so the whole page is reported at once.
113
+
114
+ ## Difference types
115
+
116
+ | Type | Meaning |
117
+ |------------------------|----------------------------------------------------|
118
+ | `layout_shift` | Bounding box moved beyond the position tolerance |
119
+ | `size_mismatch` | Size changed (position-tolerant groups) |
120
+ | `text_mismatch` | Text content differs between baseline and current |
121
+ | `extra_text` | Text present now but not in baseline |
122
+ | `missing_text` | Text present in baseline but not now |
123
+ | `histogram_difference` | Image content changed visually |
124
+ | `extra_element` | Element present now but not in baseline |
125
+ | `missing_element` | Element present in baseline but not now |
126
+
127
+ ## Annotating screenshots
128
+
129
+ Overlay the captured wireframe onto its screenshot to visualize what was checked and what differed:
130
+
131
+ ```bash
132
+ # annotate a single pair
133
+ node annotateWireframe.mjs wireframe.json screenshot.png
134
+
135
+ # annotate every matching .json/.png pair in a folder
136
+ node annotateWireframe.mjs ./wireframes/test/smoke/production/mobile/guest
137
+ ```
138
+
139
+ Boxes are color-coded: text (blue), image (green), box (amber), and error (red) for any element carrying differences. Dashed borders indicate position-tolerant groups. Output is written as `<name>_wireframe.png`.
140
+
141
+ ## Generating an HTML report
142
+
143
+ ```bash
144
+ node generate-report.js <baseline-folder> <current-folder> [output-file]
145
+ ```
146
+
147
+ Example:
148
+
149
+ ```bash
150
+ node generate-report.js \
151
+ wireframes/baseline/smoke/production/mobile/guest \
152
+ wireframes/test/smoke/production/mobile/guest \
153
+ report.html
154
+ ```
155
+
156
+ The report includes:
157
+
158
+ - Summary statistics (elements checked, total / text / image / layout differences) with click-to-jump navigation.
159
+ - A baseline↔current comparison slider (via cocoen) when both images are available.
160
+ - A global toggle between **original** and **annotated** screenshots.
161
+ - A per-wireframe list of detected differences, with empty sections auto-collapsed.
162
+
163
+ ## Roadmap
164
+
165
+ - [ ] Region auto-detection for common e-commerce patterns (cart, checkout, PDP)
166
+ - [ ] GitHub Actions annotation integration
167
+ - [ ] Plugin-change tracking integrated into the HTML report
168
+
169
+ ## Contributing
170
+
171
+ Issues and pull requests are welcome. Please open an issue before submitting large changes so we can discuss the approach first.
172
+
173
+ ## License
174
+
175
+ MIT
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "flexysnap",
3
+ "version": "0.1.0",
4
+ "description": "Flexible visual snapshot testing for Playwright, built for e-commerce.",
5
+ "keywords": [
6
+ "playwright",
7
+ "visual-testing",
8
+ "snapshot-testing",
9
+ "screenshot-testing",
10
+ "e-commerce",
11
+ "regression-testing",
12
+ "qa",
13
+ "testing"
14
+ ],
15
+ "homepage": "https://flexysnap.com",
16
+ "bugs": {
17
+ "url": "https://github.com/flexysnap/flexysnap/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/flexysnap/flexysnap.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Gabor Angyal",
25
+ "type": "module",
26
+ "main": "./src/index.js",
27
+ "exports": {
28
+ ".": "./src/index.js"
29
+ },
30
+ "bin": {
31
+ "flexysnap": "./src/cli.js"
32
+ },
33
+ "files": [
34
+ "src",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "scripts": {
42
+ "test": "playwright test",
43
+ "lint": "eslint src"
44
+ },
45
+ "dependencies": {
46
+ "@playwright/test": "^1.61.1",
47
+ "canvas": "^3.2.3",
48
+ "glob": "^13.0.6",
49
+ "playwright": "^1.61.1",
50
+ "sharp": "^0.35.3"
51
+ },
52
+ "devDependencies": {
53
+ "eslint": "^9.9.0"
54
+ }
55
+ }
package/src/cli.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * flexysnap CLI — placeholder implementation.
5
+ *
6
+ * Usage:
7
+ * npx flexysnap update Regenerate baseline snapshots
8
+ */
9
+
10
+ const [, , command] = process.argv;
11
+
12
+ switch (command) {
13
+ case 'update':
14
+ console.log('flexysnap: baseline update not implemented yet.');
15
+ break;
16
+ default:
17
+ console.log('flexysnap');
18
+ console.log('');
19
+ console.log('Usage:');
20
+ console.log(' flexysnap update Regenerate baseline snapshots');
21
+ break;
22
+ }
package/src/index.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * flexysnap — flexible visual snapshot testing for Playwright.
3
+ *
4
+ * This is a placeholder entry point. Replace with the real
5
+ * snapshot-comparison implementation.
6
+ */
7
+
8
+ /**
9
+ * Capture and compare a snapshot for the given Playwright page.
10
+ *
11
+ * @param {import('@playwright/test').Page} page
12
+ * @param {string} name - snapshot identifier, used for the stored baseline file
13
+ * @param {object} [options]
14
+ * @param {number} [options.tolerance=0.02] - allowed pixel-drift ratio (0-1)
15
+ * @param {Record<string, 'strict'|'presence'|'ignore'>} [options.regions] - per-selector comparison policy
16
+ */
17
+ export async function expectSnapshot(page, name, options = {}) {
18
+ const { tolerance = 0.02, regions = {} } = options;
19
+
20
+ // TODO: implement capture + diff logic (sharp/canvas-based comparison).
21
+ throw new Error(
22
+ `expectSnapshot("${name}") is not implemented yet. tolerance=${tolerance}, regions=${JSON.stringify(regions)}`
23
+ );
24
+ }
25
+
26
+ export default { expectSnapshot };