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.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,540 @@
/* global MocksHelper, MockNavigatorDatastore, MockDatastore, BaseModule */
/* global asyncStorage, MockService */
'use strict';
requireApp('system/shared/test/unit/mocks/mock_settings_listener.js');
require('/shared/test/unit/mocks/mock_async_storage.js');
require('/shared/test/unit/mocks/mock_service.js');
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
requireApp('system/js/service.js');
requireApp('system/js/base_module.js');
requireApp('system/js/places.js');
var mocksHelperForPlaces = new MocksHelper([
'asyncStorage',
'SettingsListener',
'Datastore',
'LazyLoader'
]).init();
suite('system/Places', function() {
var realDatastores;
var subject;
mocksHelperForPlaces.attachTestHelpers();
suiteSetup(function(done) {
window.BrowserSettings = {
start: function() {}
};
asyncStorage.getItem = function(key, callback) {
callback(null);
};
realDatastores = navigator.getDataStores;
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
subject = BaseModule.instantiate('Places');
subject.service = MockService;
subject.start().then(done);
});
suiteTeardown(function() {
delete window.BrowserSettings;
navigator.getDataStores = realDatastores;
});
setup(function() {
this.sinon.useFakeTimers();
});
var url1 = 'http://example.org';
var oneIcon = {
'windows.ico': {
sizes: ['32x32 48x48']
},
};
suite('Private Browsing', function() {
function sendPrivateBrowserEvent(event, url) {
window.dispatchEvent(new CustomEvent(event, {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return true; },
config: {
url: url
}
}
}));
}
test('Does not process events for private browsers', function() {
var locationStub = this.sinon.stub(subject, 'onLocationChange');
var debounceStub = this.sinon.stub(subject, 'debouncePlaceChanges');
sendPrivateBrowserEvent('applocationchange', url1);
sendPrivateBrowserEvent('apploaded', url1);
assert.isFalse(locationStub.called);
assert.isFalse(debounceStub.called);
});
});
suite('Test places event handling', function() {
teardown(function() {
MockDatastore.clear();
});
function sendEvent(event, url) {
window.dispatchEvent(new CustomEvent(event, {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
config: {
url: url
}
}
}));
}
test('Test visit', function(done) {
var screenshotStub = sinon.stub(subject, 'screenshotRequested');
MockDatastore.addEventListener('change', function() {
assert.ok(url1 in MockDatastore._records);
assert.equal(MockDatastore._records[url1].frecency, 1);
MockDatastore.removeEventListener();
screenshotStub.restore();
done();
});
sendEvent('applocationchange', url1);
sendEvent('apploaded', url1);
});
test('Test title event', function(done) {
var title = 'New Title!';
MockDatastore.addEventListener('change', function() {
assert.ok(url1 in MockDatastore._records);
assert.equal(MockDatastore._records[url1].title, title);
MockDatastore.removeEventListener();
done();
});
sendEvent('applocationchange', url1);
window.dispatchEvent(new CustomEvent('apptitlechange', {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
title: title,
config: {
url: url1
}
}
}));
sendEvent('apploaded', url1);
});
test('titlechange event while writeInProgress', function(done) {
var title = 'New Title!';
var originalEditPlace = subject.editPlace;
var firstCall = true;
this.sinon.stub(subject, 'editPlace', function(url, func) {
if (firstCall) {
// Is writeInProgress during the first call
firstCall = false;
return originalEditPlace.bind(subject)(url, func);
}
// On the second call, we allow to write
subject.writeInProgress = false;
return originalEditPlace.bind(subject)(url, func);
});
MockDatastore.addEventListener('change', function() {
assert.ok(url1 in MockDatastore._records);
assert.equal(MockDatastore._records[url1].title, title);
MockDatastore.removeEventListener();
done();
});
subject.writeInProgress = true;
window.dispatchEvent(new CustomEvent('apptitlechange', {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
title: title,
config: {
url: url1
}
}
}));
this.sinon.clock.tick(2000);
});
test('Test icon event', function(done) {
MockDatastore.addEventListener('change', function() {
assert.ok(url1 in MockDatastore._records);
var icons = MockDatastore._records[url1].icons;
assert.ok('windows.ico' in icons);
assert.deepEqual(icons['windows.ico'].sizes, ['32x32 48x48']);
MockDatastore.removeEventListener();
done();
});
sendEvent('applocationchange', url1);
window.dispatchEvent(new CustomEvent('appiconchange', {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
favicons: oneIcon,
config: {
url: url1
}
}
}));
sendEvent('apploaded', url1);
});
test('Test meta event', function(done) {
var meta = {
name: 'content'
};
MockDatastore.addEventListener('change', function() {
assert.ok(url1 in MockDatastore._records);
var newMeta = MockDatastore._records[url1].meta;
assert.deepEqual(newMeta, meta);
MockDatastore.removeEventListener();
done();
});
sendEvent('applocationchange', url1);
window.dispatchEvent(new CustomEvent('appmetachange', {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
meta: meta,
config: {
url: url1
}
}
}));
sendEvent('apploaded', url1);
});
test('Test screenshots', function(done) {
subject.topSites = [];
var screenshotStub = sinon.stub(subject, 'screenshotRequested');
var changes = 0;
MockDatastore.addEventListener('change', function() {
changes++;
// First url should request a screenshot
if (changes === 1) {
assert.ok(screenshotStub.calledOnce);
sendEvent('applocationchange', url1 + '/1');
sendEvent('apploaded', url1 + '/1');
return;
}
// We send 2 requests for /0 through /5 two times each
if (changes > 1 && changes < 12) {
sendEvent('applocationchange', url1 + '/' + (changes % 6));
sendEvent('apploaded', url1 + '/' + (changes % 6));
return;
}
// There are 6 urls so all are top sites
if (changes === 12) {
assert.equal(screenshotStub.callCount, 12);
sendEvent('applocationchange', url1 + '/new');
sendEvent('apploaded', url1 + '/new');
return;
}
// The last url is only visited once (rest have 2) so it should
// not request a screenshot
if (changes === 13) {
assert.equal(screenshotStub.callCount, 12);
screenshotStub.restore();
MockDatastore.removeEventListener();
done();
}
});
sendEvent('applocationchange', url1 + '/0');
sendEvent('apploaded', url1 + '/0');
});
test('Test screenshots for loading sites', function() {
var takeScreenshotStub = sinon.stub(subject, 'takeScreenshot');
subject.screenshotRequested('http://example.org');
assert.ok(takeScreenshotStub.notCalled);
this.sinon.clock.tick(6000);
assert.ok(takeScreenshotStub.calledOnce);
takeScreenshotStub.restore();
});
test('Receive icon update after apploaded saves place', function(done) {
sendEvent('applocationchange', url1);
sendEvent('apploaded', url1);
MockDatastore.addEventListener('change', function() {
// Exit after we get a save event for the icon update.
done();
});
sendEvent('appiconchange', url1);
this.sinon.clock.tick(10000);
});
test('Receive title update after apploaded saves place', function(done) {
sendEvent('applocationchange', url1);
sendEvent('apploaded', url1);
MockDatastore.addEventListener('change', function() {
// Exit after we get a save event for the icon update.
done();
});
sendEvent('apptitlechange', url1);
this.sinon.clock.tick(10000);
});
test('Correctly set visits', function(done) {
var url = 'http://example.org';
MockDatastore.addEventListener('change', function() {
assert.equal(MockDatastore._records[url].visits.length, 3);
done();
});
subject.setVisits(url, [1, 2, 3]);
});
test('Correctly set pinned status', function(done) {
var url = 'http://example.org';
MockDatastore.addEventListener('change', function() {
assert.equal(MockDatastore._records[url].pinned, true);
done();
});
subject.setPinned(url, true);
});
test('Ensure place without icon doesnt bail', function(done) {
var url = 'http://example.org';
MockDatastore.put({
url: url,
tile: 'a tile',
frecency: 1
}, url);
MockDatastore.addEventListener('change', function() {
assert.equal(MockDatastore._records[url].frecency, 2);
done();
});
sendEvent('applocationchange', url);
window.dispatchEvent(new CustomEvent('appiconchange', {
detail: {
isBrowser: function() { return true; },
isPrivateBrowser: function() { return false; },
favicons: oneIcon,
config: {url: url }
}
}));
this.sinon.clock.tick(10000);
});
suite('isPinned', function() {
setup(function() {
MockDatastore.put({
url: 'http://example.com/index.html',
tile: 'a tile',
frecency: 1
}, 'http://example.com/index.html');
MockDatastore.put({
url: 'http://example.org',
tile: 'a tile',
frecency: 1,
pinned: true,
pinTime: 1444829881365
}, 'http://example.org');
});
teardown(function() {
});
test('should return false for unpinned pages', function(done) {
subject.isPinned('http://example.com/index.html')
.then((isPinned) => {
assert.isFalse(isPinned);
done();
});
});
test('should return true for pinned pages', function(done) {
subject.isPinned('http://example.org')
.then((isPinned) => {
assert.isTrue(isPinned);
done();
});
});
});
suite('Clear history', function() {
var syncSpy;
setup(function() {
syncSpy = this.sinon.spy(MockDatastore, 'sync');
});
teardown(function() {
syncSpy.restore();
});
test('should escape prematurely when empty', function(done) {
subject.clearHistory()
.then(() => {
assert.isFalse(syncSpy.called);
done();
});
});
test('should not remove pinned pages', function(done) {
var url = 'http://example.org';
MockDatastore.put({
url: 'http://example.com/index.html',
tile: 'a tile',
frecency: 1
}, url);
MockDatastore.put({
url: url,
tile: 'a tile',
frecency: 1,
pinned: true,
pinTime: 1444829881365
}, url);
subject.clearHistory()
.then(() => {
assert.isObject(MockDatastore._records[url]);
assert.isTrue(syncSpy.called);
done();
});
});
});
suite('Clean dupes', function() {
var removeSpy, setItemStub;
var topSitesWithDupes = [
{ url: 'http://website1/', frecency: 10},
{ url: 'http://website1/', frecency: 8},
{ url: 'http://website1/', frecency: 6},
{ url: 'http://website2/', frecency: 5},
{ url: 'http://website2/', frecency: 4},
{ url: 'http://website3/', frecency: 3},
];
var topSitesWithoutDupes = [
{ url: 'http://website1/', frecency: 10},
{ url: 'http://website2/', frecency: 5},
{ url: 'http://website3/', frecency: 3},
{ url: 'http://website4/', frecency: 2},
{ url: 'http://website5/', frecency: 2},
{ url: 'http://website6/', frecency: 2},
];
setup(function() {
removeSpy = this.sinon.spy(subject, '_removeDupes');
setItemStub = this.sinon.stub(asyncStorage, 'setItem');
});
test('_start() calls _removeDupes()', function(done) {
subject._start().then(() => {
sinon.assert.calledOnce(removeSpy);
done();
});
});
suite('Duplicates removal', function() {
test('empty top sites', function() {
var result = subject._removeDupes([]);
assert.equal(result.length, 0);
});
test('removes duplicated top sites', function() {
var result = subject._removeDupes(topSitesWithDupes);
assert.equal(result.length, 3);
assert.deepEqual(result[0], topSitesWithoutDupes[0]);
assert.deepEqual(result[1], topSitesWithoutDupes[1]);
assert.deepEqual(result[2], topSitesWithoutDupes[2]);
});
test('nothing on non duplicated top sites', function() {
var result = subject._removeDupes(topSitesWithoutDupes);
assert.equal(result.length, 6);
assert.deepEqual(result[0], topSitesWithoutDupes[0]);
assert.deepEqual(result[1], topSitesWithoutDupes[1]);
assert.deepEqual(result[2], topSitesWithoutDupes[2]);
});
});
suite('checkTopSites should not add dupes', function() {
setup(function() {
subject.topSites = topSitesWithoutDupes.slice(0);
});
test('checking new place, higher frecency', function() {
var newPlace = { url: 'http://website7/', frecency: 7 };
subject.checkTopSites(newPlace);
assert.equal(subject.topSites.length, 6);
assert.deepEqual(topSitesWithoutDupes[0], subject.topSites[0]);
assert.deepEqual(newPlace, subject.topSites[1]);
assert.deepEqual(topSitesWithoutDupes[1], subject.topSites[2]);
assert.deepEqual(topSitesWithoutDupes[2], subject.topSites[3]);
assert.deepEqual(topSitesWithoutDupes[3], subject.topSites[4]);
assert.deepEqual(topSitesWithoutDupes[4], subject.topSites[5]);
});
test('checking new place, lower frecency', function() {
var newPlace = { url: 'http://website7/', frecency: 1 };
subject.checkTopSites(newPlace);
assert.equal(subject.topSites.length, 6);
for (var i = 0; i < 6; i++) {
assert.notDeepEqual(newPlace, subject.topSites[i]);
}
});
test('checking existing place, higher frecency', function() {
var existPlace = { url: 'http://website3/', frecency: 7 };
subject.checkTopSites(existPlace);
assert.equal(subject.topSites.length, 6);
assert.deepEqual(topSitesWithoutDupes[0], subject.topSites[0]);
assert.deepEqual(existPlace, subject.topSites[1]);
assert.deepEqual(topSitesWithoutDupes[1], subject.topSites[2]);
assert.notDeepEqual(topSitesWithoutDupes[2], subject.topSites[3]);
assert.deepEqual(topSitesWithoutDupes[3], subject.topSites[3]);
assert.deepEqual(topSitesWithoutDupes[4], subject.topSites[4]);
assert.deepEqual(topSitesWithoutDupes[5], subject.topSites[5]);
});
test('checking existing place, lower frecency', function() {
var existPlace = { url: 'http://website3/', frecency: 1 };
subject.checkTopSites(existPlace);
assert.equal(subject.topSites.length, 6);
for (var i = 0; i < 6; i++) {
assert.notDeepEqual(existPlace, subject.topSites[i]);
}
});
});
});
});
});