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,27 @@
'use strict';
/* jshint node: true, mocha: true */
/* global suiteSetup */
var helper = require('helper');
suite('Network-alerts tests', function() {
suiteSetup(helper.cleanupWorkspace);
teardown(helper.cleanupWorkspace);
test('APP=network-alerts make', function(done) {
helper.exec('APP=network-alerts make', function(error, stdout, stderr) {
helper.checkError(error, stdout, stderr);
var zipPath = process.cwd() +
'/profile/webapps/network-alerts.gaiamobile.org/application.zip';
var checkList = ['manifest.webapp'];
checkList.forEach(function(filePath) {
helper.checkFilePathInZip(zipPath, filePath);
});
done();
});
});
});

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,204 @@
/* global Attention,
MockL10n,
MockNavigatormozApps,
MockNotification,
MockNotifications,
NotificationHelper,
MocksHelper,
Notify,
Utils
*/
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_notification.js');
require('/shared/test/unit/mocks/mock_notification_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/test/unit/mock_utils.js');
require('/test/unit/mock_notify.js');
require('/js/attention/attention.js');
var mocksHelperForAttention = new MocksHelper([
'Notify',
'Notification',
'NotificationHelper',
'Utils'
]);
suite('Network Alerts - Attention Screen', function() {
var title,
body;
var realL10n = navigator.mozL10n;
var realMozApps = navigator.mozApps;
var realOpener;
mocksHelperForAttention.attachTestHelpers();
suiteSetup(function() {
realOpener = Object.getOwnPropertyDescriptor(window, 'opener');
Object.defineProperty(window, 'opener', {
writable: true,
enumerable: true,
configurable: true,
value: {
close: function() {}
}
});
});
suiteTeardown(function() {
Object.defineProperty(window, 'opener', realOpener);
});
setup(function() {
loadBodyHTML('/attention.html');
title = 'title-id';
body = 'some body';
this.sinon.stub(Utils, 'parseParams');
this.sinon.stub(window.opener, 'close');
this.sinon.stub(Notify, 'notify');
navigator.mozL10n = MockL10n;
this.sinon.stub(navigator.mozL10n, 'once').yields();
navigator.mozApps = MockNavigatormozApps;
});
teardown(function() {
navigator.mozL10n = realL10n;
MockNavigatormozApps.mTeardown();
navigator.mozApps = realMozApps;
});
suite('display from received message', function() {
setup(function() {
Utils.parseParams.returns({
title: title,
body: body
});
Attention.init();
Attention.render();
});
test('form is properly displayed', function() {
assert.equal(
document.querySelector('h1').getAttribute('data-l10n-id'),
title,
'The title is properly displayed'
);
assert.equal(
document.querySelector('p').textContent, body,
'The body is properly displayed'
);
});
test('Notification should be displayed and alert is played', function() {
this.sinon.stub(NotificationHelper, 'send').returns(Promise.resolve({}));
MockNavigatormozApps.mTriggerLastRequestSuccess();
sinon.assert.calledOnce(NotificationHelper.send);
sinon.assert.calledWithMatch(NotificationHelper.send, title, {
body: body,
icon: 'sms',
data: { title }
});
sinon.assert.called(Notify.notify);
});
test('Notification should not be displayed if mozApp got error',
function() {
MockNavigatormozApps.mLastRequest.onerror();
assert.equal(MockNotifications.length, 0);
});
test('click button: closes window', function() {
document.querySelector('button').click();
sinon.assert.called(window.opener.close);
});
suite('on visibility change', function() {
var realVisibility,
isDocumentHidden;
suiteSetup(function() {
realVisibility = Object.getOwnPropertyDescriptor(document, 'hidden');
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() {
return isDocumentHidden;
}
});
});
suiteTeardown(function() {
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() {
return realVisibility;
}
});
});
setup(function() {
document.querySelector('h1').style.height = '10px';
});
test('do nothing while app visible',function() {
isDocumentHidden = false;
document.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.notCalled(window.opener.close);
});
test('do nothing while app hidden but not resized',function() {
isDocumentHidden = true;
document.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.notCalled(window.opener.close);
});
test('close window while app hidden and resized',function() {
isDocumentHidden = true;
document.querySelector('h1').style.height = '';
document.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.called(window.opener.close);
});
});
});
suite('display from notification,', function() {
setup(function() {
Utils.parseParams.returns({
title: title,
body: body,
notification: 1
});
MockNotification.mTeardown();
Attention.init();
Attention.render();
});
test('Notification should not be displayed, and no alert should be played',
function() {
assert.isUndefined(
MockNotifications[0],
'should not send a new notification'
);
sinon.assert.notCalled(Notify.notify);
});
});
});

View file

@ -0,0 +1,226 @@
/*global
AudioContext,
MockAudioContext,
MockNavigatorSettings,
MocksHelper,
Notify
*/
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_audio.js');
require('/js/attention/notify.js');
var mocksHelperNotifications = new MocksHelper([
'AudioContext'
]).init();
suite('check the ringtone and vibrate function', function() {
var realMozSettings;
var getStub, setStub;
var isDocumentHidden = true;
mocksHelperNotifications.attachTestHelpers();
setup(function() {
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
this.sinon.stub(navigator, 'vibrate');
this.sinon.spy(window, 'AudioContext');
this.sinon.stub(
MockAudioContext.prototype, 'createOscillator',
() => sinon.stub({
frequency: { value: 0 },
type: '',
connect() {},
start() {},
stop() {}
})
);
this.sinon.stub(
MockAudioContext.prototype, 'createGain',
() => sinon.stub({
gain: sinon.stub({ setValueCurveAtTime() {} }),
connect() {}
})
);
getStub = sinon.stub();
setStub = sinon.stub();
this.sinon.stub(MockNavigatorSettings, 'createLock').returns({
get: getStub,
set: setStub
});
Object.defineProperty(document, 'hidden', {
configurable: true,
get: () => isDocumentHidden
});
});
teardown(function() {
navigator.mozSettings = realMozSettings;
delete document.hidden;
});
function sendVisibilityEvent() {
var visibilityEvent = new CustomEvent(
'visibilitychange',
{ bubbles: true }
);
window.dispatchEvent(visibilityEvent);
}
function assertVibrate() {
sinon.assert.calledWith(
navigator.vibrate,
[2000, 500, 1000, 500, 1000, 500, 2000, 500, 1000, 500, 1000, 500]
);
}
function assertAudio() {
sinon.assert.calledWith(AudioContext, 'notification');
sinon.assert.calledTwice(AudioContext.prototype.createOscillator);
sinon.assert.calledOnce(AudioContext.prototype.createGain);
var audioCtx = AudioContext.firstCall.returnValue;
var gainNode = AudioContext.prototype.createGain.firstCall.returnValue;
var o1 = AudioContext.prototype.createOscillator.firstCall.returnValue;
var o2 = AudioContext.prototype.createOscillator.secondCall.returnValue;
assert.equal(o1.type, 'sine');
assert.equal(o2.type, 'sine');
// either one case or the other
try {
assert.equal(o1.frequency.value, 853);
assert.equal(o2.frequency.value, 960);
} catch(e) {
assert.equal(o2.frequency.value, 853);
assert.equal(o1.frequency.value, 960);
}
sinon.assert.called(o1.connect);
sinon.assert.called(o2.connect);
sinon.assert.calledWith(o1.connect, gainNode);
sinon.assert.calledWith(o2.connect, gainNode);
sinon.assert.calledWith(gainNode.connect, audioCtx.destination);
sinon.assert.called(o1.start);
sinon.assert.called(o2.start);
sinon.assert.called(o1.stop);
sinon.assert.called(o2.stop);
sinon.assert.calledWith(o1.stop, 11);
sinon.assert.calledWith(o2.stop, 11);
sinon.assert.called(gainNode.gain.setValueCurveAtTime);
sinon.assert.calledWith(
gainNode.gain.setValueCurveAtTime,
sinon.match.instanceOf(Float32Array), 0, 11
);
}
function setSettings(settings) {
for (var key in settings) {
getStub.withArgs(key).returns(Promise.resolve({
[key]: settings[key]
}));
}
}
suite('mozSettings is unavailable', function() {
test('We do nothing but we do not stop', function(done) {
delete navigator.mozSettings;
Notify.notify().then(() => {
sinon.assert.notCalled(AudioContext);
sinon.assert.notCalled(navigator.vibrate);
}).then(done, done);
});
});
suite('volume is 1 and vibration is enabled', function() {
test('play ringtone and vibrate', function(done) {
setSettings({
'audio.volume.notification': 1,
'vibration.enabled': true
});
Notify.notify().then(() => {
// let's be sure we vibrate after the visibility event only
sinon.assert.notCalled(navigator.vibrate);
// As document is not shown in the test, we launch the
// event of visibility
sendVisibilityEvent();
assertAudio();
assertVibrate();
}).then(done, done);
});
});
suite('volume is 1 and vibration is disabled', function() {
test('play ringtone and do not vibrate', function(done) {
setSettings({
'audio.volume.notification': 1,
'vibration.enabled': false
});
Notify.notify().then(() => {
sinon.assert.notCalled(navigator.vibrate);
assertAudio();
}).then(done, done);
});
});
suite('volume is 0 and vibration is enabled', function() {
test('do not play ringtone and vibrate', function(done) {
setSettings({
'audio.volume.notification': 0,
'vibration.enabled': true
});
Notify.notify().then(() => {
sinon.assert.notCalled(navigator.vibrate);
// As document is not shown in the test, we launch the
// event of visibility
sendVisibilityEvent();
sinon.assert.notCalled(AudioContext);
assertVibrate();
}).then(done, done);
});
test('vibrate asap if the app is displayed', function(done) {
setSettings({
'audio.volume.notification': 0,
'vibration.enabled': true
});
isDocumentHidden = false;
Notify.notify().then(() => {
assertVibrate();
}).then(done, done);
});
});
suite('volume is 0 and vibration is disabled', function() {
test('do not play ringtone and do not vibrate', function(done) {
setSettings({
'audio.volume.notification': 0,
'vibration.enabled': false
});
Notify.notify().then(() => {
sinon.assert.notCalled(navigator.vibrate);
sinon.assert.notCalled(AudioContext);
}).then(done, done);
});
});
});

View file

@ -0,0 +1,223 @@
/* global MockNavigatorSettings
*/
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
suite('Network Alerts - cellbroadcast system message handling', function() {
var handlerStub;
var realSettings = navigator.mozSettings;
var CMAS_KEY = 'cmas.enabled';
var getStub, setStub;
function mockMessage(opt = {}) {
var message = {
serviceId: 0,
cdmaServiceCategory: null,
body: 'Some body'
};
for (var key in opt) {
message[key] = opt[key];
}
return message;
}
suiteSetup(function(done) {
if (!window.navigator.mozSetMessageHandler) {
window.navigator.mozSetMessageHandler = function() {};
}
sinon.stub(window.navigator, 'mozSetMessageHandler');
handlerStub = window.navigator.mozSetMessageHandler;
require('/js/index.js', done);
});
setup(function() {
this.sinon.stub(window, 'close');
this.sinon.stub(window, 'open');
getStub = sinon.stub().returns(Promise.resolve());
setStub = sinon.stub().returns(Promise.resolve());
this.sinon.stub(MockNavigatorSettings, 'createLock').returns({
get: getStub,
set: setStub
});
navigator.mozSettings = MockNavigatorSettings;
});
teardown(function() {
navigator.mozSettings = realSettings;
MockNavigatorSettings.mTeardown();
});
test('do not open attention screen if error returns from settings DB',
function(done) {
var message = mockMessage({ messageId: 4371 });
getStub.returns(Promise.reject(new Error('error')));
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
finished.then(() => {
sinon.assert.notCalled(window.open);
sinon.assert.called(window.close);
}).then(done, done);
});
suite('if CMAS in settings DB is off', function() {
setup(function() {
getStub.returns(Promise.resolve({
[CMAS_KEY]: {
0: false,
1: false
}
}));
});
test('do not open attention screen', function(done) {
var message = mockMessage({ messageId: 4371 });
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
finished.then(() => {
sinon.assert.notCalled(window.open);
sinon.assert.called(window.close);
}).then(done, done);
});
[4370, 4383].forEach((presidentialId) => {
test('opens an attention screen if GSM message is a presidential alert ' +
presidentialId,
function(done) {
var message = mockMessage({ messageId: presidentialId });
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
var expectedUrl = [
'attention.html?',
'title=emergency-alert-title&',
'body=Some%20body'
].join('');
finished.then(() => {
sinon.assert.calledWith(
window.open,
expectedUrl, '_blank', 'attention'
);
sinon.assert.notCalled(window.close);
}).then(done, done);
});
});
test('opens an attention screen if CDMA message is a presidential alert ',
function(done) {
var message = mockMessage({ cdmaServiceCategory: 4096 });
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
var expectedUrl = [
'attention.html?',
'title=emergency-alert-title&',
'body=Some%20body'
].join('');
finished.then(() => {
sinon.assert.calledWith(
window.open,
expectedUrl, '_blank', 'attention'
);
sinon.assert.notCalled(window.close);
}).then(done, done);
});
});
test('opens an attention screen if GSM message is CMAS', function(done) {
var message = mockMessage({ messageId: 4371 });
getStub.returns(Promise.resolve({
[CMAS_KEY]: {
0: true,
1: false
}
}));
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
var expectedUrl = [
'attention.html?',
'title=emergency-alert-title&',
'body=Some%20body'
].join('');
finished.then(() => {
sinon.assert.calledWith(
window.open,
expectedUrl, '_blank', 'attention'
);
sinon.assert.notCalled(window.close);
}).then(done, done);
});
test('do not open attention screen if GSM message is other cellbroadcast',
function() {
var message = mockMessage({ messageId: 4401 });
handlerStub.withArgs('cellbroadcast-received').yield(message);
sinon.assert.notCalled(window.open);
sinon.assert.called(window.close);
});
test('opens an attention screen if CDMA message is CMAS', function(done) {
var message = mockMessage({ cdmaServiceCategory: 4096 });
getStub.returns(Promise.resolve({
[CMAS_KEY]: {
0: true,
1: false
}
}));
var handler = handlerStub.withArgs('cellbroadcast-received').args[0][1];
var finished = handler(message);
var expectedUrl = [
'attention.html?',
'title=emergency-alert-title&',
'body=Some%20body'
].join('');
finished.then(() => {
sinon.assert.calledWith(
window.open,
expectedUrl, '_blank', 'attention'
);
sinon.assert.notCalled(window.close);
}).then(done, done);
});
test('do not open attention screen if CDMA message is other cellbroadcast',
function() {
var message = mockMessage({ cdmaServiceCategory: 4095 });
handlerStub.withArgs('cellbroadcast-received').yield(message);
sinon.assert.notCalled(window.open);
sinon.assert.called(window.close);
});
});

View file

@ -0,0 +1,6 @@
/* exported MockNotify */
'use strict';
var MockNotify = {
notify() {}
};

View file

@ -0,0 +1,11 @@
'use strict';
(function(exports) {
exports.MockUtils = {
parseParams: function() {
return {};
}
};
})(window);

View file

@ -0,0 +1,60 @@
/* global NotificationHandler
*/
'use strict';
require('/js/notification/notification.js');
suite('Network Alerts - Notification handling', function() {
setup(function() {
this.sinon.stub(window, 'close');
this.sinon.stub(window, 'open');
// We only need titleID after parsing, so mock parseParams that simply
// return fake titleID for testing.
if (!window.navigator.mozSetMessageHandler) {
window.navigator.mozSetMessageHandler = function() {};
}
this.sinon.stub(window.navigator, 'mozSetMessageHandler');
NotificationHandler.init();
});
test('opens an attention screen if user clicks the notification', function() {
var message = {
title: 'Some title',
body: 'Some body',
clicked: true,
data: { title: 'Some title' }
};
var handlerStub = window.navigator.mozSetMessageHandler;
handlerStub.withArgs('notification').yield(message);
var expectedUrl = [
'attention.html?',
'title=Some%20title&',
'body=Some%20body&',
'notification=1'
].join('');
sinon.assert.calledWith(
window.open,
expectedUrl, '_blank', 'attention'
);
sinon.assert.notCalled(window.close);
});
test('only closes window if user dismisses the notification', function() {
var message = {
title: 'Some title',
body: 'Some body'
};
var handlerStub = window.navigator.mozSetMessageHandler;
handlerStub.withArgs('notification').yield(message);
sinon.assert.notCalled(window.open);
sinon.assert.called(window.close);
});
});

View file

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

View file

@ -0,0 +1,42 @@
/* global Utils */
'use strict';
require('/js/utils.js');
suite('Network Alerts - Utils', function() {
var parameters = [
'attention.html?',
'subject=hello%20world&',
'body=how%20are%20you&',
'novalue&',
'emptyvalue='
];
var expected = {
subject: 'hello world',
body: 'how are you',
novalue: undefined,
emptyvalue: ''
};
test('parseParams decodes search parameters', function() {
var subject = [ 'attention.html?' ].concat(parameters).join('');
var result = Utils.parseParams(subject);
for (var key in result) {
assert.strictEqual(result[key], expected[key]);
}
});
test('parseParams decodes hash parameters', function() {
var subject = [ 'attention.html#' ].concat(parameters).join('');
var result = Utils.parseParams(subject);
for (var key in result) {
assert.strictEqual(result[key], expected[key]);
}
});
});