flexysnap 0.3.1 → 0.4.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/package.json +1 -1
- package/src/annotateWireframe.js +95 -21
- package/src/wireframeComparison.js +39 -30
- package/src/wireframeUtils.js +4 -4
package/package.json
CHANGED
package/src/annotateWireframe.js
CHANGED
|
@@ -60,10 +60,7 @@ async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
|
60
60
|
box: { fill: 'rgba(220, 160, 40, 0.4)', stroke: '#E6A500' },
|
|
61
61
|
image: { fill: 'rgba(70, 180, 120, 0.4)', stroke: '#2EBC6F' },
|
|
62
62
|
text: { fill: 'rgba(50, 130, 190, 0.4)', stroke: '#0066CC' },
|
|
63
|
-
error: { fill: 'rgba(211, 47, 47, 0.4)', stroke: '#D32F2F' }
|
|
64
|
-
missing: { fill: 'rgba(233, 30, 140, 0.4)', stroke: '#D32F2F' },
|
|
65
|
-
extra: { fill: 'rgba(156, 39, 176, 0.4)', stroke: '#D32F2F' },
|
|
66
|
-
shift: { fill: 'rgba(121, 85, 72, 0.4)', stroke: '#D32F2F' }
|
|
63
|
+
error: { fill: 'rgba(211, 47, 47, 0.4)', stroke: '#D32F2F' }
|
|
67
64
|
};
|
|
68
65
|
|
|
69
66
|
function determineColorKey(obj, defaultKey) {
|
|
@@ -71,24 +68,90 @@ async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
|
71
68
|
return defaultKey;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
const
|
|
71
|
+
const hasError = obj.differences.some(difference => difference.kind === 'error');
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
return hasError ? 'error' : defaultKey;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function hasDifferenceType(differences, types) {
|
|
77
|
+
if (!differences || !Array.isArray(differences) || differences.length === 0) {
|
|
78
|
+
return false;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
return differences.some(difference => types.includes(difference.type));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getLineDash(differences) {
|
|
85
|
+
const hasLayoutShift = hasDifferenceType(differences, ['layout_shift']);
|
|
86
|
+
const hasSizeMismatch = hasDifferenceType(differences, ['size_mismatch']);
|
|
87
|
+
|
|
88
|
+
if (hasLayoutShift && hasSizeMismatch) {
|
|
89
|
+
return [6, 2, 1, 2];
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
if (
|
|
85
|
-
return
|
|
92
|
+
if (hasLayoutShift) {
|
|
93
|
+
return [6, 4];
|
|
86
94
|
}
|
|
87
95
|
|
|
88
|
-
|
|
96
|
+
if (hasSizeMismatch) {
|
|
97
|
+
return [2, 2];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return [];
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
function
|
|
103
|
+
function drawHatchedFill(rect, width, height, fillColor) {
|
|
104
|
+
ctx.save();
|
|
105
|
+
ctx.beginPath();
|
|
106
|
+
ctx.rect(rect.left, rect.top, width, height);
|
|
107
|
+
ctx.clip();
|
|
108
|
+
|
|
109
|
+
ctx.strokeStyle = fillColor;
|
|
110
|
+
ctx.lineWidth = 2;
|
|
111
|
+
ctx.setLineDash([]);
|
|
112
|
+
|
|
113
|
+
const spacing = 6;
|
|
114
|
+
const diagonalReach = width + height;
|
|
115
|
+
|
|
116
|
+
for (let offset = -height; offset < diagonalReach; offset += spacing) {
|
|
117
|
+
ctx.beginPath();
|
|
118
|
+
ctx.moveTo(rect.left + offset, rect.top);
|
|
119
|
+
ctx.lineTo(rect.left + offset + height, rect.top + height);
|
|
120
|
+
ctx.stroke();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
ctx.restore();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function drawDiagonalCross(rect, strokeColor) {
|
|
127
|
+
ctx.strokeStyle = strokeColor;
|
|
128
|
+
ctx.lineWidth = 2;
|
|
129
|
+
ctx.setLineDash([]);
|
|
130
|
+
ctx.beginPath();
|
|
131
|
+
ctx.moveTo(rect.left, rect.top);
|
|
132
|
+
ctx.lineTo(rect.right, rect.bottom);
|
|
133
|
+
ctx.moveTo(rect.right, rect.top);
|
|
134
|
+
ctx.lineTo(rect.left, rect.bottom);
|
|
135
|
+
ctx.stroke();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function drawUprightCross(rect, strokeColor) {
|
|
139
|
+
ctx.strokeStyle = strokeColor;
|
|
140
|
+
ctx.lineWidth = 2;
|
|
141
|
+
ctx.setLineDash([]);
|
|
142
|
+
|
|
143
|
+
const midX = (rect.left + rect.right) / 2;
|
|
144
|
+
const midY = (rect.top + rect.bottom) / 2;
|
|
145
|
+
|
|
146
|
+
ctx.beginPath();
|
|
147
|
+
ctx.moveTo(midX, rect.top);
|
|
148
|
+
ctx.lineTo(midX, rect.bottom);
|
|
149
|
+
ctx.moveTo(rect.left, midY);
|
|
150
|
+
ctx.lineTo(rect.right, midY);
|
|
151
|
+
ctx.stroke();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function drawBoundingBox(rect, colorKey, differences) {
|
|
92
155
|
const adjustedRect = {
|
|
93
156
|
left: rect.left + offsetX,
|
|
94
157
|
right: rect.right + offsetX,
|
|
@@ -100,31 +163,42 @@ async function annotateWireframeFile(wireframeFile, screenshotFile) {
|
|
|
100
163
|
const boxHeight = adjustedRect.bottom - adjustedRect.top;
|
|
101
164
|
const color = colors[colorKey];
|
|
102
165
|
|
|
103
|
-
|
|
104
|
-
|
|
166
|
+
if (hasDifferenceType(differences, ['text_mismatch', 'histogram_difference'])) {
|
|
167
|
+
drawHatchedFill(adjustedRect, boxWidth, boxHeight, color.fill);
|
|
168
|
+
} else {
|
|
169
|
+
ctx.fillStyle = color.fill;
|
|
170
|
+
ctx.fillRect(adjustedRect.left, adjustedRect.top, boxWidth, boxHeight);
|
|
171
|
+
}
|
|
172
|
+
|
|
105
173
|
ctx.strokeStyle = color.stroke;
|
|
106
174
|
ctx.lineWidth = 2;
|
|
107
|
-
ctx.setLineDash(
|
|
175
|
+
ctx.setLineDash(getLineDash(differences));
|
|
108
176
|
ctx.strokeRect(adjustedRect.left, adjustedRect.top, boxWidth, boxHeight);
|
|
109
177
|
ctx.setLineDash([]);
|
|
178
|
+
|
|
179
|
+
if (hasDifferenceType(differences, ['missing_element', 'missing_text'])) {
|
|
180
|
+
drawDiagonalCross(adjustedRect, color.stroke);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (hasDifferenceType(differences, ['extra_element', 'extra_text'])) {
|
|
184
|
+
drawUprightCross(adjustedRect, color.stroke);
|
|
185
|
+
}
|
|
110
186
|
}
|
|
111
187
|
|
|
112
188
|
for (const elementGroup of wireframe.elementGroups) {
|
|
113
|
-
const dashed = elementGroup.options?.strictPosition === false;
|
|
114
|
-
|
|
115
189
|
for (const element of elementGroup.elements) {
|
|
116
190
|
const rect = element.boundingRect;
|
|
117
191
|
|
|
118
192
|
if (element.type === 'box') {
|
|
119
193
|
const colorKey = determineColorKey(element, 'box');
|
|
120
|
-
drawBoundingBox(rect, colorKey,
|
|
194
|
+
drawBoundingBox(rect, colorKey, element.differences);
|
|
121
195
|
} else if (element.type === 'image') {
|
|
122
196
|
const colorKey = determineColorKey(element, 'image');
|
|
123
|
-
drawBoundingBox(rect, colorKey,
|
|
197
|
+
drawBoundingBox(rect, colorKey, element.differences);
|
|
124
198
|
} else if (element.type === 'text' && element.texts && element.texts.length > 0) {
|
|
125
199
|
for (const text of element.texts) {
|
|
126
200
|
const colorKey = determineColorKey(text, 'text');
|
|
127
|
-
drawBoundingBox(text.boundingRect, colorKey,
|
|
201
|
+
drawBoundingBox(text.boundingRect, colorKey, text.differences);
|
|
128
202
|
}
|
|
129
203
|
}
|
|
130
204
|
}
|
|
@@ -186,6 +186,7 @@ function compareBoundingBoxes(baselineRect, currentRect, options) {
|
|
|
186
186
|
if (boundingBoxDifference > 10) {
|
|
187
187
|
differences.push({
|
|
188
188
|
type: "layout_shift",
|
|
189
|
+
kind: 'error',
|
|
189
190
|
difference: boundingBoxDifference
|
|
190
191
|
});
|
|
191
192
|
}
|
|
@@ -202,6 +203,7 @@ function compareBoundingBoxes(baselineRect, currentRect, options) {
|
|
|
202
203
|
if (sizeDiff > 10) {
|
|
203
204
|
differences.push({
|
|
204
205
|
type: "size_mismatch",
|
|
206
|
+
kind: 'error',
|
|
205
207
|
difference: sizeDiff
|
|
206
208
|
});
|
|
207
209
|
}
|
|
@@ -210,6 +212,7 @@ function compareBoundingBoxes(baselineRect, currentRect, options) {
|
|
|
210
212
|
if (diffRatio > 1.1 || diffRatio < 0.9) {
|
|
211
213
|
differences.push({
|
|
212
214
|
type: "size_mismatch",
|
|
215
|
+
kind: 'note',
|
|
213
216
|
difference: diffRatio
|
|
214
217
|
});
|
|
215
218
|
}
|
|
@@ -243,6 +246,7 @@ function compareTexts(baselineTexts, currentTexts, options) {
|
|
|
243
246
|
if (textsEqual) {
|
|
244
247
|
currentText.differences.push({
|
|
245
248
|
type: 'text_mismatch',
|
|
249
|
+
kind: 'error',
|
|
246
250
|
baseline: baselineText.text,
|
|
247
251
|
current: currentText.text,
|
|
248
252
|
});
|
|
@@ -256,22 +260,24 @@ function compareTexts(baselineTexts, currentTexts, options) {
|
|
|
256
260
|
}
|
|
257
261
|
}
|
|
258
262
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
263
|
+
const allowExtraText = options?.allowExtra === true;
|
|
264
|
+
for (const currentIndex of unmatchedCurrent) {
|
|
265
|
+
const currentText = currentTexts[currentIndex];
|
|
266
|
+
currentText.differences = [{
|
|
267
|
+
type: 'extra_text',
|
|
268
|
+
kind: allowExtraText ? 'note' : 'error',
|
|
269
|
+
}];
|
|
270
|
+
}
|
|
266
271
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
272
|
+
const allowMissingText = options?.allowMissing === true;
|
|
273
|
+
for (const baselineIndex of unmatchedBaseline) {
|
|
274
|
+
const baselineText = baselineTexts[baselineIndex];
|
|
275
|
+
currentTexts.push(baselineText);
|
|
276
|
+
baselineText.differences = [{
|
|
277
|
+
type: 'missing_text',
|
|
278
|
+
kind: allowMissingText ? 'note' : 'error',
|
|
279
|
+
}];
|
|
280
|
+
}
|
|
275
281
|
|
|
276
282
|
for (const currentText of currentTexts) {
|
|
277
283
|
if (currentText.differences?.length === 0) {
|
|
@@ -286,6 +292,7 @@ function compareElements(baselineElement, currentElement, options) {
|
|
|
286
292
|
if (dh > 15) {
|
|
287
293
|
currentElement.differences.push({
|
|
288
294
|
type: 'histogram_difference',
|
|
295
|
+
kind: 'error',
|
|
289
296
|
});
|
|
290
297
|
}
|
|
291
298
|
}
|
|
@@ -328,22 +335,24 @@ function compareWireframes(baselineWireframe, currentWireframe) {
|
|
|
328
335
|
}
|
|
329
336
|
}
|
|
330
337
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
+
const allowExtraElements = currentElementGroup.options?.allowExtra === true;
|
|
339
|
+
for (const currentIndex of unmatchedCurrent) {
|
|
340
|
+
const currentElement = currentElementGroup.elements[currentIndex];
|
|
341
|
+
currentElement.differences = [{
|
|
342
|
+
type: 'extra_element',
|
|
343
|
+
kind: allowExtraElements ? 'note' : 'error',
|
|
344
|
+
}];
|
|
345
|
+
}
|
|
338
346
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
+
const allowMissingElements = currentElementGroup.options?.allowMissing === true;
|
|
348
|
+
for (const baselineIndex of unmatchedBaseline) {
|
|
349
|
+
const baselineElement = baselineElementGroup.elements[baselineIndex];
|
|
350
|
+
currentElementGroup.elements.push(baselineElement);
|
|
351
|
+
baselineElement.differences = [{
|
|
352
|
+
type: 'missing_element',
|
|
353
|
+
kind: allowMissingElements ? 'note' : 'error',
|
|
354
|
+
}];
|
|
355
|
+
}
|
|
347
356
|
}
|
|
348
357
|
|
|
349
358
|
return currentWireframe;
|
package/src/wireframeUtils.js
CHANGED
|
@@ -109,8 +109,8 @@ function createEmptyElementGroupResult(elementGroup) {
|
|
|
109
109
|
strictPosition: elementGroup.options?.strictPosition !== false,
|
|
110
110
|
strictSize: elementGroup.options?.strictSize !== false,
|
|
111
111
|
maskDigits: elementGroup.options?.maskDigits !== false,
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
allowExtra: elementGroup.options?.allowExtra === true,
|
|
113
|
+
allowMissing: elementGroup.options?.allowMissing === true
|
|
114
114
|
},
|
|
115
115
|
elements: []
|
|
116
116
|
};
|
|
@@ -205,8 +205,8 @@ async function extractElementGroupData(page, elementGroup, groupIndex, elementId
|
|
|
205
205
|
elementGroup.options.strictPosition = elementGroup.options.strictPosition !== false;
|
|
206
206
|
elementGroup.options.strictSize = elementGroup.options.strictSize !== false;
|
|
207
207
|
elementGroup.options.maskDigits = elementGroup.options.maskDigits !== false;
|
|
208
|
-
elementGroup.options.
|
|
209
|
-
elementGroup.options.
|
|
208
|
+
elementGroup.options.allowExtra = elementGroup.options.allowExtra === true;
|
|
209
|
+
elementGroup.options.allowMissing = elementGroup.options.allowMissing === true;
|
|
210
210
|
elementGroup.elements = [];
|
|
211
211
|
|
|
212
212
|
let index = 0;
|