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,17 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML"
]
}

View file

@ -0,0 +1,80 @@
'use strict';
require('/js/contextmenu.js');
suite('search/contextmenu', function() {
var subject = null,
realBookmarks = null,
isBookmarked = false;
suiteSetup(function() {
realBookmarks = window.Bookmarks;
window.Bookmarks = {
get: function() {
return isBookmarked;
}
};
loadBodyHTML('/index.html');
});
suiteTeardown(function() {
window.Bookmarks = realBookmarks;
});
setup(function() {
this.sinon.useFakeTimers();
subject = new window.Contextmenu();
});
suite('handleEvent', function() {
test('contextmenu icon not bookmarked', function() {
var target = document.createElement('div');
target.getIcon = function() {
return {
detail: {
type: 'bookmark'
}
};
};
target.start = function() {};
target.stop = function() {};
subject.grid = target;
var fakeContextMenuEvent = {
type: 'contextmenu',
target: target
};
var startStub = this.sinon.stub(subject.grid, 'start');
var stopStub = this.sinon.stub(subject.grid, 'stop');
subject.handleEvent(fakeContextMenuEvent);
assert.ok(stopStub.calledOnce);
this.sinon.clock.tick();
assert.ok(startStub.calledOnce);
});
test('contextmenu icon already bookmarked', function() {
var target = document.createElement('div');
target.getIcon = function() {
return {
detail: {
type: 'bookmark'
}
};
};
target.stop = function() {};
subject.grid = target;
var fakeContextMenuEvent = {
type: 'contextmenu',
target: target
};
isBookmarked = true;
var stopStub = this.sinon.stub(subject.grid, 'stop');
subject.handleEvent(fakeContextMenuEvent);
assert.isFalse(stopStub.called);
});
});
});

View file

@ -0,0 +1,13 @@
'use strict';
/* global Promise */
var MockProvider = function(name) {
this.name = name;
};
MockProvider.prototype.init = function() {};
MockProvider.prototype.clear = function() {};
MockProvider.prototype.search = function() {
return new Promise(() => {});
};
MockProvider.prototype.abort = function() {};

View file

@ -0,0 +1,17 @@
'use strict';
window.Search = {
_port: null,
providers: {},
init: function() {},
provider: function(provider) {
this.providers[provider.name] = provider;
},
onSearchInput: function() {},
close: function() {},
navigate: function() {},
setInput: function() {},
collect: function(provider, results) {
provider.render(results);
}
};

View file

@ -0,0 +1,169 @@
/* globals MocksHelper */
/* globals asyncStorage */
/* globals PlacesIdbStore */
'use strict';
require('/shared/test/unit/mocks/mock_async_storage.js');
requireApp('search/js/places_idb.js');
var mocksHelper = new MocksHelper([
'asyncStorage'
]).init();
suite('search/places_idb', function() {
var subject;
var mozilla = 'http://mozilla.org';
var google = 'http://google.com';
var yahoo = 'http://yahoo.com';
function place(url, frecency, visited, visits) {
frecency = frecency || 1;
visited = visited || 0;
visits = visits || [visited];
return {
url: url,
frecency: frecency,
visited: visited,
visits: visits
};
}
mocksHelper.attachTestHelpers();
setup(done => {
asyncStorage.getItem = function(key, callback) {
callback(null);
};
subject = new PlacesIdbStore();
subject.init().then(function() {
subject.clear().then(function() {
done();
});
});
});
test('Test frecency', function(done) {
Promise.all([
subject.add('url', place(mozilla, 1, 0)),
subject.add('url', place(google, 3, 1)),
subject.add('url', place(yahoo, 2, 2)),
subject.add('url', place(mozilla, 4, 3))
]).then(function() {
subject.read('frecency', 5, function(results) {
assert.equal(results.length, 3);
assert.equal(results[0].url, mozilla);
assert.equal(results[1].url, google);
assert.equal(results[2].url, yahoo);
done();
});
});
});
test('Test history', function(done) {
Promise.all([
subject.add('url', place(mozilla, 1, 0)),
subject.add('url', place(google, 3, 1)),
subject.add('url', place(yahoo, 2, 2)),
subject.add('url', place(mozilla, 4, 3))
]).then(function() {
subject.read('visited', 5, function(results) {
assert.equal(results.length, 3);
assert.equal(results[0].url, mozilla);
assert.equal(results[1].url, yahoo);
assert.equal(results[2].url, google);
done();
});
});
});
test('Test frecency filter', function(done) {
Promise.all([
subject.add('url', place(google, -1, 0)),
subject.add('url', place(yahoo, -2, 2)),
subject.add('url', place(mozilla, 1, 3))
]).then(function() {
subject.read('frecency', 5, function(results) {
assert.equal(results.length, 1);
assert.equal(results[0].url, mozilla);
done();
}, function(place) {
return place.frecency > 0;
});
});
});
test('Test filter', function(done) {
Promise.all([
subject.add('url', place(mozilla, 1, 0)),
subject.add('url', place(google, 3, 1)),
subject.add('url', place(yahoo, 2, 2)),
subject.add('url', place(mozilla, 4, 3))
]).then(function() {
subject.read('visited', 5, function(results) {
assert.equal(results.length, 1);
assert.equal(results[0].url, mozilla);
done();
}, function(place) {
return place.url === mozilla;
});
});
});
test('Test visits', function(done) {
Promise.all([
subject.add('url', place(mozilla, 1, 0, [0])),
subject.add('url', place(google, 3, 0, [4, 2])),
subject.add('url', place(yahoo, 2, 0, [7, 3])),
subject.add('url', place(mozilla, 4, 0, [6, 5, 1, 0]))
]).then(function() {
subject.readVisits(7, function(results) {
assert.equal(results.length, 7);
assert.equal(results[0].url, yahoo);
assert.equal(results[1].url, mozilla);
assert.equal(results[2].url, mozilla);
assert.equal(results[3].url, google);
assert.equal(results[4].url, yahoo);
assert.equal(results[5].url, google);
assert.equal(results[6].url, mozilla);
done();
});
});
});
test('Adding empty visits should delete the record', function(done) {
Promise.all([
subject.add(mozilla, place(mozilla, 1, 1, [1])),
subject.add(mozilla, place(mozilla, 1, 2, []))
]).then(() => {
subject.read('visited', 5, results => {
assert.equal(results.length, 0);
subject.readVisits(5, results => {
assert.equal(results.length, 0);
done();
});
});
});
});
test('Adding empty visits should delete the record', function(done) {
Promise.all([
subject.add(mozilla, place(mozilla, 1, 1, [1])),
subject.remove(mozilla)
]).then(() => {
subject.read('visited', 5, results => {
assert.equal(results.length, 0);
subject.readVisits(5, results => {
assert.equal(results.length, 0);
done();
});
});
});
});
});

View file

@ -0,0 +1,99 @@
/* global MockNavigatorDatastore, MockDatastore, Promise */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
require('/shared/js/utilities.js');
requireApp('search/test/unit/mock_search.js');
requireApp('search/js/providers/provider.js');
requireApp('search/js/providers/grid_provider.js');
// Required files for the grid and a mozapp result
require('/shared/js/l10n.js');
require('/shared/elements/gaia_grid/js/grid_icon_renderer.js');
require('/shared/elements/gaia_grid/js/grid_layout.js');
require('/shared/elements/gaia_grid/js/grid_view.js');
require('/shared/elements/gaia_grid/script.js');
require('/shared/elements/gaia_grid/js/items/grid_item.js');
require('/shared/elements/gaia_grid/js/items/bookmark.js');
require('/shared/js/sync_datastore.js');
suite('search/providers/providers', function() {
var subject;
var realDatastore;
var promiseDone = Promise.resolve({ operation: 'done' });
var ICON = 'https://mozorg.cdn.mozilla.net/media/img/favicon.ico';
suiteSetup(function() {
realDatastore = navigator.getDataStores;
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
MockNavigatorDatastore._records = {};
MockDatastore.sync = function() {
var cursor = {
next: function() {
cursor.next = () => promiseDone;
return Promise.resolve({
operation: 'add',
data: {
url: 'http://mozilla.org',
name: 'Homepage',
icon: ICON
}
});
}
};
return cursor;
};
});
suiteTeardown(function() {
navigator.getDataStores = realDatastore;
});
setup(function(done) {
requireApp('search/js/providers/bookmarks.js', function() {
subject = window.Bookmarks;
subject.grid = document.createElement('gaia-grid');
promiseDone.then(function() {
subject.init().then(done);
});
});
});
suite('search', function() {
test('renders data url', function(done) {
subject.search('mozilla').then((results) => {
assert.equal(results[0].data.detail.url, 'http://mozilla.org');
assert.equal(results[0].data.detail.name, 'Homepage');
assert.equal(results[0].data.detail.id, 'http://mozilla.org');
assert.equal(results[0].data.detail.icon, ICON);
done();
});
});
test('search name', function(done) {
subject.search('homepage').then((results) => {
assert.equal(results[0].data.detail.name, 'Homepage');
done();
});
});
});
suite('get', function() {
test('ok', function() {
var bookmark = subject.get('http://mozilla.org');
assert.equal(bookmark.data.detail.url, 'http://mozilla.org');
assert.equal(bookmark.data.detail.name, 'Homepage');
assert.equal(bookmark.data.detail.id, 'http://mozilla.org');
assert.equal(bookmark.data.detail.icon, ICON);
});
test('fail', function() {
var bookmark = subject.get('http://catanddog.org');
assert.isNull(bookmark);
});
});
});

View file

@ -0,0 +1,94 @@
'use strict';
/* global MocksHelper, MockMozContacts, MockContactsList, MockMozActivity,
Search */
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/js/url_helper.js');
requireApp('search/test/unit/mock_search.js');
requireApp('search/js/providers/provider.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
require('/apps/communications/contacts/test/unit/mock_contacts_list.js');
var mocksForMarketplaceProvider = new MocksHelper([
'MozActivity'
]).init();
suite('search/providers/contacts', function() {
mocksForMarketplaceProvider.attachTestHelpers();
var fakeElement, stubById, subject;
suiteSetup(function() {
navigator.mozContacts = MockMozContacts;
sinon.stub(navigator.mozContacts, 'find', function() {
return {
set onsuccess(cb) {
cb(MockContactsList());
},
set onerror(cb) {
},
result: MockContactsList()
};
});
});
setup(function(done) {
fakeElement = document.createElement('div');
fakeElement.style.cssText = 'height: 100px; display: block;';
stubById = this.sinon.stub(document, 'getElementById')
.returns(fakeElement.cloneNode(true));
requireApp('search/js/providers/contacts.js', function() {
subject = Search.providers.Contacts;
subject.init();
done();
});
});
teardown(function() {
stubById.restore();
});
suite('click', function() {
test('launches the web activity', function() {
var searchCloseStub = this.sinon.stub(Search, 'close');
subject.click({
target: {
dataset: {
contactId: 'mozilla'
}
}
});
var activityInfo = MockMozActivity.calls[0];
assert.equal(activityInfo.name, 'open');
assert.equal(activityInfo.data.params.id, 'mozilla');
assert.ok(searchCloseStub.calledOnce);
});
});
suite('search', function() {
setup(function() {
this.sinon.stub(URL, 'createObjectURL');
});
test('clears results', function() {
var stub = this.sinon.stub(subject, 'clear');
subject.search(null, function() {});
assert.ok(stub.calledOnce);
});
test('contact is rendered', function(done) {
subject.search('stub content').then((results) => {
Search.collect(subject, results);
var contact = subject.container.querySelector('.result');
assert.equal(contact.querySelector('.title').innerHTML, 'Pepito A');
assert.equal(contact.getAttribute('aria-label'), 'Pepito A');
assert.equal(contact.getAttribute('role'), 'link');
assert.equal(contact.querySelector('.icon img').getAttribute('role'),
'presentation');
done();
});
});
});
});

View file

@ -0,0 +1,156 @@
'use strict';
/* global Search, MockNavigatormozApps, MockNavigatorSettings */
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp('search/test/unit/mock_search.js');
requireApp('search/js/providers/provider.js');
requireApp('search/js/providers/grid_provider.js');
suite('search/providers/local_apps', function() {
var realMozApps;
var realMozSettings;
var _apps, _blValue;
suiteSetup(function() {
_apps = [{
manifest: {
entry_points: {
'fakeapp1-1': {
launch_path: '/fakeapp1-1/index.html',
name: 'Mozilla Fake App 1 - 1'
},
'fakeapp1-2': {
launch_path: '/fakeapp1-2/index.html',
name: 'Mozilla Fake App 1 - 2'
}
},
name: 'Mozilla Fake App 1'
},
manifestURL: 'http://fakeapp1/manifest.webapp'
},{
manifest: {
launch_path: '/fakeapp2/index.html',
name: 'Mozilla Fake App 2'
},
manifestURL: 'http://fakeapp2/manifest.webapp'
},{
manifest: {
launch_path: '/fakeapp3/index.html',
name: 'Mooilla Fake App 3',
short_name: 'shorty'
}
},{
manifest: {
launch_path: '/fakeapp4/index.html',
name: 'Blah blah',
locales: {
foobar: {
name: 'localized app'
}
}
}
}];
_blValue = ['/fakeapp1-1/index.html'];
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
navigator.mozApps.mApps = _apps;
navigator.mozSettings.createLock().set({
'app.launch_path.blacklist': _blValue
});
});
suiteTeardown(function() {
navigator.mozApps = realMozApps;
navigator.mozSettings = realMozSettings;
});
suite('Blacklist', function() {
var subject;
setup(function(done) {
requireApp('search/js/providers/local_apps.js', function() {
subject = Search.providers.LocalApps;
done();
});
});
teardown(function() {
MockNavigatormozApps.mTeardown();
});
test('CreateBlacklist returns correct blacklist', function(done) {
subject.createBlacklist().then(() => {
assert.equal(subject.blacklist, _blValue);
done();
});
});
});
suite('Search', function() {
var subject;
setup(function(done) {
requireApp('search/js/providers/local_apps.js', function() {
subject = Search.providers.LocalApps;
subject.apps = _apps;
subject.blacklist = _blValue;
subject.createAppListing();
done();
});
});
teardown(function() {
MockNavigatormozApps.mTeardown();
});
test('Search returns correct applications', function() {
var results = subject.find('moz');
assert.equal(results.length, 2);
});
test('Search returns correct applications', function() {
var results = subject.find('mo');
assert.equal(results.length, 3);
});
test('Search for localized apps', function() {
document.documentElement.lang = 'foobar';
var results = subject.find('localized');
assert.equal(results.length, 1);
});
test('Search returns correct applications', function() {
var results = subject.find('shorty');
assert.equal(results.length, 1);
});
test('hidden role which downloads is not listed', function() {
var resultLength = subject.appListing.length;
var mockApp = {
manifestURL: 'marketplace/langpack',
downloading: true,
manifest: {
role: 'langpack'
}
};
navigator.mozApps.mgmt.oninstall({
application: mockApp
});
mockApp.ondownloadapplied();
assert.equal(resultLength, subject.appListing.length);
});
});
});

View file

@ -0,0 +1,108 @@
'use strict';
/* global MocksHelper, Search */
/* global MockNavigatorSettings */
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/mock_l10n.js');
requireApp('search/test/unit/mock_search.js');
requireApp('search/js/providers/provider.js');
requireApp('search/js/providers/grid_provider.js');
// Required files for the grid and a marketplaceapp result
require('/shared/js/l10n.js');
require('/shared/elements/gaia_grid/js/grid_icon_renderer.js');
require('/shared/elements/gaia_grid/js/grid_layout.js');
require('/shared/elements/gaia_grid/js/grid_view.js');
require('/shared/elements/gaia_grid/script.js');
require('/shared/elements/gaia_grid/js/items/grid_item.js');
require('/shared/elements/gaia_grid/js/items/bookmark.js');
require('/shared/elements/gaia_grid/js/items/marketplace_app.js');
var mocksForMarketplaceProvider = new MocksHelper([
'MozActivity'
]).init();
suite('search/providers/marketplace', function() {
mocksForMarketplaceProvider.attachTestHelpers();
var fakeElement, stubById, subject, realMozSettings;
setup(function(done) {
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
navigator.mozSettings.createLock().set({
'search.marketplace.url': 'http://localhost?{q}'
});
fakeElement = document.createElement('div');
fakeElement.style.cssText = 'height: 100px; display: block;';
stubById = this.sinon.stub(document, 'getElementById')
.returns(fakeElement.cloneNode(true));
requireApp('search/js/providers/marketplace.js', function() {
subject = Search.providers.Marketplace;
subject.init();
done();
});
});
teardown(function() {
stubById.restore();
});
suite('mock response', function() {
var marketplaceContent = [{
'manifest_url': 'http://fakeapp1.mozilla.org/manifest.webapp',
'name': 'FIRST Marketplace App',
'icon': ''
}, {
'manifest_url': 'http://fakeapp2.mozilla.org/manifest.webapp',
'name': 'SECOND Marketplace App',
'icon': ''
}];
var requests = [];
var xhr;
setup(function() {
xhr = sinon.useFakeXMLHttpRequest();
requests = [];
xhr.onCreate = function(req) {
setTimeout(function() {
requests.push(req);
req.responseText = JSON.stringify(marketplaceContent);
req.status = 200;
req.onload();
});
};
});
teardown(function() {
subject.request = null;
xhr.restore();
});
test('escapes search terms', function(done) {
subject.search('foo#').then(function(results) {
assert.equal(requests[0].url, 'http://localhost?foo%23');
done();
});
});
test('renders all results', function(done) {
subject.search('fake').then(results => {
assert.equal(results.length, 2);
done();
});
// setTimeout to ensure that the search microtask fires.
setTimeout(function() {
var req = requests[0];
req.responseText = JSON.stringify(marketplaceContent);
req.onload();
});
});
});
});

View file

@ -0,0 +1,68 @@
'use strict';
/* global Provider, MocksHelper */
requireApp('search/shared/js/everythingme/eme.js');
requireApp('search/js/providers/provider.js');
require('/shared/test/unit/mocks/mock_icons_helper.js');
var mocks = new MocksHelper([
'IconsHelper'
]).init();
suite('search/providers/provider', function() {
var fakeElement, stubById, subject;
mocks.attachTestHelpers();
setup(function() {
fakeElement = document.createElement('div');
fakeElement.style.cssText = 'height: 100px; display: block;';
stubById = this.sinon.stub(document, 'getElementById')
.returns(fakeElement.cloneNode(true));
subject = new Provider();
});
teardown(function() {
stubById.restore();
});
suite('constructor', function() {
test('everything looks ok', function() {
assert.ok(subject.name);
});
});
suite('init', function() {
test('sets up container', function() {
subject.init();
assert.ok(subject.container);
});
});
suite('render', function() {
test('escapes HTML', function() {
subject.init();
subject.render([{
title: '<hello>',
meta: '<world>',
dataset: {}
}]);
var title = subject.container.querySelector('.title');
assert.equal(title.innerHTML, '&lt;hello&gt;');
var meta = subject.container.querySelector('.meta');
assert.equal(meta.innerHTML, '&lt;world&gt;');
});
});
suite('clear', function() {
test('clears container', function() {
subject.init();
subject.container.innerHTML = 'not empty';
subject.clear();
assert.equal(subject.container.innerHTML, '');
});
});
});

View file

@ -0,0 +1,37 @@
'use strict';
/* global MocksHelper, Search */
requireApp('search/test/unit/mock_search.js');
requireApp('search/js/providers/provider.js');
require('/shared/js/metrics_helper.js');
requireApp('search/test/unit/mock_provider.js');
require('/shared/test/unit/mocks/mock_search_provider.js');
var mocksForSuggestionsProvider = new MocksHelper([
'SearchProvider'
]).init();
suite('search/providers/suggestions', function() {
mocksForSuggestionsProvider.attachTestHelpers();
var subject;
requireApp('search/js/providers/suggestions.js', function(done) {
subject = Search.providers.Suggestions;
subject.init();
done();
});
suite('click', function() {
test('should report to metrics', function() {
var stub = this.sinon.stub(window.MetricsHelper.prototype, 'report');
subject.click({
target: {
dataset: {
suggestions: 'mozilla'
}
}
});
assert.ok(stub.calledWith('websearch', 'testProvider'));
});
});
});

View file

@ -0,0 +1,734 @@
'use strict';
/* global MockNavigatormozApps, MockNavigatormozSetMessageHandler,
Search, MockProvider, MockasyncStorage, Promise, MocksHelper */
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/mock_search_provider.js');
require('/shared/js/url_helper.js');
require('/shared/js/dedupe.js');
require('/js/contextmenu.js');
require('/shared/js/metrics_helper.js');
require('/shared/test/unit/mocks/mock_async_storage.js');
requireApp('search/test/unit/mock_provider.js');
var mocksForSearch = new MocksHelper([
'SearchProvider'
]).init();
suite('search/search', function() {
var realAsyncStorage;
var realMozApps;
var realSetMessageHandler;
var realOnLine;
var clock;
mocksForSearch.attachTestHelpers();
function removeProvider(provider) {
delete Search.providers[provider.name];
}
suiteSetup(function(done) {
loadBodyHTML('/index.html');
realSetMessageHandler = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realAsyncStorage = window.asyncStorage;
window.asyncStorage = MockasyncStorage;
window.SettingsListener = {
observe: function() {}
};
clock = sinon.useFakeTimers();
requireApp('search/js/search.js', function() {
// assert.equal(Search.toShowNotice, null);
// Search.toShowNotice = false;
Search._port = { postMessage: function() {} };
done();
});
});
suiteTeardown(function() {
navigator.mozSetMessageHandler = realSetMessageHandler;
navigator.mozApps = realMozApps;
window.asyncStorage = realAsyncStorage;
clock.restore();
delete window.SettingsListener;
});
setup(function() {
MockNavigatormozSetMessageHandler.mSetup();
});
teardown(function() {
MockNavigatormozSetMessageHandler.mTeardown();
MockNavigatormozApps.mTeardown();
});
suite('init', function() {
test('will call provider init method', function() {
var initCalled;
this.sinon.spy(window.MetricsHelper.prototype, 'init');
this.sinon.spy(navigator, 'mozSetMessageHandler');
Search.providers = [{
init: function() {
initCalled = true;
},
search: function() {},
abort: function() {}
}];
Search.init();
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.equal(MockNavigatormozApps.mLastConnectionKeyword,
'search-results');
MockNavigatormozApps.mLastConnectionCallback([]);
assert.ok(initCalled);
Search.providers = [];
assert.isFalse(Search.suggestionsWrapper.classList.contains('offline'));
assert.ok(window.MetricsHelper.prototype.init.calledOnce);
assert.ok(navigator.mozSetMessageHandler.called);
});
});
suite('offline', function() {
suiteSetup(function() {
realOnLine = Object.getOwnPropertyDescriptor(navigator, 'onLine');
Object.defineProperty(navigator, 'onLine', {
configurable: true,
get: function() { return false; }
});
});
suiteTeardown(function() {
if (realOnLine) {
Object.defineProperty(navigator, 'onLine', realOnLine);
}
});
test('init while offline', function() {
this.sinon.spy(Search, 'initConnectivityCheck');
Search.init();
assert.ok(Search.initConnectivityCheck.calledOnce);
assert.isTrue(Search.searchResults.classList.contains('offline'));
});
});
suite('provider', function() {
test('increments number of providers', function() {
function numProviders() {
return Object.keys(Search.providers).length;
}
var count = numProviders();
Search.provider({
name: 'Foo'
});
assert.equal(count + 1, numProviders());
removeProvider({
name: 'Foo'
});
assert.equal(count, numProviders());
});
});
suite('initNotice', function() {
test('gets the settingsLink on every call', function() {
var stub = this.sinon.stub(document, 'getElementById').returns({
addEventListener: this.sinon.spy()
});
Search.initNotice();
Search.initNotice();
assert.isTrue(stub.withArgs('settings-link').callCount === 2);
});
});
suite('scrolling focus', function() {
test('we grab focus when scrolling results', function() {
var stub = this.sinon.stub(window, 'focus');
window.dispatchEvent(new window.Event('scroll'));
assert.ok(stub.calledOnce);
});
});
suite('dispatchMessage', function() {
test('dispatches messages based on action', function() {
var stub = this.sinon.stub(Search, 'change');
Search.dispatchMessage({
data: {
action: 'change',
input: 'http://mozilla.org'
}
});
assert.ok(stub.calledOnce);
});
});
suite('change', function() {
setup(function() {
var fakeProvider = {
name: 'Foo',
search: function() {
return Promise.resolve();
},
abort: function() {},
clear: function() {}
};
Search.provider(fakeProvider);
});
test('only searches once if called twice rapidly', function() {
var stub = this.sinon.stub(Search.providers.Foo, 'search').returns(
new Promise(() => {}));
Search.change({
data: {
input: 'a'
}
});
Search.change({
data: {
input: 'a'
}
});
assert.ok(stub.notCalled);
clock.tick(1000); // For typing timeout
assert.ok(stub.calledOnce);
});
test('full URLs do not get sent', function() {
var remoteProvider = {
name: 'remoteguy',
remote: true,
search: function() {},
abort: function() {},
clear: function() {}
};
Search.provider(remoteProvider);
Search.suggestionsEnabled = true;
var stub = this.sinon.stub(Search.providers.remoteguy, 'search');
Search.change({
data: {
input: 'http://mozilla.org'
}
});
clock.tick(1000); // For typing timeout
assert.ok(stub.notCalled);
});
test('aborting will cancel search timeout', function() {
var stub = this.sinon.stub(Search.providers.Foo, 'search');
Search.change({
data: {
input: 'a'
}
});
Search.change({
data: {
input: 'a'
}
});
assert.ok(stub.notCalled);
Search.abort();
clock.tick(1000); // For typing timeout
assert.ok(stub.notCalled);
});
test('when a private browser', function() {
var remoteProvider = {
name: 'remoteguy',
remote: true,
search: function() {},
abort: function() {},
clear: function() {}
};
Search.provider(remoteProvider);
Search.suggestionsEnabled = true;
var stub = this.sinon.stub(Search.providers.remoteguy, 'search');
Search.change({
data: {
input: 'search me',
isPrivateBrowser: true
}
});
clock.tick(1000); // For typing timeout
assert.ok(stub.notCalled);
});
});
suite('submit', function() {
test('Navigates to a URL', function() {
var stub = this.sinon.stub(Search, 'navigate');
Search.dispatchMessage({
data: {
action: 'submit',
input: 'http://mozilla.org'
}
});
clock.tick(1000); // For typing timeout
assert.ok(stub.calledOnce);
});
test('Fires correct Event to metrics search for search term', function() {
var stub = this.sinon.stub(window.MetricsHelper.prototype, 'report');
Search.dispatchMessage({
data: {
action: 'submit',
input: 'searchterm'
}
});
clock.tick(1000); // For typing timeout
assert.ok(stub.calledWith('websearch', 'testProvider'));
});
});
suite('clear', function() {
var called = 0;
suiteSetup(function() {
Search.providers = {
Fake: {
clear: function() {
called++;
},
abort: function() {}
}
};
});
test('calls the provider.clear method', function() {
Search.clear();
assert.equal(called, 1);
});
});
suite('abort', function() {
test('calls abort method of provider', function() {
Search.providers = {
Fake: {
clear: function() {},
abort: function() {}
}
};
var abortStub = this.sinon.stub(Search.providers.Fake, 'abort');
Search.abort();
assert.ok(abortStub.calledOnce);
});
});
suite('close', function() {
test('posts a message to the port', function() {
var stub = this.sinon.stub(Search._port, 'postMessage');
Search.close();
assert.ok(stub.calledWith({action: 'hide'}));
});
});
suite('navigate', function() {
test('Open activity is fired', function() {
var url = 'http://mozilla.org';
var stub = this.sinon.stub(window, 'open');
Search.navigate(url);
assert.ok(stub.calledOnce);
});
});
suite('expandSearch', function() {
setup(function() {
Search.providers = {
WebResults: {
clear: function() {},
abort: function() {},
search: function() {
return new Promise(() => {});
},
fullscreen: function() {}
}
};
});
test('calls search for WebResults', function() {
var stub = this.sinon.stub(Search.providers.WebResults,'fullscreen');
Search.expandSearch();
assert.ok(stub.calledOnce);
});
});
suite('setInput', function() {
test('posts a message to the port', function() {
var stub = this.sinon.stub(Search._port, 'postMessage');
this.sinon.stub(Search, 'expandSearch');
Search.setInput('foo');
assert.ok(stub.calledWith({action: 'input', input: 'foo'}));
});
});
suite('handleActivityEvents', function() {
test('handle search activity', function() {
this.sinon.stub(Search, 'submit');
Search.handleActivityEvents({
source: {
name: 'search',
data: {keyword: 'mozilla'}
}
});
assert.ok(Search.submit.calledWith({data: {
input: 'mozilla'
}}));
});
});
suite('collect', function() {
setup(function() {
Search.dedupe.exactResults = {};
Search.dedupe.fuzzyResults = {};
});
// Suppport functions
function exactProvider() {
return {
dedupes: true,
dedupeStrategy: 'exact',
render: function() {}
};
}
function fuzzyProvider() {
return {
dedupes: true,
dedupeStrategy: 'fuzzy',
render: function() {}
};
}
test('provider does not de-dupe', function() {
var results = [
{dedupeId: 'a'},
{dedupeId: 'b'}
];
var provider = {
dedupes: false,
render: function() {}
};
var renderStub = this.sinon.stub(provider, 'render');
Search.collect(provider, results);
assert.equal(renderStub.getCall(0).args[0].length, 2);
});
test('when no de-duplication necessary', function() {
var provider1 = exactProvider();
var provider2 = exactProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, [{dedupeId: 'a'}]);
Search.collect(provider2, [{dedupeId: 'b'}]);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 1);
});
test('de-duplicates an exact manifestURL', function() {
var results1 = [
{dedupeId: 'http://mozilla.org/manifest.webapp'}
];
var results2 = [
{dedupeId: 'http://mozilla.org/manifest.webapp'}
];
var provider1 = exactProvider();
var provider2 = exactProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('de-duplicates a fuzzy match /w same domain', function() {
var results1 = [
{dedupeId: 'http://mozilla.org/manifest.webapp'}
];
var results2 = [
{dedupeId: 'http://mozilla.org/awesome/app'}
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('de-duplicates a fuzzy match /w subdomain', function() {
var results1 = [
{dedupeId: 'http://mozilla.org/manifest.webapp'}
];
var results2 = [
{dedupeId: 'http://omggame.mozilla.org/awesome/app'}
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('attempt to avoid de-duplicating second level domains', function() {
var results1 = [
{dedupeId: 'http://mozilla.co.org/manifest.webapp'}
];
var results2 = [
{dedupeId: 'http://bob.co.org/awesome/app'}
];
var provider1 = exactProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 1);
});
test('installed hosted app /w web result', function() {
var results1 = [
{dedupeId:
'https://m.facebook.com/openwebapp/manifest.webapp'
}
];
var results2 = [
{dedupeId: 'https://www.facebook.com/'}
];
var provider1 = exactProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('mis-matching domain against manifest url', function() {
var results1 = [
{dedupeId:
'https://bits.wikimedia.org/wikipediamobilefirefoxos/manifest.webapp'
}
];
var results2 = [
{dedupeId: 'http://en.m.wikipedia.org'}
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('common domain parts filtered out', function() {
Search.dedupe.fuzzyResults = {
'touch': true,
'mobile': true
};
var results1 = [
{dedupeId: 'https://touch.www.linkedin.com/login.html'}
];
var results2 = [
{dedupeId: 'https://touch.mozilla.org/fake.html'}
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 1);
});
test('bug 1030713 - subdomain dededupe matching', function() {
var results1 = [
{dedupeId: 'http://translate.google.com'}
];
var results2 = [
{dedupeId: 'http://mail.google.com/tasks'},
{dedupeId: 'http://drive.google.com/keep'},
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 2);
});
test('bug 1030713 - subdomain dededupe matching with path', function() {
var results1 = [
{dedupeId: 'http://drive.google.com/keep'}
];
var results2 = [
{dedupeId: 'http://mail.google.com/tasks'},
{dedupeId: 'http://translate.google.com'},
];
var provider1 = fuzzyProvider();
var provider2 = fuzzyProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 2);
});
test('exact provider does not de-dupe against itself', function() {
var results = [
{dedupeId: 'https://mozilla.org/index.html'},
{dedupeId: 'https://mozilla.org/sauce.html'}
];
var provider = exactProvider();
var renderStub = this.sinon.stub(provider, 'render');
Search.collect(provider, results);
assert.equal(renderStub.getCall(0).args[0].length, 2);
});
test('fuzzy provider does not de-dupe against itself', function() {
var results = [
{dedupeId: 'https://mozilla.org/index.html'},
{dedupeId: 'https://mozilla.org/sauce.html'}
];
var provider = fuzzyProvider();
var renderStub = this.sinon.stub(provider, 'render');
Search.collect(provider, results);
assert.equal(renderStub.getCall(0).args[0].length, 2);
});
test('exact provider ignores querystring', function() {
var results1 = [
{dedupeId: 'https://mozilla.org/index.html?ignoreme=true'}
];
var results2 = [
{dedupeId: 'https://mozilla.org/index.html'}
];
var provider1 = exactProvider();
var provider2 = exactProvider();
var renderStub1 = this.sinon.stub(provider1, 'render');
var renderStub2 = this.sinon.stub(provider2, 'render');
Search.collect(provider1, results1);
Search.collect(provider2, results2);
assert.equal(renderStub1.getCall(0).args[0].length, 1);
assert.equal(renderStub2.getCall(0).args[0].length, 0);
});
test('Dont search remote providers when suggestions disabled', function() {
var localProvider = new MockProvider('local');
var remoteProvider = new MockProvider('remote');
remoteProvider.remote = true;
var remoteStub = this.sinon.stub(remoteProvider, 'search')
.returns(new Promise(() => {}));
var localStub = this.sinon.stub(localProvider, 'search')
.returns(new Promise(() => {}));
Search.provider(localProvider);
Search.provider(remoteProvider);
Search.suggestionsEnabled = false;
Search.change({data: {input: 'test'}});
clock.tick(1000);
assert.ok(remoteStub.notCalled);
assert.ok(localStub.calledOnce);
removeProvider(localProvider);
removeProvider(remoteProvider);
});
test('Search all providers when suggestions enabled', function() {
var localProvider = new MockProvider('local');
var remoteProvider = new MockProvider('remote');
remoteProvider.remote = true;
var remoteStub = this.sinon.stub(remoteProvider, 'search')
.returns(new Promise(() => {}));
var localStub = this.sinon.stub(localProvider, 'search')
.returns(new Promise(() => {}));
Search.provider(localProvider);
Search.provider(remoteProvider);
Search.suggestionsEnabled = true;
Search.change({data: {input: 'test'}});
clock.tick(1000);
assert.ok(remoteStub.calledOnce);
assert.ok(localStub.calledOnce);
removeProvider(localProvider);
removeProvider(remoteProvider);
});
});
});

View file

@ -0,0 +1,4 @@
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/load_body_html_helper.js');

View file

@ -0,0 +1,78 @@
/* globals DateHelper */
'use strict';
requireApp('search/shared/js/utilities.js');
suite('Date Helper', function() {
function assertMidnight(date) {
assert.equal(date.getHours(), 0);
assert.equal(date.getMinutes(), 0);
assert.equal(date.getSeconds(), 0);
assert.equal(date.getMilliseconds(), 0);
}
suite('started', function() {
test('todayStarted', function() {
var now = new Date();
var todayStartedTimestamp = DateHelper.todayStarted();
var todayStarted = new Date(todayStartedTimestamp);
assert.equal(todayStarted.getFullYear(), now.getFullYear());
assert.equal(todayStarted.getMonth(), now.getMonth());
assert.equal(todayStarted.getDate(), now.getDate());
assertMidnight(todayStarted);
});
test('yesterdayStarted', function() {
var now = new Date();
var yesterdayStartedTimestamp = DateHelper.yesterdayStarted();
var yesterdayStarted = new Date(yesterdayStartedTimestamp);
assert.equal(true, yesterdayStartedTimestamp <= now.valueOf());
assertMidnight(yesterdayStarted);
});
test('thisWeekStarted', function() {
var now = new Date();
var weekStartedTimestamp = DateHelper.thisWeekStarted();
var weekStarted = new Date(weekStartedTimestamp);
assert.equal(true, weekStartedTimestamp <= now.valueOf());
assert.equal(weekStarted.getDay(), 0);
assertMidnight(weekStarted);
});
test('thisMonthStarted', function() {
var now = new Date();
var monthStartedTimestamp = DateHelper.thisMonthStarted();
var monthStarted = new Date(monthStartedTimestamp);
assert.equal(true, monthStartedTimestamp <= now.valueOf());
assert.equal(monthStarted.getFullYear(), now.getFullYear());
assert.equal(monthStarted.getMonth(), now.getMonth());
assert.equal(monthStarted.getDate(), 1);
assertMidnight(monthStarted);
});
test('lastSixMonthsStarted', function() {
var sixMonthsStarted = DateHelper.lastSixMonthsStarted();
var now = new Date().valueOf();
var delta = now - sixMonthsStarted;
assert.equal(true, delta >= 2629743830 * 6);
assert.equal(true, delta < 2629743830 * 7);
});
test('thisYearStarted', function() {
var now = new Date();
var yearStartedTimestamp = DateHelper.thisYearStarted();
var yearStarted = new Date(yearStartedTimestamp);
assert.equal(true, yearStartedTimestamp <= now.valueOf());
assert.equal(yearStarted.getFullYear(), now.getFullYear());
assert.equal(yearStarted.getMonth(), 0);
assert.equal(yearStarted.getDate(), 1);
assertMidnight(yearStarted);
});
});
});