VulpesOS/gaia/apps/sharedtest/test/unit/icons_helper_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

650 lines
17 KiB
JavaScript
Executable file

/* global IconsHelper, MocksHelper, MockNavigatorDatastore, MockDatastore,
MockXMLHttpRequest, WebManifestHelper */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
require('/apps/system/test/unit/mock_xmlhttprequest.js');
require('/shared/js/web_manifest_helper.js');
require('/shared/js/icons_helper.js');
if (!window.XMLHttpRequest) {
window.XMLHttpRequest = null;
}
var mocksForIconsHelper = new MocksHelper([
'Datastore'
]).init();
suite('Icons Helper', () => {
var devicePixelRatioProperty;
var dpr = 1;
var fakeDevicePixelRatio = {
get: () => {
return dpr;
}
};
var realDataStores;
var realXHR;
mocksForIconsHelper.attachTestHelpers();
suiteSetup(() => {
// As we are selecting the icons based on device pixel ratio
// let's mock the property.
devicePixelRatioProperty =
Object.getOwnPropertyDescriptor(window, 'devicePixelRatio');
Object.defineProperty(window, 'devicePixelRatio', {
configurable: true,
get: fakeDevicePixelRatio.get
});
realDataStores = navigator.getDataStores;
realXHR = window.XMLHttpRequest;
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
window.XMLHttpRequest = MockXMLHttpRequest;
});
suiteTeardown(() => {
Object.defineProperty(window, 'devicePixelRatio', devicePixelRatioProperty);
navigator.getDataStores = realDataStores;
window.XMLHttpRequest = realXHR;
});
suite('IconsHelper.getIcon()', () => {
var placeObj, siteObj;
setup(() => {
placeObj = {
icons: {
'http://example.com/metaTagIconUrl': {
sizes: ['32x32']
}
}
};
siteObj = {
webManifestUrl: 'http://example.com',
webManifest: WebManifestHelper.processRawManifest({
icons: [
{
src: 'webManifestIconUrl',
sizes: '32x32'
}
]
}, 'http://example.com')
};
});
test('Prioritise icons from the Web manifest over the rest', done => {
var origin = 'http://origin.com';
var path = '/test.png';
siteObj.manifest = {
origin: origin,
icons: {
'32': path
}
};
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/webManifestIconUrl');
done();
});
});
test('Prioritise Firefox manifest after Web manifest', done => {
var origin = 'http://origin.com';
var path = '/test.png';
siteObj.webManifest = null;
siteObj.manifest = {
origin: origin,
icons: {
'32': path
}
};
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).href, origin + path);
done();
});
});
test('Works with overridden siteObj.origin', done => {
var origin = 'http://origin.com';
var path = '/test.png';
siteObj.webManifest = null;
siteObj.manifest = {
origin: origin,
icons: {
'32': path
}
};
siteObj.origin = 'https://realorigin.com';
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).href, siteObj.origin + path);
done();
});
});
test('Works with external icons', done => {
var origin = 'http://origin.com';
var path = 'http://test.com/test.png';
siteObj.webManifest = null;
siteObj.manifest = {
origin: origin,
icons: {
'32': path
}
};
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).href, path);
done();
});
});
test('Prioritise icons from the web manifest over the rest', done => {
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/webManifestIconUrl');
done();
});
});
test('Return meta tag icons when the web manifest hasn\'t any', done => {
siteObj.webManifest.icons = [];
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/metaTagIconUrl');
done();
});
});
test('Prioritise icons from the meta tags over favicon', done => {
IconsHelper.getIcon('http://example.com', 32, placeObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/metaTagIconUrl');
done();
});
});
test('Return favicon when no icons in the meta tags or manifest', done => {
siteObj.webManifest.icons = [];
placeObj.icons = [];
IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj)
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/favicon.ico');
done();
});
});
test('Ensure we fallback to favicon.ico', done => {
IconsHelper.getIcon('http://example.com')
.then(iconUrl => {
assert.equal((new URL(iconUrl)).pathname, '/favicon.ico');
done();
});
});
suite('-moz-resolution fragment', () => {
teardown(() => {
dpr = 1;
});
test('Without icon target size', done => {
IconsHelper.getIcon('http://example.com')
.then(iconUrl => {
assert.equal((new URL(iconUrl)).hash, '');
})
.then(() => {
done();
})
.catch(() => {
done();
});
});
test('With icon target size', done => {
dpr = 1.5;
IconsHelper.getIcon('http://example.com', 64)
.then(iconUrl => {
// targetSize * devicePixelRatio
assert.ok(new URL(iconUrl).includes('-moz-resolution=96,96'));
})
.then(() => {
done();
})
.catch(() => {
done();
});
});
});
});
suite('IconsHelper.getIconBlob()', () => {
var createElementStub, createObjectURLStub;
function getStubs() {
createElementStub = sinon.stub(document, 'createElement', () => {
return {
src: '',
naturalWidth: 32,
naturalHeight: 32,
set onload(fn) {
fn();
},
onerror: null
};
});
createObjectURLStub = sinon.stub(URL, 'createObjectURL', url => url);
}
teardown(() => {
createElementStub.restore();
createObjectURLStub.restore();
MockXMLHttpRequest.mTeardown();
});
test('The dataStore should be empty on first call', done => {
getStubs();
IconsHelper.getIconBlob('http://example.com', 32)
.then(() => {
assert.equal(MockDatastore.getLength(), 0);
done();
})
.catch(() => {
done();
});
setTimeout(() => {
MockXMLHttpRequest.mSendReadyState();
MockXMLHttpRequest.mSendOnLoad({response: 'abc'});
});
});
test('The icon should already be in the datastore', done => {
getStubs();
IconsHelper.getIconBlob('http://example.com', 32)
.then(() => {
assert.equal(MockDatastore.getLength(), 1);
done();
})
.catch(() => {
done();
});
setTimeout(() => {
MockXMLHttpRequest.mSendReadyState();
MockXMLHttpRequest.mSendOnLoad({response: 'abc'});
});
});
});
suite('defaultIconSize', () => {
test('1', function() {
dpr = 1;
assert.isDefined(IconsHelper.defaultIconSize);
assert.equal(84, IconsHelper.defaultIconSize);
});
test('1.5', function() {
dpr = 1.5;
assert.isDefined(IconsHelper.defaultIconSize);
assert.equal(142, IconsHelper.defaultIconSize);
});
test('2', function() {
dpr = 2;
assert.isDefined(IconsHelper.defaultIconSize);
assert.equal(142, IconsHelper.defaultIconSize);
});
});
suite('IconsHelper.getBestIconFromWebManifest()', () => {
var webManifest;
var webManifestUrl = 'http://example.com';
test('Get correct icon with no size', () => {
webManifest = WebManifestHelper.processRawManifest({
icons: [
{
src: 'uri1',
sizes: ''
}
]
}, webManifestUrl);
var iconUrl = IconsHelper.getBestIconFromWebManifest(webManifest);
assert.isNotNull(iconUrl);
assert.equal((new URL(iconUrl)).pathname, '/uri1');
});
test('Get icon with size', () => {
webManifest = WebManifestHelper.processRawManifest({
icons: [
{
src: 'uri1',
sizes: ''
},
{
src: 'uri2',
sizes: '16x16'
}
]
}, webManifestUrl);
var iconUrl = IconsHelper.getBestIconFromWebManifest(webManifest);
assert.isNotNull(iconUrl);
assert.equal((new URL(iconUrl)).pathname, '/uri2');
});
test('Get best icon which doesn\'t match specific size', () => {
// With dpr = 1
webManifest = WebManifestHelper.processRawManifest({
icons: [
{
src: 'uri1',
sizes: '90x90'
},
{
src: 'uri2',
sizes: '200x200'
}
]
}, webManifestUrl);
var iconUrl = IconsHelper.getBestIconFromWebManifest(webManifest);
assert.isNotNull(iconUrl);
assert.equal((new URL(iconUrl)).pathname, '/uri1');
});
test('With higher dpi', () => {
// With dpr = 1.5
dpr = 1.5;
webManifest = WebManifestHelper.processRawManifest({
icons: [
{
src: 'uri1',
sizes: '90x90'
},
{
src: 'uri2',
sizes: '180x180'
},
{
src: 'uri3',
sizes: '500x500'
}
]
}, webManifestUrl);
var iconUrl = IconsHelper.getBestIconFromWebManifest(webManifest);
assert.isNotNull(iconUrl);
assert.equal((new URL(iconUrl)).pathname, '/uri2');
dpr = 1;
});
test('Specific icon size', () => {
// With dpr = 1.5
dpr = 1.5;
webManifest = WebManifestHelper.processRawManifest({
icons: [
{
src: 'uri1',
sizes: '90x90'
},
{
src: 'uri2',
sizes: '190x190'
},
{
src: 'uri3',
sizes: '500x500'
}
]
}, webManifestUrl);
// With dpr 1.5 we should get icon 'uri2'
// Let's ask for a bigger size.
var iconUrl = IconsHelper.getBestIconFromWebManifest(webManifest, 400);
assert.isNotNull(iconUrl);
assert.equal((new URL(iconUrl)).pathname, '/uri3');
dpr = 1;
});
});
suite('IconsHelper.getBestIconFromMetaTags()', () => {
test('Get correct icon with no size', () => {
var icons = {
'uri1': {
sizes: []
}
};
var iconUrl = IconsHelper.getBestIconFromMetaTags(icons);
assert.isNotNull(iconUrl);
assert.equal(iconUrl, 'uri1');
});
test('Get icon with size', () => {
var icons = {
'uri1': {
sizes: []
},
'uri2': {
sizes: ['16x16']
}
};
var iconUrl = IconsHelper.getBestIconFromMetaTags(icons);
assert.isNotNull(iconUrl);
assert.equal(iconUrl, 'uri2');
});
test('Get best icon which doesn\'t match specific size', () => {
// With dpr = 1
var icons = {
'uri1': {
sizes: ['90x90']
},
'uri2': {
sizes: ['200x200']
}
};
var iconUrl = IconsHelper.getBestIconFromMetaTags(icons);
assert.isNotNull(iconUrl);
assert.equal(iconUrl, 'uri1');
});
test('With higher dpi', () => {
// With dpr = 1.5
dpr = 1.5;
var icons = {
'uri1': {
sizes: ['90x90']
},
'uri2': {
sizes: ['180x180']
},
'uri3': {
sizes: ['500x500']
}
};
var iconUrl = IconsHelper.getBestIconFromMetaTags(icons);
assert.isNotNull(iconUrl);
assert.equal(iconUrl, 'uri2');
dpr = 1;
});
test('Specific icon size', () => {
// With dpr = 1.5
dpr = 1.5;
var icons = {
'uri1': {
sizes: ['90x90']
},
'uri2': {
sizes: ['200x200']
},
'uri3': {
sizes: ['500x500']
}
};
// With dpr 1.5 we should get icon 'uri2'
// Let's ask for a bigger size.
var iconUrl = IconsHelper.getBestIconFromMetaTags(icons, 400);
assert.isNotNull(iconUrl);
assert.equal(iconUrl, 'uri3');
dpr = 1;
});
});
suite('IconsHelper.getNearestSize()', () => {
suite('No size information', () => {
test('Single element with no size info', () => {
var sizes = [];
var result = IconsHelper.getNearestSize(sizes, 64);
assert.equal(result, -1);
});
test('Several items one without size info', () => {
var sizes = [undefined, '10x10', ''];
var result = IconsHelper.getNearestSize(sizes, 64);
assert.isNotNull(result);
assert.equal(result, '10');
});
});
suite('Correct size', () => {
test('Check sizes detected', () => {
var sizes = ['10x10', '20x20', '30x30'];
var result = IconsHelper.getNearestSize(sizes, 24);
assert.isNotNull(result);
// nearestSize picks the nearest - larger or smaller
assert.equal(result, 20);
});
test('Check with incorrect file sizes', () => {
var sizes = ['10', '', '30x30', 'x'];
var result = IconsHelper.getNearestSize(sizes, 24);
assert.equal(result, 30);
});
test('bestMatch param', () => {
var sizes = ['10x10', '20x20', '30x30'];
var result = IconsHelper.getNearestSize(sizes, 64, 40);
assert.equal(result, 40);
});
});
});
suite('IconsHelper.fetchIcon()', () => {
var createElementStub, createObjectURLStub;
function getStubs(naturalWidth = 32) {
createElementStub = sinon.stub(document, 'createElement', () => {
return {
src: '',
naturalWidth: naturalWidth,
naturalHeight: 32,
set onload(fn) {
fn();
},
onerror: null
};
});
createObjectURLStub = sinon.stub(URL, 'createObjectURL', url => url);
}
teardown(() => {
createElementStub.restore();
createObjectURLStub.restore();
MockXMLHttpRequest.mTeardown();
});
test('Should resolve to an object', done => {
getStubs();
IconsHelper.fetchIcon('http://example.com')
.then((iconObject) => {
assert.isTrue(createObjectURLStub.calledOnce);
assert.isObject(iconObject);
assert.equal(iconObject.blob.content, 'abc');
assert.equal(iconObject.size, '32');
done();
})
.catch(() => {
assert.isTrue(false, 'Should not throw.');
done();
});
MockXMLHttpRequest.mSendReadyState();
MockXMLHttpRequest.mSendOnLoad({
response: { content: 'abc', type: 'image/png' }
});
});
test('Should return the largest side of the image', done => {
getStubs(512);
IconsHelper.fetchIcon('http://example.com')
.then((iconObject) => {
assert.equal(iconObject.size, '512');
done();
})
.catch(() => {
assert.isTrue(false, 'Should not throw.');
done();
});
MockXMLHttpRequest.mSendReadyState();
MockXMLHttpRequest.mSendOnLoad({
response: { content: 'abc', type: 'image/png' }
});
});
test('getBestIconFromWebManifest', function() {
var manifest1 = WebManifestHelper.processRawManifest({
'icons': [
{
'src': 'icon-64.png',
'sizes': '32x32 64x64',
'type': 'image/png'
},
{
'src': 'icon-128.png',
'sizes': '128x128',
'type': 'image/png'
}
]
}, 'http://example.com/manifest.json');
var manifest2 = WebManifestHelper.processRawManifest({
'icons': [
{
'src': 'icon-with-no-sizes.png',
'type': 'image/png'
},
{
'src': 'icon-128.png',
'sizes': '128x128',
'type': 'image/png'
}
]
}, 'http://example.com/manifest.json');
var manifest3 = WebManifestHelper.processRawManifest({
'icons': []
}, 'http://example.com/manifest.json');
var url = IconsHelper.getBestIconFromWebManifest(manifest1, 64);
assert.equal(url, 'http://example.com/icon-64.png');
url = IconsHelper.getBestIconFromWebManifest(manifest2, 64);
assert.equal(url, 'http://example.com/icon-128.png');
url = IconsHelper.getBestIconFromWebManifest(manifest3, 64);
assert.equal(url, null);
});
});
});