Skip to content

Commit 190d516

Browse files
committed
Update tests, use a diff library
1 parent 2f7052a commit 190d516

File tree

18 files changed

+514
-69
lines changed

18 files changed

+514
-69
lines changed

package-lock.json

Lines changed: 458 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"i18next": "^19.0.2",
4646
"i18next-browser-languagedetector": "^4.0.1",
4747
"lint-staged": "^15.1.0",
48+
"resemblejs": "^5.0.0",
4849
"rollup": "^4.9.6",
4950
"rollup-plugin-string": "^3.0.0",
5051
"rollup-plugin-visualizer": "^5.12.0",

src/image/loading_displaying.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,13 @@ p5.prototype.loadImage = async function(
140140
successCallback,
141141
failureCallback,
142142
(pImg => {
143-
self._decrementPreload();
144143
resolve(pImg);
145144
}).bind(self)
146145
);
147146
}catch(e){
148147
console.error(e.toString(), e.stack);
149148
if (typeof failureCallback === 'function') {
150149
failureCallback(e);
151-
self._decrementPreload();
152150
} else {
153151
console.error(e);
154152
}
@@ -160,7 +158,6 @@ p5.prototype.loadImage = async function(
160158
e => {
161159
if (typeof failureCallback === 'function') {
162160
failureCallback(e);
163-
self._decrementPreload();
164161
} else {
165162
console.error(e);
166163
}
@@ -182,14 +179,12 @@ p5.prototype.loadImage = async function(
182179
successCallback(pImg);
183180
}
184181
resolve();
185-
self._decrementPreload();
186182
};
187183

188184
img.onerror = e => {
189185
p5._friendlyFileLoadError(0, img.src);
190186
if (typeof failureCallback === 'function') {
191187
failureCallback(e);
192-
self._decrementPreload();
193188
} else {
194189
console.error(e);
195190
}
@@ -215,7 +210,6 @@ p5.prototype.loadImage = async function(
215210
p5._friendlyFileLoadError(0, path);
216211
if (typeof failureCallback === 'function') {
217212
failureCallback(e);
218-
self._decrementPreload();
219213
} else {
220214
console.error(e);
221215
}

src/typography/loading_displaying.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,11 @@ import '../core/friendly_errors/fes_core';
126126
* </code>
127127
* </div>
128128
*/
129-
p5.prototype.loadFont = function(path, onSuccess, onError) {
129+
p5.prototype.loadFont = async function(path, onSuccess, onError) {
130130
p5._validateParameters('loadFont', arguments);
131131
const p5Font = new p5.Font(this);
132132

133-
const self = this;
134-
opentype.load(path, (err, font) => {
133+
await new Promise(resolve => opentype.load(path, (err, font) => {
135134
if (err) {
136135
p5._friendlyFileLoadError(4, path);
137136
if (typeof onError !== 'undefined') {
@@ -146,8 +145,7 @@ p5.prototype.loadFont = function(path, onSuccess, onError) {
146145
if (typeof onSuccess !== 'undefined') {
147146
onSuccess(p5Font);
148147
}
149-
150-
self._decrementPreload();
148+
resolve();
151149

152150
// check that we have an acceptable font type
153151
const validFontTypes = ['ttf', 'otf', 'woff', 'woff2'];
@@ -174,7 +172,7 @@ p5.prototype.loadFont = function(path, onSuccess, onError) {
174172
);
175173
document.head.appendChild(newStyle);
176174
}
177-
});
175+
}));
178176

179177
return p5Font;
180178
};

src/webgl/loading.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ function parseObj(model, lines, materials = {}) {
665665
model.vertexColors.push(materialDiffuseColor[0]);
666666
model.vertexColors.push(materialDiffuseColor[1]);
667667
model.vertexColors.push(materialDiffuseColor[2]);
668+
model.vertexColors.push(1);
668669
}
669670
} else {
670671
hasColorlessVertices = true;

test/unit/visual/cases/typography.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ visualSuite('Typography', function() {
66
p5.createCanvas(50, 50);
77
p5.textSize(20);
88
p5.textAlign(p5.LEFT, p5.TOP);
9-
p5.text('test', 0, 0);
9+
p5.text('broken', 0, 0);
1010
screenshot();
1111
});
1212

Loading
Loading
Loading
Loading
Loading
Loading

test/unit/visual/visualTest.js

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
import p5 from '../../../src/app.js';
2+
import { server } from '@vitest/browser/context'
3+
import resemble from 'resemblejs'
4+
const { readFile, writeFile } = server.commands
25

3-
/**
4-
* A helper class to contain an error and also the screenshot data that
5-
* caused the error.
6-
*/
7-
class ScreenshotError extends Error {
8-
constructor(message, actual, expected) {
9-
super(message);
10-
this.actual = actual;
11-
this.expected = expected;
12-
}
6+
function writeImageFile(filename, base64Data) {
7+
const prefix = /^data:image\/\w+;base64,/;
8+
writeFile(filename, base64Data.replace(prefix, ''), 'base64');
139
}
1410

1511
function toBase64(img) {
@@ -37,45 +33,49 @@ export function visualSuite(
3733
callback,
3834
{ focus = false, skip = false } = {}
3935
) {
40-
const lastPrefix = namePrefix;
41-
namePrefix += escapeName(name) + '/';
42-
43-
let suiteFn = suite;
36+
let suiteFn = describe;
4437
if (focus) {
4538
suiteFn = suiteFn.only;
4639
}
4740
if (skip) {
4841
suiteFn = suiteFn.skip;
4942
}
50-
suiteFn(name, callback);
43+
suiteFn(name, () => {
44+
let lastPrefix;
45+
beforeAll(() => {
46+
lastPrefix = namePrefix;
47+
namePrefix += escapeName(name) + '/';
48+
})
5149

52-
namePrefix = lastPrefix;
50+
callback()
51+
52+
afterAll(() => {
53+
namePrefix = lastPrefix;
54+
});
55+
});
5356
}
5457

55-
export function checkMatch(actual, expected, p5) {
58+
export async function checkMatch(actual, expected, p5) {
5659
const maxSide = 50;
5760
const scale = Math.min(maxSide/expected.width, maxSide/expected.height);
61+
5862
for (const img of [actual, expected]) {
5963
img.resize(
6064
Math.ceil(img.width * scale),
6165
Math.ceil(img.height * scale)
6266
);
6367
}
64-
const diff = p5.createImage(actual.width, actual.height);
65-
diff.drawingContext.drawImage(actual.canvas, 0, 0);
66-
diff.drawingContext.globalCompositeOperation = 'difference';
67-
diff.drawingContext.drawImage(expected.canvas, 0, 0);
68-
diff.filter(p5.ERODE, false);
69-
diff.loadPixels();
70-
71-
let ok = true;
72-
for (let i = 0; i < diff.pixels.length; i++) {
73-
if (i % 4 === 3) continue; // Skip alpha checks
74-
if (Math.abs(diff.pixels[i]) > 10) {
75-
ok = false;
76-
break;
77-
}
78-
}
68+
69+
resemble.outputSettings({ useCrossOrigin: false });
70+
const diff = await new Promise(resolve => resemble(toBase64(actual))
71+
.compareTo(toBase64(expected))
72+
.ignoreAntialiasing()
73+
.onComplete((data) => {
74+
resolve(data)
75+
})
76+
)
77+
const ok = diff.rawMisMatchPercentage === 0;
78+
7979
return { ok, diff };
8080
}
8181

@@ -103,8 +103,7 @@ export function visualTest(
103103
callback,
104104
{ focus = false, skip = false } = {}
105105
) {
106-
const name = namePrefix + escapeName(testName);
107-
let suiteFn = suite;
106+
let suiteFn = describe;
108107
if (focus) {
109108
suiteFn = suiteFn.only;
110109
}
@@ -113,9 +112,11 @@ export function visualTest(
113112
}
114113

115114
suiteFn(testName, function() {
115+
let name;
116116
let myp5;
117117

118118
beforeAll(function() {
119+
name = namePrefix + escapeName(testName);
119120
return new Promise(res => {
120121
myp5 = new p5(function(p) {
121122
p.setup = function() {
@@ -132,20 +133,15 @@ export function visualTest(
132133
test('matches expected screenshots', async function() {
133134
let expectedScreenshots;
134135
try {
135-
metadata = await fetch(
136-
`unit/visual/screenshots/${name}/metadata.json`
137-
).then(res => res.json());
136+
const metadata = JSON.parse(await readFile(
137+
`../screenshots/${name}/metadata.json`
138+
));
138139
expectedScreenshots = metadata.numScreenshots;
139140
} catch (e) {
141+
console.log(e);
140142
expectedScreenshots = 0;
141143
}
142144

143-
if (!window.shouldGenerateScreenshots && !expectedScreenshots) {
144-
// If running on CI, all expected screenshots should already
145-
// be generated
146-
throw new Error('No expected screenshots found');
147-
}
148-
149145
const actual = [];
150146

151147
// Generate screenshots
@@ -163,34 +159,31 @@ export function visualTest(
163159
);
164160
}
165161
if (!expectedScreenshots) {
166-
writeTextFile(
167-
`unit/visual/screenshots/${name}/metadata.json`,
162+
await writeFile(
163+
`../screenshots/${name}/metadata.json`,
168164
JSON.stringify({ numScreenshots: actual.length }, null, 2)
169165
);
170166
}
171167

172168
const expectedFilenames = actual.map(
173-
(_, i) => `unit/visual/screenshots/${name}/${i.toString().padStart(3, '0')}.png`
169+
(_, i) => `../screenshots/${name}/${i.toString().padStart(3, '0')}.png`
174170
);
175171
const expected = expectedScreenshots
176172
? (
177173
await Promise.all(
178-
expectedFilenames.map(path => new Promise((resolve, reject) => {
179-
myp5.loadImage(path, resolve, reject);
180-
}))
174+
expectedFilenames.map(path => myp5.loadImage('/unit/visual' + path.slice(2)))
181175
)
182176
)
183177
: [];
184178

185179
for (let i = 0; i < actual.length; i++) {
186180
if (expected[i]) {
187-
if (!checkMatch(actual[i], expected[i], myp5).ok) {
188-
throw new ScreenshotError(
189-
`Screenshots do not match! Expected:\n${toBase64(expected[i])}\n\nReceived:\n${toBase64(actual[i])}\n\n` +
190-
'If this is unexpected, paste these URLs into your browser to inspect them, or run grunt yui:dev and go to http://127.0.0.1:9001/test/visual.html.\n\n' +
191-
`If this change is expected, please delete the test/unit/visual/screenshots/${name} folder and run tests again to generate a new screenshot.`,
192-
actual[i],
193-
expected[i]
181+
const result = await checkMatch(actual[i], expected[i], myp5);
182+
if (!result.ok) {
183+
throw new Error(
184+
`Screenshots do not match! Expected:\n${toBase64(expected[i])}\n\nReceived:\n${toBase64(actual[i])}\n\nDiff:\n${result.diff.getImageDataUrl()}\n\n` +
185+
'If this is unexpected, paste these URLs into your browser to inspect them.\n\n' +
186+
`If this change is expected, please delete the screenshots/${name} folder and run tests again to generate a new screenshot.`,
194187
);
195188
}
196189
} else {

0 commit comments

Comments
 (0)