Merge branch 'leipert-make-canvas-more-tolerant' into 'master'
Make favicon tests more fault resistent Closes #50527 See merge request gitlab-org/gitlab-ce!22686
This commit is contained in:
commit
7b6fa5ffcb
4 changed files with 82 additions and 10 deletions
|
@ -161,6 +161,7 @@
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-webpack": "^4.0.0-beta.0",
|
"karma-webpack": "^4.0.0-beta.0",
|
||||||
"nodemon": "^1.18.4",
|
"nodemon": "^1.18.4",
|
||||||
|
"pixelmatch": "^4.0.2",
|
||||||
"prettier": "1.15.3",
|
"prettier": "1.15.3",
|
||||||
"vue-jest": "^3.0.2",
|
"vue-jest": "^3.0.2",
|
||||||
"webpack-dev-server": "^3.1.14",
|
"webpack-dev-server": "^3.1.14",
|
||||||
|
|
|
@ -3,6 +3,25 @@ import * as commonUtils from '~/lib/utils/common_utils';
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data';
|
import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data';
|
||||||
|
|
||||||
|
const PIXEL_TOLERANCE = 0.2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a data URL as the src of an
|
||||||
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image|Image}
|
||||||
|
* and resolves to that Image once loaded.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
const urlToImage = url =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = function() {
|
||||||
|
resolve(img);
|
||||||
|
};
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
|
|
||||||
describe('common_utils', () => {
|
describe('common_utils', () => {
|
||||||
describe('parseUrl', () => {
|
describe('parseUrl', () => {
|
||||||
it('returns an anchor tag with url', () => {
|
it('returns an anchor tag with url', () => {
|
||||||
|
@ -513,8 +532,9 @@ describe('common_utils', () => {
|
||||||
it('should return the favicon with the overlay', done => {
|
it('should return the favicon with the overlay', done => {
|
||||||
commonUtils
|
commonUtils
|
||||||
.createOverlayIcon(faviconDataUrl, overlayDataUrl)
|
.createOverlayIcon(faviconDataUrl, overlayDataUrl)
|
||||||
.then(url => {
|
.then(url => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)]))
|
||||||
expect(url).toEqual(faviconWithOverlayDataUrl);
|
.then(([actual, expected]) => {
|
||||||
|
expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
|
@ -536,10 +556,10 @@ describe('common_utils', () => {
|
||||||
it('should set page favicon to provided favicon overlay', done => {
|
it('should set page favicon to provided favicon overlay', done => {
|
||||||
commonUtils
|
commonUtils
|
||||||
.setFaviconOverlay(overlayDataUrl)
|
.setFaviconOverlay(overlayDataUrl)
|
||||||
.then(() => {
|
.then(() => document.getElementById('favicon').getAttribute('href'))
|
||||||
expect(document.getElementById('favicon').getAttribute('href')).toEqual(
|
.then(url => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)]))
|
||||||
faviconWithOverlayDataUrl,
|
.then(([actual, expected]) => {
|
||||||
);
|
expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
|
@ -582,10 +602,10 @@ describe('common_utils', () => {
|
||||||
|
|
||||||
commonUtils
|
commonUtils
|
||||||
.setCiStatusFavicon(BUILD_URL)
|
.setCiStatusFavicon(BUILD_URL)
|
||||||
.then(() => {
|
.then(() => document.getElementById('favicon').getAttribute('href'))
|
||||||
const favicon = document.getElementById('favicon');
|
.then(url => Promise.all([urlToImage(url), urlToImage(faviconWithOverlayDataUrl)]))
|
||||||
|
.then(([actual, expected]) => {
|
||||||
expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
|
expect(actual).toImageDiffEqual(expected, PIXEL_TOLERANCE);
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import pixelmatch from 'pixelmatch';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
toContainText: () => ({
|
toContainText: () => ({
|
||||||
compare(vm, text) {
|
compare(vm, text) {
|
||||||
|
@ -54,4 +56,41 @@ export default {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
toImageDiffEqual: () => {
|
||||||
|
const getImageData = img => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
canvas.getContext('2d').drawImage(img, 0, 0);
|
||||||
|
return canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
compare(actual, expected, threshold = 0.1) {
|
||||||
|
if (actual.height !== expected.height || actual.width !== expected.width) {
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
message: `Expected image dimensions (h x w) of ${expected.height}x${expected.width}.
|
||||||
|
Received an image with ${actual.height}x${actual.width}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { width, height } = actual;
|
||||||
|
const differentPixels = pixelmatch(
|
||||||
|
getImageData(actual),
|
||||||
|
getImageData(expected),
|
||||||
|
null,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
{ threshold },
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
pass: differentPixels < 20,
|
||||||
|
message: `${differentPixels} pixels differ more than ${threshold *
|
||||||
|
100} percent between input and output.`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -7735,6 +7735,13 @@ pinkie@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||||
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
||||||
|
|
||||||
|
pixelmatch@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854"
|
||||||
|
integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=
|
||||||
|
dependencies:
|
||||||
|
pngjs "^3.0.0"
|
||||||
|
|
||||||
pkg-dir@^1.0.0:
|
pkg-dir@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
|
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
|
||||||
|
@ -7766,6 +7773,11 @@ pn@^1.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
|
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
|
||||||
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
|
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
|
||||||
|
|
||||||
|
pngjs@^3.0.0:
|
||||||
|
version "3.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
|
||||||
|
integrity sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q==
|
||||||
|
|
||||||
pofile@^1:
|
pofile@^1:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"
|
resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954"
|
||||||
|
|
Loading…
Reference in a new issue