flexysnap 0.1.3-alpha.1 → 0.1.3-alpha.2
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/package.json +1 -1
- package/src/annotateWireframe.js +27 -4
- package/src/wireframeUtils.js +50 -15
package/package.json
CHANGED
package/src/annotateWireframe.js
CHANGED
|
@@ -16,6 +16,16 @@ function applyGrayscale(ctx, width, height) {
|
|
|
16
16
|
ctx.putImageData(imageData, 0, 0);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
function getScrollOffset(wireframe) {
|
|
20
|
+
const extractionScrollPosition = wireframe.scrollPosition || { x: 0, y: 0 };
|
|
21
|
+
const screenshotScrollPosition = wireframe.screenshotScrollPosition || extractionScrollPosition;
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
offsetX: extractionScrollPosition.x - screenshotScrollPosition.x,
|
|
25
|
+
offsetY: extractionScrollPosition.y - screenshotScrollPosition.y
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
20
30
|
if (!fs.existsSync(wireframeFile)) {
|
|
21
31
|
console.error(`Wireframe file not found: ${wireframeFile}`);
|
|
@@ -40,6 +50,12 @@ async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
|
40
50
|
ctx.drawImage(screenshot, 0, 0);
|
|
41
51
|
applyGrayscale(ctx, width, height);
|
|
42
52
|
|
|
53
|
+
const { offsetX, offsetY } = getScrollOffset(wireframe);
|
|
54
|
+
|
|
55
|
+
if (offsetX !== 0 || offsetY !== 0) {
|
|
56
|
+
console.log(`Compensating for scroll drift between extraction and screenshot: offsetX=${offsetX}, offsetY=${offsetY}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
const colors = {
|
|
44
60
|
box: { fill: 'rgba(220, 160, 40, 0.4)', stroke: '#E6A500' },
|
|
45
61
|
image: { fill: 'rgba(70, 180, 120, 0.4)', stroke: '#2EBC6F' },
|
|
@@ -52,16 +68,23 @@ async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
function drawBoundingBox(rect, colorKey, dashed = false) {
|
|
55
|
-
const
|
|
56
|
-
|
|
71
|
+
const adjustedRect = {
|
|
72
|
+
left: rect.left + offsetX,
|
|
73
|
+
right: rect.right + offsetX,
|
|
74
|
+
top: rect.top + offsetY,
|
|
75
|
+
bottom: rect.bottom + offsetY
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const boxWidth = adjustedRect.right - adjustedRect.left;
|
|
79
|
+
const boxHeight = adjustedRect.bottom - adjustedRect.top;
|
|
57
80
|
const color = colors[colorKey];
|
|
58
81
|
|
|
59
82
|
ctx.fillStyle = color.fill;
|
|
60
|
-
ctx.fillRect(
|
|
83
|
+
ctx.fillRect(adjustedRect.left, adjustedRect.top, boxWidth, boxHeight);
|
|
61
84
|
ctx.strokeStyle = color.stroke;
|
|
62
85
|
ctx.lineWidth = 2;
|
|
63
86
|
ctx.setLineDash(dashed ? [6, 4] : []);
|
|
64
|
-
ctx.strokeRect(
|
|
87
|
+
ctx.strokeRect(adjustedRect.left, adjustedRect.top, boxWidth, boxHeight);
|
|
65
88
|
ctx.setLineDash([]);
|
|
66
89
|
}
|
|
67
90
|
|
package/src/wireframeUtils.js
CHANGED
|
@@ -4,6 +4,8 @@ import { logTimestamp } from './testUtils.js';
|
|
|
4
4
|
import { loadImage, createCanvas } from 'canvas';
|
|
5
5
|
import { areWireframesStable } from './wireframeStability.js';
|
|
6
6
|
|
|
7
|
+
const FLEXYSNAP_ELEMENT_ID_ATTRIBUTE = 'data-flexysnap-id';
|
|
8
|
+
|
|
7
9
|
async function getRGBHistogramFromBuffer(buffer) {
|
|
8
10
|
const image = await loadImage(buffer);
|
|
9
11
|
const canvas = createCanvas(image.width, image.height);
|
|
@@ -37,7 +39,7 @@ function delay(milliseconds) {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
async function extractWireframe(page, elementGroups) {
|
|
40
|
-
const wireframeData = await page.evaluate(async ({elementGroups}) => {
|
|
42
|
+
const wireframeData = await page.evaluate(async ({elementGroups, elementIdAttribute}) => {
|
|
41
43
|
|
|
42
44
|
function createBoundingRect(element) {
|
|
43
45
|
const rect = element.getBoundingClientRect();
|
|
@@ -80,7 +82,8 @@ async function extractWireframe(page, elementGroups) {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
|
|
83
|
-
for (
|
|
85
|
+
for (let groupIndex = 0; groupIndex < elementGroups.length; groupIndex++) {
|
|
86
|
+
const elementGroup = elementGroups[groupIndex];
|
|
84
87
|
elementGroup.strictPosition = elementGroup.strictPosition !== false;
|
|
85
88
|
elementGroup.elements = [];
|
|
86
89
|
|
|
@@ -159,18 +162,26 @@ async function extractWireframe(page, elementGroups) {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
const elementData = {
|
|
163
166
|
index: index,
|
|
164
167
|
boundingRect: createBoundingRect(element),
|
|
165
168
|
texts: texts,
|
|
166
169
|
type: elementGroup.type
|
|
167
|
-
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (elementGroup.type === 'image') {
|
|
173
|
+
const elementId = `fsnap-g${groupIndex}-e${index}`;
|
|
174
|
+
element.setAttribute(elementIdAttribute, elementId);
|
|
175
|
+
elementData.elementId = elementId;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
elementGroup.elements.push(elementData);
|
|
168
179
|
index += 1;
|
|
169
180
|
}
|
|
170
181
|
}
|
|
171
182
|
|
|
172
183
|
return elementGroups;
|
|
173
|
-
}, {elementGroups});
|
|
184
|
+
}, {elementGroups, elementIdAttribute: FLEXYSNAP_ELEMENT_ID_ATTRIBUTE});
|
|
174
185
|
|
|
175
186
|
const scrollPosition = await page.evaluate(() => ({
|
|
176
187
|
x: window.scrollX,
|
|
@@ -179,8 +190,8 @@ async function extractWireframe(page, elementGroups) {
|
|
|
179
190
|
|
|
180
191
|
for (const elementGroup of wireframeData) {
|
|
181
192
|
for (const element of elementGroup.elements) {
|
|
182
|
-
if (element.type === 'image') {
|
|
183
|
-
const locator = page.locator(
|
|
193
|
+
if (element.type === 'image' && element.elementId) {
|
|
194
|
+
const locator = page.locator(`[${FLEXYSNAP_ELEMENT_ID_ATTRIBUTE}="${element.elementId}"]`);
|
|
184
195
|
await locator.waitFor({state: 'visible'});
|
|
185
196
|
|
|
186
197
|
try {
|
|
@@ -199,14 +210,28 @@ async function extractWireframe(page, elementGroups) {
|
|
|
199
210
|
} catch {
|
|
200
211
|
element.type = 'box'
|
|
201
212
|
}
|
|
213
|
+
|
|
214
|
+
delete element.elementId;
|
|
202
215
|
}
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
|
|
206
|
-
await page.evaluate(({scrollPosition}) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
219
|
+
await page.evaluate(({scrollPosition, elementIdAttribute}) => {
|
|
220
|
+
for (const el of document.querySelectorAll(`[${elementIdAttribute}]`)) {
|
|
221
|
+
el.removeAttribute(elementIdAttribute);
|
|
222
|
+
}
|
|
223
|
+
window.scrollTo({top: scrollPosition.y, left: scrollPosition.x, behavior: 'instant'});
|
|
224
|
+
}, {scrollPosition, elementIdAttribute: FLEXYSNAP_ELEMENT_ID_ATTRIBUTE});
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
await page.waitForFunction((expectedScrollPosition) => {
|
|
228
|
+
return window.scrollX === expectedScrollPosition.x && window.scrollY === expectedScrollPosition.y;
|
|
229
|
+
}, scrollPosition, {timeout: 2000});
|
|
230
|
+
} catch {
|
|
231
|
+
logTimestamp('Scroll position did not settle back to the extraction position.');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {wireframeData, scrollPosition};
|
|
210
235
|
}
|
|
211
236
|
|
|
212
237
|
function cloneElementGroups(elementGroups) {
|
|
@@ -216,17 +241,20 @@ function cloneElementGroups(elementGroups) {
|
|
|
216
241
|
async function extractStableWireframe(page, elementGroups, retryDelay, maxRetryCount) {
|
|
217
242
|
let previousWireframeData = null;
|
|
218
243
|
let currentWireframeData = null;
|
|
244
|
+
let currentScrollPosition = null;
|
|
219
245
|
|
|
220
246
|
for (let attempt = 0; attempt < maxRetryCount; attempt++) {
|
|
221
247
|
const extractionStartTime = Date.now();
|
|
222
|
-
|
|
248
|
+
const extractionResult = await extractWireframe(page, cloneElementGroups(elementGroups));
|
|
249
|
+
currentWireframeData = extractionResult.wireframeData;
|
|
250
|
+
currentScrollPosition = extractionResult.scrollPosition;
|
|
223
251
|
const extractionDuration = Date.now() - extractionStartTime;
|
|
224
252
|
logTimestamp(`Wireframe candidate captured after ${extractionDuration/1000} seconds.`)
|
|
225
253
|
|
|
226
254
|
if (previousWireframeData !== null &&
|
|
227
255
|
areWireframesStable(previousWireframeData, currentWireframeData)) {
|
|
228
256
|
logTimestamp(`Wireframe stabilized after ${attempt + 1} extraction(s)`);
|
|
229
|
-
return currentWireframeData;
|
|
257
|
+
return {wireframeData: currentWireframeData, scrollPosition: currentScrollPosition};
|
|
230
258
|
}
|
|
231
259
|
|
|
232
260
|
previousWireframeData = currentWireframeData;
|
|
@@ -239,22 +267,29 @@ async function extractStableWireframe(page, elementGroups, retryDelay, maxRetryC
|
|
|
239
267
|
}
|
|
240
268
|
|
|
241
269
|
logTimestamp(`Wireframe did not stabilize within ${maxRetryCount} extraction(s)`);
|
|
242
|
-
return currentWireframeData;
|
|
270
|
+
return {wireframeData: currentWireframeData, scrollPosition: currentScrollPosition};
|
|
243
271
|
}
|
|
244
272
|
|
|
245
273
|
async function expectWireframe(page, elementGroups, configName, outputFile, outputName, retryDelay = 1000, maxRetryCount = 10) {
|
|
246
274
|
logTimestamp(`Starting wireframe capture for: ${outputFile}`);
|
|
247
|
-
const wireframeData = await extractStableWireframe(page, elementGroups, retryDelay, maxRetryCount);
|
|
275
|
+
const { wireframeData, scrollPosition } = await extractStableWireframe(page, elementGroups, retryDelay, maxRetryCount);
|
|
248
276
|
|
|
249
277
|
const userType = process.env.USER_TYPE;
|
|
250
278
|
const deviceType = process.env.DEVICE_TYPE;
|
|
251
279
|
const testType = process.env.TEST_TYPE;
|
|
252
280
|
|
|
281
|
+
const screenshotScrollPosition = await page.evaluate(() => ({
|
|
282
|
+
x: window.scrollX,
|
|
283
|
+
y: window.scrollY
|
|
284
|
+
}));
|
|
285
|
+
|
|
253
286
|
const wireframeOutput = {
|
|
254
287
|
name: outputName,
|
|
255
288
|
timestamp: new Date().toISOString(),
|
|
256
289
|
deviceType: process.env.DEVICE_TYPE,
|
|
257
290
|
userType: process.env.USER_TYPE,
|
|
291
|
+
scrollPosition,
|
|
292
|
+
screenshotScrollPosition,
|
|
258
293
|
elementGroups: wireframeData
|
|
259
294
|
};
|
|
260
295
|
|