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,78 @@
/* global MocksHelper, MockNavigatorMozMobileConnections, MockL10n,
MockNavigatormozApps, AppManager, MockNavigatormozApps */
'use strict';
require('/shared/js/event_safety.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('ftu/test/unit/mock_version_helper.js');
requireApp('ftu/test/unit/mock_tutorial.js');
requireApp('ftu/test/unit/mock_sim_manager.js');
requireApp('ftu/test/unit/mock_wifi_manager.js');
requireApp('ftu/test/unit/mock_import_services.js');
requireApp('ftu/test/unit/mock_time_manager.js');
requireApp('ftu/test/unit/mock_ui_manager.js');
requireApp('ftu/test/unit/mock_navigation.js');
requireApp('ftu/test/unit/mock_data_mobile.js');
requireApp('ftu/test/unit/mock_language_manager.js');
suite('AppManager >', function() {
var mocksHelperForAppManager = new MocksHelper([
'Tutorial',
'VersionHelper',
'SimManager',
'WifiManager',
'ImportIntegration',
'TimeManager',
'UIManager',
'Navigation',
'DataMobile',
'LanguageManager'
]).init();
var realL10n;
var realMozMobileConnections;
var realMozApps;
suiteSetup(function(done) {
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realL10n = document.l10n;
document.l10n = MockL10n;
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
mocksHelperForAppManager.suiteSetup();
// also call setup the first time
mocksHelperForAppManager.setup();
requireApp('ftu/js/app.js', done);
});
suiteTeardown(function() {
mocksHelperForAppManager.suiteTeardown();
navigator.mozMobileConnections = realMozMobileConnections;
realMozMobileConnections = null;
document.l10n = realL10n;
realL10n = null;
navigator.mozApps = realMozApps;
});
teardown(function() {
MockNavigatormozApps.mTeardown();
});
test('publishes setup event', function(done) {
window.addEventListener('ftu-setup', function() {
done();
});
AppManager.init();
});
});

View file

@ -0,0 +1,211 @@
'use strict';
/* global UIManager, DataMobile, MocksHelper, Navigation */
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp('ftu/test/unit/mock_ui_manager.js');
requireApp('ftu/js/navigation.js');
requireApp('ftu/js/data_mobile.js');
suite('mobile data >', function() {
var mocksHelperForDataMobile = new MocksHelper([
'UIManager'
]).init();
var realSettings,
settingToggleKey = 'ril.data.enabled',
settingApnKey = 'ril.data.apnSettings';
var TINY_TIMEOUT = 30;
suiteSetup(function() {
mocksHelperForDataMobile.suiteSetup();
realSettings = navigator.mozSettings;
navigator.mozSettings = window.MockNavigatorSettings;
DataMobile.init();
});
suiteTeardown(function() {
mocksHelperForDataMobile.suiteTeardown();
navigator.mozSettings = realSettings;
realSettings = null;
});
suite('Load APN values from database', function() {
var result;
setup(function(done) {
window.MockNavigatorSettings.mSettings[settingApnKey] = '[[]]';
DataMobile.getAPN(function(response) {
result = response;
done();
});
});
test('Values are loaded', function() {
assert.isNotNull(result);
});
test('Observer is added before', function() {
assert.isNotNull(window.MockNavigatorSettings.mObservers);
});
test('Observer is removed after', function() {
assert.isNotNull(window.MockNavigatorSettings.mRemovedObservers);
});
});
suite('Toggle status of mobile data', function() {
test('toggle status of mobile data', function(done) {
DataMobile.toggle(true, function() {
assert.isTrue(window.MockNavigatorSettings.mSettings[settingToggleKey]);
done();
});
});
test('toggle status of mobile data', function(done) {
DataMobile.toggle(false, function() {
assert.isFalse(
window.MockNavigatorSettings.mSettings[settingToggleKey]
);
done();
});
});
});
suite('Get data status', function() {
var KEY = 'ril.data.enabled';
var KEY_SV = 'ftu.ril.data.enabled';
var clock;
var realCurrentStep;
var DATA_SCREEN = 2;
var testCases = [
{
'title': 'On data screen. ' + KEY + '= true' + ' and ' + KEY_SV +
' and switch modify by user',
'currentStep': DATA_SCREEN,
'isModifiedByUser': true,
'key': true,
'keySV': true,
'expectedValue': true
},
{
'title': 'On data screen. ' + KEY + '= false' + ' and ' + KEY_SV +
' and switch modify by user',
'currentStep': DATA_SCREEN,
'isModifiedByUser': true,
'key': false,
'keySV': true,
'expectedValue': false
},
{
'title': 'On data screen. ' + KEY + '= true' + ' and not ' + KEY_SV,
'currentStep': DATA_SCREEN,
'key': true,
'expectedValue': true
},
{
'title': 'On data screen. ' + KEY + '= false' + ' and not ' + KEY_SV,
'currentStep': DATA_SCREEN,
'key': false,
'expectedValue': false
},
{
'title': 'On data screen. ' + KEY + '= true' + ' and ' + KEY_SV +
'= true',
'currentStep': DATA_SCREEN,
'key': true,
'keySV': true,
'expectedValue': true
},
{
'title': 'On data screen. ' + KEY + '= true' + ' and ' + KEY_SV +
'= false',
'currentStep': DATA_SCREEN,
'key': true,
'keySV': false,
'expectedValue': false
},
{
'title': 'On data screen. ' + KEY + '= false' + ' and ' + KEY_SV +
'= true',
'currentStep': DATA_SCREEN,
'key': false,
'keySV': true,
'expectedValue': true
},
{
'title': 'On data screen. ' + KEY + '= false' + ' and ' + KEY_SV +
'= false',
'currentStep': DATA_SCREEN,
'key': false,
'keySV': false,
'expectedValue': false
},
{
'title': 'Not on data screen. ' + KEY + '= true' + ' and ' + KEY_SV +
'= true',
'currentStep': DATA_SCREEN + 1,
'key': true,
'keySV': true,
'expectedValue': true
},
{
'title': 'Not on data screen. ' + KEY + '= true' + ' and ' + KEY_SV +
'= false',
'currentStep': DATA_SCREEN + 1,
'key': true,
'keySV': false,
'expectedValue': true
},
{
'title': 'Not on data screen. ' + KEY + '= false' + ' and ' + KEY_SV +
'= true',
'currentStep': DATA_SCREEN + 1,
'key': false,
'keySV': true,
'expectedValue': false
},
{
'title': 'Not on data screen. ' + KEY + '= false' + ' and ' + KEY_SV +
'= false',
'currentStep': DATA_SCREEN + 1,
'key': false,
'keySV': false,
'expectedValue': false
}
];
suiteSetup(function() {
realCurrentStep = Navigation.currentStep;
});
suiteTeardown(function() {
Navigation.currentStep = realCurrentStep;
});
setup(function() {
clock = this.sinon.useFakeTimers();
});
teardown(function() {
clock.restore();
});
testCases.forEach(function(testCase) {
test(testCase.title, function(done) {
Navigation.currentStep = testCase.currentStep;
UIManager.dataConnectionChangedByUsr = testCase.isModifiedByUser;
window.MockNavigatorSettings.mSettings[KEY] = testCase.key;
window.MockNavigatorSettings.mSettings[KEY_SV] = testCase.keySV;
DataMobile.getStatus(function() {});
clock.tick(TINY_TIMEOUT);
assert.equal(DataMobile.isDataAvailable, testCase.expectedValue);
done();
});
});
});
});

View file

@ -0,0 +1,42 @@
/* global FinishScreen, MocksHelper */
'use strict';
requireApp('ftu/test/unit/mock_screenlayout.js');
requireApp('ftu/js/finish_screen.js');
requireApp('ftu/js/utils.js');
suite('FinishScreen >', function() {
var mocksHelperForFTU = new MocksHelper([
'ScreenLayout'
]).init();
mocksHelperForFTU.attachTestHelpers();
suiteSetup(function() {
mocksHelperForFTU.attachTestHelpers();
loadBodyHTML('/index.html');
});
suiteTeardown(function() {
document.body.innerHTML = '';
});
test(' init', function() {
this.sinon.spy(window, 'close');
// We call to FinishScreen
FinishScreen.init();
// Is shown? We know by default the layout is tiny
assert.isTrue(
document.getElementById('tutorial-finish-tiny').classList.contains('show')
);
// As is tiny, a click should be enough to close the app
document.getElementById('tutorialFinished').click();
assert.isTrue(window.close.calledOnce);
// Reset the spy
window.close.reset();
});
});

View file

@ -0,0 +1,104 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
/* global MocksHelper */
/* global MockSdCard */
/* global Mockutils */
/* global SdManager */
require('/shared/test/unit/mocks/mock_sdcard.js');
require('/shared/test/unit/mocks/mock_vcard_parser.js');
requireApp('ftu/test/unit/mock_ui_manager.js');
requireApp('ftu/test/unit/mock_utils.js');
requireApp('ftu/js/memcard_manager.js');
if (!window.utils) { window.utils = null; }
var mocksHelperForSdCardImport = new MocksHelper([
'utils',
'UIManager',
'VCFReader'
]).init();
suite('Import contacts from SD card >', () => {
var mocksHelper = mocksHelperForSdCardImport;
var realUtils;
var showSpy;
var showMenuSpy;
suiteSetup(() => {
mocksHelper.suiteSetup();
realUtils = window.utils;
window.utils = Mockutils;
window.utils.sdcard = MockSdCard;
window.utils.misc = {
getTimestamp: function(element, cb) {
if (typeof cb === 'function') {
cb();
}
},
setTimestamp: function(time, cb) {
if (typeof cb === 'function') {
cb();
}
}
};
window.utils.status = {
show: function() {}
};
});
suiteTeardown(() => {
mocksHelper.suiteTeardown();
});
setup(() => {
mocksHelper.setup();
showSpy = this.sinon.spy(window.utils.status, 'show');
showMenuSpy = this.sinon.spy(window.utils.overlay, 'showMenu');
});
teardown(() => {
mocksHelper.teardown();
showSpy.restore();
showMenuSpy.restore();
});
test('Integrity', () => {
assert.ok(SdManager);
});
test('SD import success', done => {
SdManager.importContacts().then(() => {
assert.ok(showMenuSpy.calledOnce);
assert.ok(showSpy.calledOnce);
assert.ok(showSpy.calledWith({
id: 'memoryCardContacts-imported3',
args: {
n: 1
}
}));
done();
}).catch(error => {
assert.ok(false, 'Unexpected error ' + error);
});
});
test('SD import failed - no files to import', done => {
MockSdCard.failOnRetrieveFiles = true;
SdManager.importContacts().then(() => {
assert.ok(false, 'Unexpected success');
}).catch(() => {
assert.ok(showMenuSpy.calledOnce);
assert.ok(showSpy.notCalled);
done();
});
});
});

View file

@ -0,0 +1,135 @@
/* global MockL10n, MockNavigatorSettings, MockLanguageList,
LanguageManager, LanguageList, KeyboardHelper,
MockImportNavigationHTML */
'use strict';
require('/shared/js/component_utils.js');
require('/shared/elements/gaia_radio/script.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_language_list.js');
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('ftu/test/unit/mock_navigation.html.js');
requireApp('ftu/js/language.js');
suite('languages >', function() {
var realSettings;
var realLanguageList;
var realL10n;
var realHTML;
suiteSetup(function() {
realHTML = document.body.innerHTML;
document.body.innerHTML = MockImportNavigationHTML;
// mock l10n
realL10n = document.l10n;
document.l10n = MockL10n;
realSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
realLanguageList = window.LanguageList;
window.LanguageList = MockLanguageList;
LanguageManager.settings = MockNavigatorSettings;
LanguageManager._kbLayoutList = null;
});
suiteTeardown(function() {
document.body.innerHTML = realHTML;
document.l10n = realL10n;
navigator.mozSettings = realSettings;
realSettings = null;
window.LanguageList = realLanguageList;
});
test('change language', function() {
var settingName = 'language.current';
var fakeEvent = {
target: {
name: settingName,
value: 'a different language'
}
};
LanguageManager.handleEvent(fakeEvent);
assert.equal(MockNavigatorSettings.mSettings[fakeEvent.target.name],
fakeEvent.target.value);
});
test('build language list', function(done) {
var section = document.createElement('section');
section.id = 'languages';
document.body.appendChild(section);
var list = document.createElement('ul');
section.appendChild(list);
// XXX in reality this method is async b/c it uses LanguageList.get; here
// however it uses the mock which is sync. Fix this in bug 1119865.
LanguageManager.buildLanguageList();
var selected = document.querySelectorAll('gaia-radio');
assert.equal(selected.length, 1);
assert.equal(selected[0].checked, true);
assert.equal(selected[0].value, 'en-US');
// mock's _languages is sync, too
LanguageList._languages.then(function(langs) {
assert.equal(document.querySelectorAll('li').length,
Object.keys(langs).length);
});
done();
});
suite('keyboard settings >', function() {
var langKey = 'language.current';
suiteSetup(function() {
window.KeyboardHelper = {};
MockNavigatorSettings.mSyncRepliesOnly = true;
});
suiteTeardown(function() {
delete window.KeyboardHelper;
});
setup(function() {
KeyboardHelper.changeDefaultLayouts = this.sinon.spy();
LanguageManager.init();
});
teardown(function() {
LanguageManager.uninit();
});
test('observes settings', function() {
assert.equal(MockNavigatorSettings.mObservers[langKey].length, 1);
});
test('keyboard layouts changed after language change', function() {
MockNavigatorSettings.mTriggerObservers(langKey,
{settingValue: 'newLanguage'});
assert.isTrue(KeyboardHelper.changeDefaultLayouts.called);
});
});
suite('screen reader', function() {
suiteSetup(function() {
LanguageManager.init();
});
suiteTeardown(function() {
LanguageManager.uninit();
});
test('enable screen reader', function(done) {
function afterEnabledHandler() {
window.removeEventListener('languagelistready', afterEnabledHandler);
window.addEventListener('languagelistready', afterDisabledHandler);
MockNavigatorSettings.mTriggerObservers('accessibility.screenreader',
{ settingValue: false });
}
function afterDisabledHandler() {
window.removeEventListener('languagelistready', afterDisabledHandler);
done();
}
window.addEventListener('languagelistready', afterEnabledHandler);
MockNavigatorSettings.mTriggerObservers('accessibility.screenreader',
{ settingValue: true });
});
});
});

View file

@ -0,0 +1,405 @@
/* global LateCustomizationPanel, MockLazyLoader, MockNavigatorSettings,
MockL10n, MockPromise, MockDOMRequest */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/mocks/mock_promise.js');
require('/shared/test/unit/mocks/mock_event_target.js');
require('/shared/test/unit/mocks/mock_dom_request.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/js/sanitizer.js');
suite('Late Customization >', function() {
var realSettings;
var realL10n;
var lcPanel;
var MockNavigation = {
registerStep: function() {},
skipStep: function() {}
};
var realLazyLoader, realPromise;
var mockResponse = {
objects: [
{
device_types: ['firefoxos'],
icons: {},
name: {
'en-US': 'Foo',
'sv-Chef': 'Bork'
},
manifest_url: 'http://host/manifest.webapp'
},
{
device_types: ['android-mobile', 'firefoxos'],
icons: {},
name: {
'en-yorks': 'Now then',
'en-US': 'Hey',
},
manifest_url: 'http://host2/manifest.webapp'
},
{
device_types: ['desktop'],
icons: {},
name: {
'en-US': 'Foo',
},
manifest_url: 'http://host3/manifest.webapp'
},
]
};
suiteSetup(function(done) {
realSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
realL10n = document.l10n;
document.l10n = MockL10n;
window.Navigation = MockNavigation;
realLazyLoader = window.LazyLoader;
window.LazyLoader = MockLazyLoader;
realPromise = window.Promise;
document.body.innerHTML = `
<section id="activation-screen" role="region"
class="skin-organic no-options">
<gaia-header>
<h1 id="main-title"></h1>
<button id="wifi-refresh-button" data-l10n-id="refresh">Refresh</button>
</gaia-header>
<article role="main"></article>
</section>`;
requireApp('ftu/js/late_customization.js', done);
});
suiteTeardown(function() {
navigator.mozSettings = realSettings;
realSettings = null;
});
function switchReadOnlyProperty(originObject, propName, targetObj) {
Object.defineProperty(originObject, propName, {
configurable: true,
get: function() { return targetObj; }
});
}
setup(function() {
lcPanel = new LateCustomizationPanel();
});
suite('init', function() {
setup(function() {
switchReadOnlyProperty(lcPanel, 'enabled', true);
this.sinon.stub(window.Navigation, 'registerStep');
});
test('registerStep is called', function() {
this.sinon.stub(lcPanel, 'render');
this.sinon.stub(lcPanel, 'attemptToPopulateAppList')
.returns(new MockPromise());
lcPanel.init();
assert.ok(window.Navigation.registerStep.calledOnce);
});
test('render renders', function() {
this.sinon.stub(lcPanel, 'attemptToPopulateAppList');
lcPanel.init();
var ids = [
'late_customization',
'late-customization-cando',
'late_customization-downloading-message',
'late-customization-nocando',
'late_customization-download-later-message',
'late_customization-download-later-marketplace',
'late_customization-applist'
];
var byId = document.getElementById.bind(document);
assert.ok(ids.every(id => byId(id)));
assert.equal(byId('late_customization-applist').childElementCount, 0);
assert.ok(byId('late-customization-cando').classList.contains('hidden'));
});
test('kicks off process to fetch apps', function() {
var connectedPromise = new MockPromise();
this.sinon.stub(lcPanel, 'render');
this.sinon.stub(lcPanel, 'whenUsableConnection')
.returns(connectedPromise);
lcPanel.init();
assert.ok(lcPanel.whenUsableConnection.calledOnce);
});
});
test('operatorInfo', function() {
var settingsPromise = new MockPromise();
this.sinon.stub(lcPanel, 'waitForSettingValues').returns(settingsPromise);
this.sinon.stub(lcPanel, 'getInfoFromMobileConnection').returns({
operator: 'acme',
mcc: '01', mnc: '02'
});
lcPanel.start();
settingsPromise.mFulfillToValue(['url', { region: 'foo'}]);
console.log('lcPanel.operatorInfo', lcPanel.operatorInfo);
});
suite('navigation', function() {
setup(function() {
this.sinon.stub(window.Navigation, 'registerStep');
this.sinon.stub(window.Navigation, 'skipStep');
this.sinon.stub(lcPanel, 'onPanelShown');
this.sinon.stub(lcPanel, 'render');
this.sinon.stub(lcPanel, 'attemptToPopulateAppList');
lcPanel.init();
switchReadOnlyProperty(lcPanel, 'enabled', true);
});
test('panel is shown when there are apps', function() {
var appsMap = new Map([
['http://host/manifest.webapp', mockResponse.objects[0]]
]);
lcPanel._appsToInstall = appsMap;
lcPanel.handleEvent({ type: 'hashchange', target: {
location: {
hash: '#late_customization'
}
}});
assert.ok(lcPanel.onPanelShown.calledOnce);
assert.isFalse(window.Navigation.skipStep.called);
});
test('panel is not shown when there are no apps', function() {
var appsMap = new Map([]);
lcPanel._appsToInstall = appsMap;
lcPanel.handleEvent({ type: 'hashchange', target: {
location: {
hash: '#late_customization'
}
}});
assert.ok(!lcPanel.onPanelShown.called);
assert.ok(window.Navigation.skipStep.calledOnce);
});
});
test('buildManifestUrl', function() {
var manifestURL = 'http://host.com/api/?carrier=acme&region=foo';
var operatorInfo = {
operator: 'meh', mcc: '01', mnc: '02', region: 'bar'
};
var url = lcPanel.buildManifestUrl(manifestURL, operatorInfo);
assert.equal(url,
'http://host.com/api/?carrier=acme&region=bar&' +
'operator=meh&mcc=01&mnc=02');
});
suite('fetch manifest', function() {
var getJSONPromise;
var manifestURL = 'http://host.com/api/?carrier=Acme&region=foo';
var result;
setup(function() {
window.Promise = MockPromise;
getJSONPromise = new MockPromise();
this.sinon.stub(lcPanel, 'buildManifestUrl').returns(manifestURL);
this.sinon.stub(window.LazyLoader, 'getJSON').returns(getJSONPromise);
});
teardown(function() {
window.Promise = realPromise;
result = null;
});
test('valid response', function() {
lcPanel.fetchManifest().mExecuteCallback(function(_result) {
return (result = _result);
});
getJSONPromise.mFulfillToValue({ objects: [] });
assert.ok(result && result.objects);
});
test('Server error in response', function() {
var expectedError;
lcPanel.fetchManifest().mExecuteCallback(function(_result) {
return (result = _result);
}, function(_err) {
expectedError = _err;
});
getJSONPromise.mFulfillToValue({ error: {} });
assert.ok(!result);
assert.ok(expectedError);
});
test('Request failed', function() {
var expectedError;
lcPanel.fetchManifest().mExecuteCallback(function(_result) {
return (result = _result);
}, function(_err) {
expectedError = _err;
});
getJSONPromise.mRejectToError({});
assert.ok(!result);
assert.ok(expectedError);
});
});
suite('getAppsFromManifest', function() {
var realLanguages = navigator.languages;
setup(function() {
switchReadOnlyProperty(navigator, 'languages', [ 'sv-Chef', 'en-US' ]);
});
teardown(function() {
switchReadOnlyProperty(navigator, 'languages', realLanguages);
});
test('getAppsFromManifest ignores non-firefoxos app', function() {
var result = lcPanel.getAppsFromManifest(mockResponse);
assert.equal(result.size, 2);
assert.isFalse(result.has('http://host3/manifest.webapp'));
});
test('displayName', function() {
var result = lcPanel.getAppsFromManifest(mockResponse);
var app1 = result.get('http://host/manifest.webapp');
assert.ok(app1 && app1.displayName);
assert.equal(app1.displayName, 'Bork');
var app2 = result.get('http://host2/manifest.webapp');
assert.equal(app2.displayName, 'Hey');
});
});
suite('Populate and send', function() {
setup(function() {
window.Promise = MockPromise;
});
teardown(function() {
window.Promise = realPromise;
});
test('No message when there are no apps', function() {
var connected = new MockPromise();
var appsFetched = new MockPromise();
this.sinon.stub(lcPanel, 'whenUsableConnection').returns(connected);
this.sinon.stub(lcPanel, 'fetchAppsToInstall').returns(appsFetched);
this.sinon.stub(lcPanel, 'updateAppList');
this.sinon.stub(lcPanel, 'postConnectMessage');
lcPanel.attemptToPopulateAppList();
connected.mFulfillToValue(true);
var appsMap = new Map([]);
lcPanel._appsToInstall = appsMap;
appsFetched.mFulfillToValue(appsMap);
assert.ok(!lcPanel.updateAppList.calledOnce);
assert.ok(!lcPanel.postConnectMessage.called);
});
test('Posts message when there are apps', function() {
var connected = new MockPromise();
var appsFetched = new MockPromise();
this.sinon.stub(lcPanel, 'whenUsableConnection').returns(connected);
this.sinon.stub(lcPanel, 'fetchAppsToInstall').returns(appsFetched);
this.sinon.stub(lcPanel, 'updateAppList');
this.sinon.stub(lcPanel, 'postConnectMessage');
lcPanel.attemptToPopulateAppList();
connected.mFulfillToValue(true);
var appsMap = new Map([
['http://host/manifest.webapp', mockResponse.objects[0]]
]);
lcPanel._appsToInstall = appsMap;
appsFetched.mFulfillToValue(appsMap);
assert.ok(lcPanel.updateAppList.calledOnce);
assert.ok(lcPanel.postConnectMessage.calledOnce);
var message = lcPanel.postConnectMessage.firstCall.args[0];
assert.equal(message.urls.length, appsMap.size);
});
});
suite('postMessage', function() {
var mockSelfRequest, mockConnectRequest;
var fakeMessage, mockSelf, mockPort;
setup(function() {
mockSelfRequest = new MockDOMRequest();
mockConnectRequest = new MockDOMRequest();
fakeMessage = 'some message';
mockSelf = {
connect: sinon.stub().returns(mockConnectRequest)
};
this.sinon.stub(navigator.mozApps, 'getSelf').returns(mockSelfRequest);
mockPort = {
postMessage: this.sinon.stub()
};
});
test('postConnectMessage', function() {
lcPanel.postConnectMessage(fakeMessage);
mockSelfRequest.fireSuccess(mockSelf);
mockConnectRequest.then(function() {
assert.ok(mockPort.postMessage.calledOnce);
assert.equal(mockPort.postMessage.firstCall.args[0], fakeMessage);
}).catch(err => {
assert.ok(!err);
});
mockConnectRequest.fireSuccess([ mockPort ]);
});
});
suite('install queue', function() {
var appsToInstall;
function FauxPromise() {
this.then = this.catch = function() {};
}
setup(function() {
appsToInstall = new Map();
mockResponse.objects.forEach(appData => {
appData.manifestURL = appData.manifest_url;
appsToInstall.set(appData.manifest_url, appData);
});
switchReadOnlyProperty(lcPanel, 'enabled', true);
this.sinon.stub(window.Navigation, 'registerStep');
this.sinon.stub(lcPanel, 'attemptToPopulateAppList');
this.sinon.stub(lcPanel, 'getAppIconUrl').returns(new FauxPromise());
this.sinon.stub(lcPanel, 'finalizeRender');
lcPanel.init();
lcPanel.updateAppList(appsToInstall);
});
test('installApp', function(done) {
var initiallyPendingNodes = lcPanel.element.querySelectorAll('.pending');
assert.equal(initiallyPendingNodes.length, appsToInstall.size);
var fakeAppData = appsToInstall.get('http://host/manifest.webapp');
var promisedConnection = Promise.resolve();
fakeAppData.manifestURL = fakeAppData.manifest_url;
var mockInstallRequest = new MockDOMRequest();
this.sinon.stub(lcPanel, 'whenUsableConnection')
.returns(promisedConnection);
this.sinon.stub(window.navigator.mozApps, 'install')
.returns(mockInstallRequest);
lcPanel.installApp(fakeAppData);
promisedConnection.then(() => {
mockInstallRequest.fireSuccess();
var nowPendingNodes = lcPanel.element.querySelectorAll('.pending');
assert.equal(nowPendingNodes.length, appsToInstall.size -1);
}).then(done, (err) => { done(err); });
});
test('attemptInstalls', function(done) {
this.sinon.stub(lcPanel, 'installApp').returns(Promise.resolve());
this.sinon.spy(lcPanel, 'enqueAppInstall');
lcPanel._appsToInstall = appsToInstall;
return lcPanel.attemptInstalls().then(() => {
assert.equal(lcPanel.enqueAppInstall.callCount, appsToInstall.size);
assert.equal(lcPanel.installApp.callCount, appsToInstall.size);
}).then(() => {
done();
}, (err) => { done(err); });
});
});
});

View file

@ -0,0 +1,11 @@
'use strict';
/* exported MockDataMobile */
var MockDataMobile = {
init: function() {},
setStatus: function(s) { this.status = s;},
getStatus: function() {
return status;
},
removeSVStatusObserver: function() {}
};

View file

@ -0,0 +1,10 @@
/*exported MockFinishScreen*/
'use strict';
var MockFinishScreen = {
init: function(cb) {
if (typeof cb === 'function') {
cb();
}
}
};

View file

@ -0,0 +1,40 @@
/* exported MockFxAccountsIACHelper */
'use strict';
var MockFxAccountsIACHelper = {
getAccountNoCallback: false,
logoutNoCallback: false,
getAccountError: null,
account: null,
reset: function() {
this.account = null;
this.getAccountError = null;
this.getAccountNoCallback = null;
this.logoutNoCallback = null;
},
// FxAccountIACHelper API
openFlow: function() {},
getAccount: function(successCb, errorCb) {
if (this.getAccountNoCallback) {
return;
}
if (this.getAccountError) {
errorCb(this.getAccountError);
return;
}
successCb(this.account);
},
logout: function(successCb) {
if (!this.logoutNoCallback) {
successCb();
}
}
};

View file

@ -0,0 +1,7 @@
'use strict';
/* exported MockImportIntegration */
var MockImportIntegration = {
checkImport: function() {},
init: function() {}
};

View file

@ -0,0 +1,6 @@
/* exported MockLanguageManager */
'use strict';
var MockLanguageManager = {
init: function() {},
};

View file

@ -0,0 +1,9 @@
/* exported MockMozWifiNetwork */
'use strict';
function MockMozWifiNetwork(network) {
for (var elem in network) {
this[elem] = network[elem];
}
}

View file

@ -0,0 +1,17 @@
/* exported MockMozApps*/
'use strict';
var MockMozApps = {
onsuccess: function() {
},
result: {
connect: function() {
return this;
},
then: function() {
}
},
getSelf: function() {
return this.onsuccess;
}
};

View file

@ -0,0 +1,10 @@
'use strict';
/* exported MockImportNavigationHTML */
var MockImportNavigationHTML = (function MockImportHTML() {
var req = new XMLHttpRequest();
req.open('GET', 'mock_navigation_index.html', false);
req.send(null);
return req.responseText;
})();

View file

@ -0,0 +1,7 @@
/* exported MockNavigation */
'use strict';
var MockNavigation = {
init: function() {},
};

View file

@ -0,0 +1,63 @@
<gaia-header>
<h1 id="main-title"></h1>
<button id="wifi-refresh-button" data-l10n-id="refresh">Refresh</button>
</gaia-header>
<section id="unlock-sim-screen" role="region" class="skin-organic">
<gaia-buttons>
<button id="skip-pin-button" class="button-left" data-l10n-id="skip">
Skip
</button>
<button id="back-sim-button" class="button-left back hidden" data-l10n-id="back">
Back
</button>
<button id="unlock-sim-button" class="recommend" data-l10n-id="send">
Send
</button>
</gaia-buttons>
</section>
<section id="activation-screen" role="region" class="skin-organic no-options">
<article>
<section id="languages">
<ul>
<!-- filled by js/language.js -->
</ul>
</section>
<section id="wifi">
<div id="wifi-wrapper">
<article id="networks">
</article>
<button id="join-hidden-button">
Join hidden network
</button>
</div>
</section>
</article>
<gaia-buttons id="nav-bar" class="forward-only">
<button id="back" class="button-left back">
Back
<span></span>
</button>
<button class="recommend forward" id="forward">
Next
<span></span>
</button>
<button class="recommend" id="wifi-join-button">
Join
</button>
</gaia-buttons>
<a href="https://www.mozilla.org/privacy/firefox-os/"
class="external" title="URL title">url text</a>
<section role="region" id="sharing-performance-data">
</section>
<section role="region" id="browser_privacy">
</section>
<section role="region" id="welcome_browser">
<gaia-checkbox id="share-performance"></gaia-checkbox>
</section>
</section>
<section id="finish-screen" role="region">
</section>
<section id="tutorial-screen" role="region">
</section>
<section id="external-url-loader" class="external">
</section>

View file

@ -0,0 +1,18 @@
'use strict';
(function(window) {
var contacts = [];
window.MockNavigatorContacts = {
save: function(contact) {
contacts.push(contact);
return {};
},
get contacts() {
return contacts;
},
resetContacts: function() {
contacts = [];
}
};
})(window);

View file

@ -0,0 +1,22 @@
'use strict';
/* exported MockNavigatorMozWifiManager */
var MockNavigatorMozWifiManager = {
setNetworks: function(networks) {
this.networks = networks;
},
getNetworks: function() {
var self = this;
return {
result: self.networks,
set onsuccess(callback) {
this.result = self.networks;
callback(this);
}
};
},
connection: {
network: null
}
};

View file

@ -0,0 +1,7 @@
/* exported MockOperatorVariant */
'use strict';
var MockOperatorVariant = {
setSIMOnFirstBootState: function() {}
};

View file

@ -0,0 +1,10 @@
'use strict';
var MockScreenLayout = {};
MockScreenLayout._currentDevice = 'tiny';
MockScreenLayout.setDevice = function(deviceType) {
this._currentDevice = deviceType.toLowerCase() || 'tiny';
};
MockScreenLayout.getCurrentLayout = function() {
return this._currentDevice;
};

View file

@ -0,0 +1,8 @@
'use strict';
/* exported MockSdManager */
var MockSdManager = {
available: function() { return true; },
checkSDButton: function() {},
importContacts: function() {}
};

View file

@ -0,0 +1,10 @@
'use strict';
/* exported MockImportSimManagerHTML */
var MockImportSimManagerHTML = (function MockImportHTML() {
var req = new XMLHttpRequest();
req.open('GET', 'test/unit/mock_sim_manager_index.html', false);
req.send(null);
return req.responseText;
})();

View file

@ -0,0 +1,10 @@
'use strict';
/* exported MockSimManager */
var MockSimManager = {
handleCardState: function() {},
checkSIMButton: function() {},
available: function() {},
init: function() {}
};

View file

@ -0,0 +1,7 @@
/* exported MockTimeManager */
'use strict';
var MockTimeManager = {
init: function () {},
set: function (date) {}
};

View file

@ -0,0 +1,18 @@
/* exported MockTutorial */
'use strict';
var MockTutorial = {
currentStep: 0,
imagesLoaded: [],
init: function() {},
jumpTo: function() {},
jumpToExitStep: function() {},
manageStep: function() {},
loadConfig: function() {
return {
then: function(cb) {
cb();
}
};
}
};

View file

@ -0,0 +1,19 @@
/* exported MockTzSelect */
'use strict';
var MockTzSelect = function tzSelect(regionSelector,
citySelector,
onchange,
onload) {
var timezone = {
id: 'Fake ID',
region: 'Fake Region',
city: 'Fake City',
cc: 'Fake CC',
utcOffset: '+0:00',
dstOffset: '+0:00'
};
onchange(timezone);
onload(timezone);
};

View file

@ -0,0 +1,141 @@
'use strict';
/* global DataMobile */
/* exported MockUIManager */
var MockUIManager = {
domSelectors: [
'container',
'splash-screen',
'activation-screen',
'finish-screen',
'update-screen',
'nav-bar',
'main-title',
// Unlock SIM Screen
'unlock-sim-screen',
'unlock-sim-header',
// PIN Screen
'pincode-screen',
'pin-label',
'pin-retries-left',
'pin-input',
'back-sim-button',
'pin-error',
'skip-pin-button',
'unlock-sim-button',
// PUK Screen
'pukcode-screen',
'puk-label',
'puk-retries-left',
'puk-input',
'puk-info',
'puk-error',
'newpin-input',
'newpin-error',
'confirm-newpin-input',
'confirm-newpin-error',
// XCK Screen
'xckcode-screen',
'xck-label',
'xck-retries-left',
'xck-input',
'xck-error',
// SIM info
'sim-info-screen',
'sim-info-back',
'sim-info-forward',
'sim-info-1',
'sim-info-2',
'sim-number-1',
'sim-number-2',
'sim-carrier-1',
'sim-carrier-2',
// Import contacts
'sim-import',
'sim-import-button',
'no-sim',
'sd-import-button',
'no-memorycard',
// Fxa Intro
'fxa-create-account',
'fxa-intro',
// Wifi
'networks',
'wifi-refresh-button',
'wifi-join-button',
'join-hidden-button',
// Hidden Wifi
'hidden-wifi-authentication',
'hidden-wifi-ssid',
'hidden-wifi-security',
'hidden-wifi-password',
'hidden-wifi-identity',
'hidden-wifi-identity-box',
'hidden-wifi-show-password',
//Date & Time
'date-configuration',
'time-configuration',
'date-configuration-label',
'time-configuration-label',
'time-form',
// 3G
'data-connection-switch',
// Geolocation
'geolocation-switch',
// Tutorial
'lets-go-button',
'update-lets-go-button',
'skip-tutorial-button',
'update-skip-tutorial-button',
// Privacy Settings
'share-performance',
'offline-error-dialog',
'metrics-basic',
'metrics-enhanced',
'metrics-none',
// Browser privacy newsletter subscription
'newsletter-form',
'newsletter-input',
'newsletter-success-screen',
'offline-newsletter-error-dialog',
'invalid-email-error-dialog'
],
DARK_THEME: '#242d33',
LIGHT_THEME: '#eeeeee',
mSetup: function muim_mSuiteSetup() {
this.domSelectors.forEach(function createElementRef(name) {
if (name) {
var element = document.getElementById(name);
if (!element) {
element = document.createElement('div');
element.id = name;
}
this[toCamelCase(name)] = element;
}
}.bind(this));
},
mTeardown: function muim_mSuiteTeardown() {
this.domSelectors.forEach(function destroyElementRef(name) {
if (name) {
this[toCamelCase(name)] = null;
}
}.bind(this));
},
timeZoneNeedsConfirmation: true,
sendNewsletter: function(callback) {return callback(true);},
updateDataConnectionStatus: function(status) {return DataMobile.getStatus();},
displayOfflineDialog: function() {},
hideActivationScreenFromScreenReader: function() {},
init: function() {},
initTZ: function() {},
changeStatusBarColor: function(color) {}
};
function toCamelCase(str) {
return str.replace(/\-(.)/g, function replacer(str, p1) {
return p1.toUpperCase();
});
}

View file

@ -0,0 +1,15 @@
'use strict';
/* exported Mockutils */
var Mockutils = {
overlay: {
show: function() {
return {
update: function() {}
};
},
showMenu: function() {},
hide: function() {}
}
};

View file

@ -0,0 +1,19 @@
'use strict';
var MockVersionHelper = {
mIsUpgrade: false,
mVersionInfo: {
isUpgrade: function() {
return MockVersionHelper.mIsUpgrade;
}
},
getVersionInfo: function() {
return {
then: function(cb) {
cb(MockVersionHelper.mVersionInfo);
}
};
}
};

View file

@ -0,0 +1,27 @@
'use strict';
/* global MockNavigatorMozWifiManager */
/* exported MockWifiHelper */
require('/shared/test/unit/mock_navigator_moz_wifi_manager.js');
var MockWifiHelper = {
setPassword: function(pswd) {},
getWifiManager: function() {
return MockNavigatorMozWifiManager;
},
getSecurity: function(network) {
return network.security;
},
isConnected: function(network) {
return false;
},
isOpen: function(network) {
return false;
},
isEap: function(network) {
return false;
},
getNetworkStatus: function(network) {
return 'disconnected';
}
};

View file

@ -0,0 +1,15 @@
'use strict';
/* exported MockWifiManager, MockWifiUI */
var MockWifiManager = {
scan: function() {},
getNetworks: function() {},
init: function() {}
};
var MockWifiUI = {
renderNetworks: function() {},
addHiddenNetwork: function() {},
joinNetwork: function() {},
joinHiddenNetwork: function() {}
};

View file

@ -0,0 +1,618 @@
'use strict';
/* global
MockIccHelper,
MockL10n,
MockNavigatormozApps,
MockNavigatorMozMobileConnections,
MockNavigatorSettings,
MocksHelper,
Navigation,
SimManager,
UIManager
*/
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_icc_helper.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('ftu/js/navigation.js');
requireApp('ftu/test/unit/mock_wifi_manager.js');
requireApp('ftu/test/unit/mock_data_mobile.js');
requireApp('ftu/test/unit/mock_sim_manager.js');
requireApp('ftu/test/unit/mock_sd_manager.js');
requireApp('ftu/test/unit/mock_import_services.js');
requireApp('ftu/test/unit/mock_ui_manager.js');
requireApp('ftu/test/unit/mock_tutorial.js');
requireApp('ftu/test/unit/mock_wifi_manager.js');
requireApp('ftu/test/unit/mock_utils.js');
requireApp('ftu/test/unit/mock_operatorVariant.js');
requireApp('ftu/test/unit/mock_navigation.html.js');
var mocksHelperForNavigation = new MocksHelper([
'utils',
'UIManager',
'SimManager',
'DataMobile',
'OperatorVariant',
'IccHelper',
'Tutorial',
'SdManager',
'ImportIntegration',
'WifiManager',
'WifiUI'
]).init();
suite('navigation >', function() {
var isOnLine = true;
var realOnLine,
realL10n,
realMozMobileConnections,
realMozApps,
realSettings;
function navigatorOnLine() {
return isOnLine;
}
function setNavigatorOnLine(value) {
isOnLine = value;
}
suiteSetup(function() {
loadBodyHTML('/index.html');
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
realL10n = document.l10n;
document.l10n = MockL10n;
realOnLine = Object.getOwnPropertyDescriptor(navigator, 'onLine');
Object.defineProperty(navigator, 'onLine', {
configurable: true,
get: navigatorOnLine,
set: setNavigatorOnLine
});
MockIccHelper.setProperty('cardState', 'ready');
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
});
suiteTeardown(function() {
navigator.mozMobileConnections = realMozMobileConnections;
realMozMobileConnections = null;
navigator.mozSettings = realSettings;
realSettings = null;
document.l10n = realL10n;
realL10n = null;
if (realOnLine) {
Object.defineProperty(navigator, 'onLine', realOnLine);
} else {
delete navigator.onLine;
}
document.body.innerHTML = '';
});
function setStepStateToIndex(current, callback) {
Navigation.currentStepIndex = current;
Navigation.previousStepIndex = current > 0 ? current - 1 : 0;
Navigation.manageStep(callback);
}
// The mocks need the HTML markup
mocksHelperForNavigation.attachTestHelpers();
// Navigation.init needs the mocks
setup(function() {
Navigation.init();
this.sinon.stub(Navigation, 'postStepMessage');
});
teardown(function() {
Navigation.simMandatory = false;
Navigation.uninit();
});
test('indexOfStep', function() {
assert.equal(Navigation.indexOfStep('languages'), 0);
assert.equal(Navigation.indexOfStep('browser_privacy'), 8);
assert.equal(Navigation.indexOfStep('no_such_step'), -1);
});
test('stepAt', function() {
assert.equal(Navigation.stepAt(0).id, 'languages');
assert.equal(Navigation.stepAt(8).id, 'browser_privacy');
assert.isUndefined(Navigation.stepAt(9));
});
suite('registerStep', function() {
test(' > default position', function() {
var newStep = { hash: '#new', };
var oldStepCount = Navigation.stepCount;
Navigation.registerStep(newStep);
assert.equal(Navigation.stepCount, oldStepCount + 1);
var lastIndex = Navigation.stepCount - 1;
assert.ok(Navigation.stepAt(lastIndex));
assert.equal(Navigation.stepAt(lastIndex).hash, newStep.hash);
});
test(' > beforeStep', function() {
var newStep = { hash: '#new', beforeStep: 'browser_privacy' };
var refIndex = Navigation.indexOfStep('browser_privacy');
var oldStepCount = Navigation.stepCount;
Navigation.registerStep(newStep);
assert.equal(Navigation.stepCount, oldStepCount + 1);
assert.equal(Navigation.stepAt(refIndex).hash, newStep.hash);
setStepStateToIndex(refIndex);
assert.equal(window.location.hash, newStep.hash);
Navigation.forward();
assert.equal(window.location.hash, '#browser_privacy');
});
test(' > afterStep', function() {
var newStep = { hash: '#new', afterStep: 'languages' };
var refIndex = Navigation.indexOfStep('languages');
var oldStepCount = Navigation.stepCount;
Navigation.registerStep(newStep);
assert.equal(Navigation.stepCount, oldStepCount + 1);
assert.equal(Navigation.stepAt(refIndex +1).hash, newStep.hash);
setStepStateToIndex(refIndex + 1);
assert.equal(window.location.hash, newStep.hash);
Navigation.back();
assert.equal(window.location.hash, '#languages');
Navigation.forward();
assert.equal(window.location.hash, newStep.hash);
});
});
test('navigates forward', function() {
MockIccHelper.setProperty('cardState', 'ready');
Navigation.simMandatory = true;
setStepStateToIndex(0);
var lastIndex = Navigation.stepCount - 1;
for (var i = Navigation.currentStepIndex; i < lastIndex; i++) {
Navigation.forward();
assert.equal(Navigation.previousStepIndex, i);
assert.equal(Navigation.currentStepIndex, (i + 1));
assert.equal(window.location.hash,
Navigation.stepAt(i + 1).hash);
assert.isTrue(Navigation.postStepMessage.calledWith(i));
}
});
test('navigates backwards', function() {
MockIccHelper.setProperty('cardState', 'ready');
Navigation.simMandatory = true;
setStepStateToIndex(Navigation.stepCount - 1);
// The second step isn't mandatory.
for (var i = Navigation.currentStepIndex; i > 2; i--) {
Navigation.back();
assert.equal(Navigation.previousStepIndex, i);
assert.equal(Navigation.currentStepIndex, i - 1);
assert.equal(window.location.hash,
Navigation.stepAt(i - 1).hash);
}
});
test('navigates backwards and skips data_3g', function() {
setStepStateToIndex(2);
this.sinon.stub(SimManager, 'handleCardState', function(cb) {
cb(null);
});
Navigation.back();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 0);
});
test('skips date and time when network time is available', function() {
var oldTimeZoneNeedsConfirmation = UIManager.timeZoneNeedsConfirmation;
UIManager.timeZoneNeedsConfirmation = false;
UIManager.updateSetting = sinon.stub();
MockIccHelper.setProperty('cardState', 'ready');
Navigation.simMandatory = true;
setStepStateToIndex(2);
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 2);
assert.equal(Navigation.currentStepIndex, 4);
assert.equal(window.location.hash, Navigation.stepAt(4).hash);
// Make sure we posted both steps.
assert.isTrue(Navigation.postStepMessage.callCount > 1);
UIManager.timeZoneNeedsConfirmation = oldTimeZoneNeedsConfirmation;
});
test('last step launches tutorial', function() {
var lastIndex = Navigation.stepCount - 1;
Navigation.currentStepIndex = lastIndex;
setStepStateToIndex(lastIndex);
Navigation.forward();
assert.isTrue(UIManager.finishScreen.classList.contains('show'));
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
});
suite('UI changes>', function() {
var observerConfig = {
attributes: true
};
setup(function() {
MockIccHelper.setProperty('cardState', 'ready');
Navigation.simMandatory = false;
});
teardown(function() {
Navigation.simMandatory = false;
});
test('languages screen >', function(done) {
setStepStateToIndex(0);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'language');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('data mobile screen >', function(done) {
setStepStateToIndex(1);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'), '3g');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('wifi screen >', function(done) {
setStepStateToIndex(2);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'selectNetwork');
assert.isFalse(UIManager.navBar.classList.contains('secondary-menu'));
assert.isFalse(UIManager.activationScreen.classList.contains(
'no-options'));
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('date&time screen >', function(done) {
setStepStateToIndex(3);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'dateAndTime');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('geolocation screen >', function(done) {
setStepStateToIndex(4);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'geolocation');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('import contacts screen >', function(done) {
setStepStateToIndex(5);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'importContacts3');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('firefox accounts screen >', function(done) {
setStepStateToIndex(6);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'firefox-accounts');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('welcome screen >', function(done) {
setStepStateToIndex(7);
var observer = new MutationObserver(function(records) {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'aboutBrowser');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('privacy screen >', function(done) {
setStepStateToIndex(8);
var observer = new MutationObserver(function() {
done(function() {
observer.disconnect();
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'aboutBrowser');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
});
suite('Metrics Settings>', function() {
const DOGFOODSETTING = 'debug.performance_data.dogfooding';
const METRICSSETTING = 'metrics.selectedMetrics.level';
var basicMetrics, enhancedMetrics, noneMetrics;
var observerConfig = {
attributes: true
};
setup(function() {
// Needed to make sure that there is a DOM change to trigger
// the MutationObserver.
MockNavigatorSettings.mSetup();
MockNavigatorSettings.mSyncRepliesOnly = true;
basicMetrics = document.getElementById('metrics-basic');
enhancedMetrics = document.getElementById('metrics-enhanced');
noneMetrics = document.getElementById('metrics-none');
setStepStateToIndex(1);
});
teardown(function() {
MockNavigatorSettings.mTeardown();
});
test('correct default button should be enabled and others disabled > ',
function(done) {
MockNavigatorSettings.mSettings[METRICSSETTING] = 'Basic';
setStepStateToIndex(7);
var observer = new MutationObserver(function(records) {
done(function () {
MockNavigatorSettings.mReplyToRequests();
observer.disconnect();
// Only default selection shoudl be enabled.
assert.equal(basicMetrics.checked, true);
assert.equal(enhancedMetrics.checked, undefined);
assert.equal(noneMetrics.checked, undefined);
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('metrics radio buttons should be disabled for dogfooders > ',
function(done) {
MockNavigatorSettings.mSettings[DOGFOODSETTING] = true;
setStepStateToIndex(7);
var observer = new MutationObserver(function(records) {
done(function () {
MockNavigatorSettings.mReplyToRequests();
observer.disconnect();
// All radio buttons should be disabled.
assert.equal(enhancedMetrics.getAttribute('disabled'), 'true');
assert.equal(basicMetrics.getAttribute('disabled'), 'true');
assert.equal(noneMetrics.getAttribute('disabled'), 'true');
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
test('metrics radio should be enabled for non dogfooders > ',
function(done) {
MockNavigatorSettings.mSettings[DOGFOODSETTING] = false;
setStepStateToIndex(7);
var observer = new MutationObserver(function(records) {
done(function () {
MockNavigatorSettings.mReplyToRequests();
observer.disconnect();
// All radio buttons should be enabled.
assert.equal(enhancedMetrics.getAttribute('disabled'), null);
assert.equal(basicMetrics.getAttribute('disabled'), null);
assert.equal(noneMetrics.getAttribute('disabled'), null);
});
});
observer.observe(UIManager.mainTitle, observerConfig);
});
});
suite('SIM pin > ', function() {
var cardStateChangeCallback = null;
var handleCardStateStub = null;
setup(function() {
setStepStateToIndex(0);
handleCardStateStub.reset();
});
teardown(function() {
});
suiteSetup(function() {
handleCardStateStub = sinon.stub(SimManager, 'handleCardState',
function(cb, skipUnlockScreen) {
cardStateChangeCallback = cb;
});
sinon.stub(SimManager, 'available', function() {
return true;
});
});
suiteTeardown(function() {
SimManager.handleCardState.restore();
SimManager.available.restore();
});
test('with SIM card pin required', function() {
MockIccHelper.setProperty('cardState', 'pinRequired');
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
// Fire a cardstate change
cardStateChangeCallback('ready');
// Ensure we don't skip this current state
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
});
test('skip pin and go back', function() {
MockIccHelper.setProperty('cardState', 'pinRequired');
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
// Make sure we don't skip unlock screens on way forward.
assert.isTrue(handleCardStateStub.calledWith(
cardStateChangeCallback, false));
// Skip step 2, sim pin entry
Navigation.skipStep();
Navigation.skipMobileDataScreen = true;
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 2);
// Go back
Navigation.back();
assert.equal(Navigation.previousStepIndex, 2);
assert.equal(Navigation.currentStepIndex, 1);
// Make sure we skip unlock screens going back.
assert.isTrue(handleCardStateStub.calledWith(
cardStateChangeCallback, true));
});
test('skip pin and go forward', function() {
MockIccHelper.setProperty('cardState', 'pinRequired');
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
// Make sure we don't skip unlock screens on way forward.
assert.isTrue(handleCardStateStub.calledWith(
cardStateChangeCallback, false));
// Skip step 2, sim pin entry
Navigation.skipStep();
Navigation.skipMobileDataScreen = true;
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 2);
// Go forward
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 2);
assert.equal(Navigation.currentStepIndex, 3);
});
});
suite('SIM mandatory', function() {
var hash = '#SIM_mandatory';
setup(function() {
Navigation.simMandatory = true;
setStepStateToIndex(0);
});
teardown(function() {
Navigation.simMandatory = false;
});
test('without SIM card', function() {
MockIccHelper.setProperty('cardState', null);
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
assert.equal(window.location.hash, hash);
Navigation.back();
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
assert.equal(window.location.hash, hash);
});
test('with SIM card', function() {
MockIccHelper.setProperty('cardState', 'ready');
Navigation.forward();
assert.equal(Navigation.previousStepIndex, 0);
assert.equal(Navigation.currentStepIndex, 1);
assert.equal(window.location.hash,
Navigation.stepAt(Navigation.currentStepIndex).hash);
});
});
suite('external-url-loader >', function() {
var link;
setup(function() {
this.sinon.stub(window, 'open');
this.sinon.spy(UIManager, 'displayOfflineDialog');
// load welcome page which has external link on it
setStepStateToIndex(7);
link = UIManager.activationScreen.querySelector('a.external');
});
test('handles external links when online', function() {
navigator.onLine = true;
link.click();
assert.ok(window.open.called);
assert.ok(window.open.calledWith(link.href));
});
test('shows an error when offline', function() {
navigator.onLine = false;
link.click();
assert.isFalse(window.open.called);
var title = link.title,
href = link.href;
assert.ok(UIManager.displayOfflineDialog.calledWith(href, title));
});
});
});

View file

@ -0,0 +1,109 @@
/* global MocksHelper, MockNavigatorSettings, MockIccHelper, OperatorVariant */
'use strict';
require('/shared/test/unit/mocks/mock_icc_helper.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp('ftu/js/operatorVariant.js');
var mocksHelper = new MocksHelper([
'IccHelper'
]).init();
suite('operatorVariant set First run state >', function() {
const KEY_SIM_ON_1ST_RUN = 'ftu.simPresentOnFirstBoot';
var SAVE_STATE_WAIT_TIMEOUT = 200;
var realSettings;
suiteSetup(function() {
mocksHelper.suiteSetup();
realSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.mozSettings = realSettings;
realSettings = null;
});
// helper to change single key-value of mozSettings
function changeSettings(key, value) {
var cset = {};
cset[key] = value;
MockNavigatorSettings.createLock().set(cset);
}
setup(function() {
mocksHelper.setup();
});
var testCases = [
{
'title': 'Operator Variant set first run type - no previously value, ' +
'known mcc-mnc',
'preValSet': undefined,
'cardState': 'ready',
'iccInfo': {
'mcc': '214',
'mnc': '07'
},
'expecValSet': '214-007'
},
{
'title': 'Operator Variant set first run type - no previously value, ' +
'unknown mcc-mnc',
'preValSet': undefined,
'cardState': 'ready',
'iccInfo': {},
'expecValSet': '000-000'
},
{
'title': 'Operator Variant first run type - previously value different ' +
'of the current one',
'preValSet': '333-333',
'cardState': 'ready',
'iccInfo': {
'mcc': '214',
'mnc': '07'
},
'expecValSet': '333-333'
},
{
'title': 'Operator Variant set first run - no previously value, no cardState',
'preValSet': undefined,
'cardState': undefined,
'iccInfo': {
'mcc': '214',
'mnc': '07'
},
'expecValSet': '000-000'
},
{
'title': 'Operator Variant first run - has previously value, no cardState',
'preValSet': '444-444',
'cardState': undefined,
'iccInfo': {
'mcc': '214',
'mnc': '07'
},
'expecValSet': '444-444'
}
];
testCases.forEach((function(testCase) {
test(testCase.title, function(done) {
MockIccHelper.mProps.cardState = testCase.cardState;
MockIccHelper.mProps.iccInfo = testCase.iccInfo;
changeSettings(KEY_SIM_ON_1ST_RUN, testCase.preValSet);
OperatorVariant.setSIMOnFirstBootState();
setTimeout(function() {
var mSettings = MockNavigatorSettings.mSettings;
assert.equal(mSettings[KEY_SIM_ON_1ST_RUN], testCase.expecValSet);
done();
}, SAVE_STATE_WAIT_TIMEOUT);
});
}));
});

View file

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

View file

@ -0,0 +1,689 @@
/* global UIManager, SimManager, MobileOperator, MocksHelper,
MockNavigatorMozIccManager, MockNavigatorMozMobileConnections,
Navigation, MockL10n */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
require('/shared/test/unit/mocks/mock_mobile_operator.js');
requireApp('ftu/test/unit/mock_ui_manager.js');
require('/shared/test/unit/mocks/mock_l10n.js');
requireApp('ftu/js/sim_manager.js');
requireApp('ftu/js/navigation.js');
require('/shared/test/unit/load_body_html_helper.js');
var mocksHelperForSimManager = new MocksHelper([
'UIManager',
'MobileOperator'
]).init();
suite('sim mgmt >', function() {
var realL10n,
realMozIccManager,
realMozMobileConnections;
var navigationStub,
iccId0,
iccInfo0,
req,
getCardLockRetryCountStub;
var l10nAttrs;
var setupRetryCount = function() {
req = { result: { retryCount: 3 } };
getCardLockRetryCountStub = sinon.stub(iccInfo0, 'getCardLockRetryCount',
function() {
return req;
});
};
var fireRetryCountCallback = function() {
req.onsuccess && req.onsuccess();
};
var teardownRetryCount = function() {
getCardLockRetryCountStub.restore();
};
mocksHelperForSimManager.attachTestHelpers();
suiteSetup(function() {
loadBodyHTML('/index.html');
realMozIccManager = navigator.mozIccManager;
navigator.mozIccManager = MockNavigatorMozIccManager;
realMozMobileConnections = navigator.mozMobileConnections;
MockNavigatorMozMobileConnections[0].iccId =
navigator.mozIccManager.iccIds[0];
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realL10n = document.l10n;
document.l10n = MockL10n;
iccId0 = navigator.mozIccManager.iccIds[0];
iccInfo0 = navigator.mozIccManager.getIccById(iccId0);
});
setup(function() {
SimManager.init();
UIManager.activationScreen.classList.remove('show');
UIManager.unlockSimScreen.classList.add('show');
setupRetryCount();
});
teardown(function() {
teardownRetryCount();
});
suiteTeardown(function() {
document.body.innerHTML = '';
navigator.mozMobileConnections = realMozMobileConnections;
realMozMobileConnections = null;
navigator.mozIccManager = realMozIccManager;
realMozIccManager = null;
document.l10n = realL10n;
realL10n = null;
});
test('"Skip" hides the screen', function() {
iccInfo0.cardState = 'pinRequired';
SimManager.handleCardState();
fireRetryCountCallback();
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isTrue(getCardLockRetryCountStub.calledOnce);
SimManager.skip();
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
});
test('"Back" hides the screen', function() {
iccInfo0.cardState = 'pinRequired';
SimManager.handleCardState();
fireRetryCountCallback();
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
navigationStub = sinon.stub(Navigation, 'back');
SimManager.back();
assert.ok(navigationStub.calledOnce);
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
navigationStub.restore();
});
suite('Handle state changes', function() {
setup(function() {
UIManager.unlockSimScreen.classList.remove('show');
});
test('pinRequired shows PIN screen', function() {
iccInfo0.cardState = 'pinRequired';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pincodeScreen.classList.contains('show'));
assert.isFalse(UIManager.pukcodeScreen.classList.contains('show'));
assert.isFalse(UIManager.xckcodeScreen.classList.contains('show'));
fireRetryCountCallback();
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isTrue(getCardLockRetryCountStub.calledOnce);
});
test('pukRequired shows PUK screen', function() {
iccInfo0.cardState = 'pukRequired';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.equal(UIManager.pinLabel.getAttribute('data-l10n-id'), 'type_pin');
assert.isFalse(UIManager.pincodeScreen.classList.contains('show'));
assert.isTrue(UIManager.pukcodeScreen.classList.contains('show'));
assert.isFalse(UIManager.xckcodeScreen.classList.contains('show'));
fireRetryCountCallback();
assert.isFalse(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(getCardLockRetryCountStub.calledOnce);
});
test('networkLocked shows XCK screen', function() {
iccInfo0.cardState = 'networkLocked';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.isFalse(UIManager.pincodeScreen.classList.contains('show'));
assert.isFalse(UIManager.pukcodeScreen.classList.contains('show'));
assert.isTrue(UIManager.xckcodeScreen.classList.contains('show'));
fireRetryCountCallback();
assert.isFalse(UIManager.xckRetriesLeft.classList.contains('hidden'));
assert.isTrue(getCardLockRetryCountStub.calledOnce);
});
});
suite('Handle state changes DSDS', function() {
var iccId1;
var iccInfo1;
suiteSetup(function() {
iccId1 = '98765';
navigator.mozIccManager.addIcc(iccId1);
iccInfo1 = navigator.mozIccManager.getIccById(iccId1);
navigator.mozMobileConnections.mAddMobileConnection();
navigator.mozMobileConnections[1].iccId = iccId1;
navigator.mozMobileConnections[1].iccInfo = iccInfo1;
SimManager.updateIccState(iccId1);
SimManager.simSlots = 2;
});
suiteTeardown(function() {
navigator.mozIccManager.removeIcc(iccId1);
navigator.mozMobileConnections.mRemoveMobileConnection(1);
SimManager.simSlots = 1;
SimManager.icc1 = null;
});
test('pinRequired DSDS screen', function() {
iccInfo0.cardState = 'pinRequired';
iccInfo1.cardState = 'pinRequired';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
l10nAttrs = document.l10n.getAttributes(UIManager.pinLabel);
assert.equal(l10nAttrs.id, 'pincodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 1});
fireRetryCountCallback();
assert.isTrue(getCardLockRetryCountStub.calledOnce);
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
SimManager.skip();
l10nAttrs = document.l10n.getAttributes(UIManager.pinLabel);
assert.equal(l10nAttrs.id, 'pincodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 2});
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
});
test('pukRequired DSDS screen', function() {
iccInfo0.cardState = 'pukRequired';
iccInfo1.cardState = 'pukRequired';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
l10nAttrs = document.l10n.getAttributes(UIManager.pukLabel);
assert.equal(l10nAttrs.id, 'pukcodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 1});
fireRetryCountCallback();
assert.isTrue(getCardLockRetryCountStub.calledOnce);
assert.isFalse(UIManager.pukRetriesLeft.classList.contains('hidden'));
SimManager.skip();
l10nAttrs = document.l10n.getAttributes(UIManager.pukLabel);
assert.equal(l10nAttrs.id, 'pukcodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 2});
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
});
test('xckRequired DSDS screen', function() {
iccInfo0.cardState = 'networkLocked';
iccInfo1.cardState = 'networkLocked';
SimManager.handleCardState();
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
l10nAttrs = document.l10n.getAttributes(UIManager.xckLabel);
assert.equal(l10nAttrs.id, 'nckcodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 1});
fireRetryCountCallback();
assert.isTrue(getCardLockRetryCountStub.calledOnce);
assert.isFalse(UIManager.xckRetriesLeft.classList.contains('hidden'));
SimManager.skip();
l10nAttrs = document.l10n.getAttributes(UIManager.xckLabel);
assert.equal(l10nAttrs.id, 'nckcodeLabel');
assert.deepEqual(l10nAttrs.args, {n: 2});
assert.isTrue(UIManager.xckRetriesLeft.classList.contains('hidden'));
});
});
suite('Unlocking', function() {
suite('PIN unlock ', function() {
setup(function() {
iccInfo0.cardState = 'pinRequired';
SimManager.resetForm();
SimManager.handleCardState();
// start from original state each test
UIManager.pinInput.classList.remove('onerror');
UIManager.pinError.classList.add('hidden');
UIManager.pinRetriesLeft.classList.add('hidden');
});
suite('Unlock button > ', function() {
teardown(function() {
UIManager.pinInput.value = '';
});
test('should be disabled by default', function() {
assert.isTrue(UIManager.unlockSimButton.disabled);
});
test('should be disabled with short PIN', function() {
UIManager.pinInput.value = 123;
UIManager.pinInput.dispatchEvent(new CustomEvent('input'));
assert.isTrue(UIManager.unlockSimButton.disabled);
});
test('should be enabled with proper size PIN', function() {
UIManager.pinInput.value = 1234;
UIManager.pinInput.dispatchEvent(new CustomEvent('input'));
assert.isFalse(UIManager.unlockSimButton.disabled);
});
});
test('too short PIN', function() {
UIManager.pinInput.value = 123;
SimManager.unlock();
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.pinInput.classList.contains('onerror'));
assert.isFalse(UIManager.pinError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('too long PIN', function() {
UIManager.pinInput.value = 123456789;
SimManager.unlock();
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.pinInput.classList.contains('onerror'));
assert.isFalse(UIManager.pinError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('wrong sim pin with > 1 retries', function() {
SimManager.icc0.mozIcc.retryCount = 2;
UIManager.pinInput.value = '0000'; // special failure pin value
SimManager.unlock();
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.pinInput.classList.contains('onerror'));
assert.isFalse(UIManager.pinError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pinError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isFalse(UIManager.pinError.querySelector('.main')
.classList.contains('hidden'));
});
test('wrong sim pin with exactly 1 retry', function() {
SimManager.icc0.mozIcc.retryCount = 1;
UIManager.pinInput.value = '0000'; // special failure pin value
SimManager.unlock();
assert.isFalse(UIManager.pinError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isTrue(UIManager.pinError.querySelector('.main')
.classList.contains('hidden'));
});
test('all fields correct', function() {
fireRetryCountCallback();
assert.isFalse(UIManager.pinRetriesLeft.classList.contains('hidden'));
UIManager.pinInput.value = 1234;
SimManager.unlock();
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
assert.isFalse(UIManager.pinInput.classList.contains('onerror'));
assert.isTrue(UIManager.pinError.classList.contains('hidden'));
assert.isTrue(SimManager.icc0.unlocked);
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
});
});
suite('PUK unlock ', function() {
setup(function() {
iccInfo0.cardState = 'pukRequired';
SimManager.resetForm();
SimManager.handleCardState();
// start from original state each test
UIManager.pukInput.classList.remove('onerror');
UIManager.pukError.classList.add('hidden');
UIManager.pukRetriesLeft.classList.add('hidden');
});
test('wrong length PUK', function() {
UIManager.pukInput.value = 123;
SimManager.unlock();
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.pukInput.classList.contains('onerror'));
assert.isFalse(UIManager.pukError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('too short newPIN', function() {
UIManager.pukInput.value = 12345678;
UIManager.newpinInput.value = 123;
SimManager.unlock();
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.newpinInput.classList.contains('onerror'));
assert.isFalse(UIManager.newpinError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('too long newPIN', function() {
UIManager.pukInput.value = 12345678;
UIManager.newpinInput.value = 123456789;
SimManager.unlock();
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.newpinInput.classList.contains('onerror'));
assert.isFalse(UIManager.newpinError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('different PIN and confirm PIN', function() {
UIManager.pukInput.value = 12345678;
UIManager.newpinInput.value = 1234;
UIManager.confirmNewpinInput.value = 4321;
SimManager.unlock();
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.newpinInput.classList.contains('onerror'));
assert.isTrue(UIManager.confirmNewpinInput.classList.contains(
'onerror'));
assert.isFalse(UIManager.confirmNewpinError.classList.contains(
'hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('wrong sim puk with > 1 retries', function() {
SimManager.icc0.mozIcc.retryCount = 2;
UIManager.pukInput.value = '00000000'; // special failure pin value
UIManager.newpinInput.value = 1234;
UIManager.confirmNewpinInput.value = 1234;
SimManager.unlock();
assert.isFalse(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.pukInput.classList.contains('onerror'));
assert.isFalse(UIManager.pukError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pukError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isFalse(UIManager.pukError.querySelector('.main')
.classList.contains('hidden'));
});
test('wrong sim puk with exactly 1 retry', function() {
SimManager.icc0.mozIcc.retryCount = 1;
UIManager.pukInput.value = '00000000'; // special failure pin value
UIManager.newpinInput.value = 1234;
UIManager.confirmNewpinInput.value = 1234;
SimManager.unlock();
assert.isFalse(UIManager.pukError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isTrue(UIManager.pukError.querySelector('.main')
.classList.contains('hidden'));
});
test('all fields correct', function() {
fireRetryCountCallback();
assert.isFalse(UIManager.pukRetriesLeft.classList.contains('hidden'));
UIManager.pukInput.value = 12345678;
UIManager.newpinInput.value = 1234;
UIManager.confirmNewpinInput.value = 1234;
SimManager.unlock();
assert.isTrue(UIManager.pukRetriesLeft.classList.contains('hidden'));
assert.isFalse(UIManager.pukInput.classList.contains('onerror'));
assert.isTrue(UIManager.pukError.classList.contains('hidden'));
assert.isFalse(UIManager.newpinInput.classList.contains('onerror'));
assert.isTrue(UIManager.newpinError.classList.contains('hidden'));
assert.isFalse(UIManager.confirmNewpinInput.classList.contains(
'onerror'));
assert.isTrue(UIManager.confirmNewpinError.classList.contains(
'hidden'));
assert.isTrue(SimManager.icc0.unlocked);
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
});
});
suite('XCK unlock ', function() {
setup(function() {
iccInfo0.cardState = 'networkLocked';
SimManager.resetForm();
SimManager.handleCardState();
// start from original state each test
UIManager.xckInput.classList.remove('onerror');
UIManager.xckError.classList.add('hidden');
UIManager.xckRetriesLeft.classList.add('hidden');
});
test('too short XCK', function() {
UIManager.xckInput.value = 1234567;
SimManager.unlock();
assert.isTrue(UIManager.xckRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.xckInput.classList.contains('onerror'));
assert.isFalse(UIManager.xckError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('too long XCK', function() {
UIManager.xckInput.value = 12345678901234567;
SimManager.unlock();
assert.isTrue(UIManager.xckRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.xckInput.classList.contains('onerror'));
assert.isFalse(UIManager.xckError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
});
test('wrong sim XCK with > 1 retries', function() {
SimManager.icc0.mozIcc.retryCount = 2;
UIManager.xckInput.value = '00000000'; // special failure pin value
SimManager.unlock();
assert.isFalse(UIManager.xckRetriesLeft.classList.contains('hidden'));
assert.isTrue(UIManager.xckInput.classList.contains('onerror'));
assert.isFalse(UIManager.xckError.classList.contains('hidden'));
assert.isFalse(SimManager.icc0.unlocked);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.xckError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isFalse(UIManager.xckError.querySelector('.main')
.classList.contains('hidden'));
});
test('wrong sim XCK with exactly 1 retry', function() {
SimManager.icc0.mozIcc.retryCount = 1;
UIManager.xckInput.value = '00000000'; // special failure pin value
SimManager.unlock();
assert.isFalse(UIManager.xckError.querySelector('.lastchance')
.classList.contains('hidden'));
assert.isTrue(UIManager.xckError.querySelector('.main')
.classList.contains('hidden'));
});
test('all fields correct', function() {
fireRetryCountCallback();
assert.isFalse(UIManager.xckRetriesLeft.classList.contains('hidden'));
UIManager.xckInput.value = 12345678;
SimManager.unlock();
assert.isTrue(UIManager.xckRetriesLeft.classList.contains('hidden'));
assert.isFalse(UIManager.xckInput.classList.contains('onerror'));
assert.isTrue(UIManager.xckError.classList.contains('hidden'));
assert.isTrue(SimManager.icc0.unlocked);
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
});
});
});
suite('No Telephony', function() {
var realMozMobileConnections;
setup(function() {
realMozMobileConnections = navigator.mozMobileConnections;
// no telephony API
navigator.mozMobileConnections = null;
SimManager.init();
});
teardown(function() {
navigator.mozMobileConnections = realMozMobileConnections;
realMozMobileConnections = null;
});
test('hide sim import section', function() {
SimManager.skip();
assert.isFalse(UIManager.simImport.classList.contains('show'));
});
});
suite('Operator', function() {
setup(function() {
iccInfo0.cardState = 'ready';
MobileOperator.mOperator = '';
SimManager.updateSIMInfoText(SimManager.icc0);
});
test('should read locked state', function() {
iccInfo0.cardState = 'pinRequired';
SimManager.updateSIMInfoText(SimManager.icc0);
assert.equal('simPinLocked',
UIManager.simCarrier1.getAttribute('data-l10n-id'));
});
test('should read no operator when unlocked', function() {
assert.equal(UIManager.simCarrier1.getAttribute('data-l10n-id'),
'searchingOperator');
});
test('should update the operator on voicechange', function() {
MobileOperator.mOperator = 'Fake Operator';
MockNavigatorMozMobileConnections[0].triggerEventListeners('voicechange');
assert.equal('Fake Operator', UIManager.simCarrier1.textContent);
});
test('should have 1 event listener per ICC', function() {
SimManager.updateSIMInfoText(SimManager.icc0);
SimManager.updateSIMInfoText(SimManager.icc0);
assert.equal(1, SimManager.voiceChangeListeners.length);
});
});
suite('SIM2 inserted >', function() {
var iccId1;
var iccInfo1;
var realIccId0;
setup(function() {
iccId1 = '98765';
navigator.mozIccManager.addIcc(iccId1);
iccInfo1 = navigator.mozIccManager.getIccById(iccId1);
navigator.mozMobileConnections.mAddMobileConnection();
navigator.mozMobileConnections[1].iccId = iccId1;
navigator.mozMobileConnections[1].iccInfo = iccInfo1;
SimManager.updateIccState(iccId1);
SimManager.simSlots = 2;
realIccId0 = SimManager.icc0;
SimManager.icc0 = null;
this.sinon.stub(Navigation, 'back');
});
teardown(function() {
navigator.mozIccManager.removeIcc(iccId1);
navigator.mozMobileConnections.mRemoveMobileConnection(1);
SimManager.simSlots = 1;
SimManager.icc1 = null;
SimManager.icc0 = realIccId0;
});
test('"simUnlockBack" hides the screen', function() {
iccInfo1.cardState = 'pinRequired';
SimManager.handleCardState();
SimManager.simUnlockBack();
assert.ok(Navigation.back.calledOnce);
assert.isTrue(UIManager.activationScreen.classList.contains('show'));
assert.isFalse(UIManager.unlockSimScreen.classList.contains('show'));
assert.isTrue(UIManager.pinRetriesLeft.classList.contains('hidden'));
});
});
suite('SIM1 and SIM2 inserted >', function() {
var iccId1;
var iccInfo1;
setup(function() {
iccId1 = '98765';
navigator.mozIccManager.addIcc(iccId1);
iccInfo1 = navigator.mozIccManager.getIccById(iccId1);
navigator.mozMobileConnections.mAddMobileConnection();
navigator.mozMobileConnections[1].iccId = iccId1;
navigator.mozMobileConnections[1].iccInfo = iccInfo1;
SimManager.updateIccState(iccId1);
SimManager.simSlots = 2;
this.sinon.spy(SimManager, 'skip');
this.sinon.spy(Navigation, 'back');
});
teardown(function() {
navigator.mozIccManager.removeIcc(iccId1);
navigator.mozMobileConnections.mRemoveMobileConnection(1);
SimManager.simSlots = 1;
SimManager.icc1 = null;
});
test('if SIM1 is skipped, SIM2 screen should be shown', function() {
iccInfo0.cardState = 'pinRequired';
iccInfo1.cardState = 'pinRequired';
SimManager.handleCardState();
SimManager.skip();
assert.ok(SimManager.skip.calledOnce);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.equal(UIManager.pinLabel.getAttribute('data-l10n-args'),
'{"n":2}');
});
test('SIM1 is skipped and user go back', function() {
iccInfo0.cardState = 'pinRequired';
iccInfo1.cardState = 'pinRequired';
SimManager.handleCardState();
SimManager.skip();
assert.ok(SimManager.skip.calledOnce);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.equal(UIManager.pinLabel.getAttribute('data-l10n-args'),
'{"n":2}');
SimManager.icc0.skipped = true;
SimManager.simUnlockBack();
assert.isFalse(Navigation.back.calledOnce);
assert.isFalse(UIManager.activationScreen.classList.contains('show'));
assert.isTrue(UIManager.unlockSimScreen.classList.contains('show'));
assert.equal(UIManager.pinLabel.getAttribute('data-l10n-args'),
'{"n":1}');
});
});
});

View file

@ -0,0 +1,362 @@
/* global MocksHelper, MockNavigatorSettings,
MockL10n, MockNavigatorMozMobileConnections,
IccHelper, tzSelect
*/
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_icc_helper.js');
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/load_body_html_helper.js');
var mocksHelperForTimezones = new MocksHelper([
'IccHelper'
]).init();
suite('timezones >', function() {
var realMozMobileConnections,
realMozSettings,
realXHR,
realL10n;
var regionSelector, citySelector;
var TIMEZONES = {
'Africa': [
{'cc':'NA', 'offset':'+01:00,+02:00', 'city':'Windhoek'}
],
'America': [
{'cc':'VE', 'offset':'-04:30,-04:30', 'city':'Caracas'},
{'cc':'US', 'offset':'-05:00,-04:00', 'city':'New_York'}
],
'Asia': [
{'cc':'TW', 'offset':'+08:00,+08:00', 'city':'Taipei'}
],
'Australia': [
{'cc':'AU', 'offset':'+10:00,+11:00', 'city':'Sydney'}
],
'Europe': [
{'cc':'ES', 'offset':'+01:00,+02:00', 'city':'Madrid'},
{'cc':'GB', 'offset':'+00:00,+01:00', 'city':'London'}
],
'Indian': [
{'cc':'RE', 'offset':'+04:00,+04:00', 'city':'Reunion'}
]
};
var APN_TZ = {
'649': 'Africa/Windhoek',
'734': 'America/Caracas',
'310': 'America/New_York',
'311': 'America/New_York',
'466': 'Asia/Taipei',
'505': 'Australia/Sydney',
'647': 'Indian/Reunion',
'214': 'Europe/Madrid',
'234': {
'00': 'Europe/London',
'02': 'Europe/London',
'03': 'Europe/Jersey',
'10': 'Europe/London',
'11': 'Europe/London',
'15': 'Europe/London',
'20': 'Europe/London',
'30': 'Europe/London',
'31': 'Europe/London',
'32': 'Europe/London',
'33': 'Europe/London',
'34': 'Europe/London',
'36': 'Europe/Isle_of_Man',
'50': 'Europe/Jersey',
'55': 'Europe/Jersey',
'58': 'Europe/Isle_of_Man'
}
};
function getTextFromSelectors() {
// Careful with the text transformations for the lists
// we add 'tzRegion-' to mapped regions
var mRegion = regionSelector.querySelectorAll('option')
[regionSelector.selectedIndex].getAttribute('data-l10n-id');
var mCity = citySelector.querySelectorAll('option')
[citySelector.selectedIndex].textContent;
return {
region: mRegion,
city: mCity
};
}
function MockXMLHttpRequest() {
var mResponse = MockXMLHttpRequest.mResponse;
this.open = function(method, url) {
if (url) {
var urlArray = url.split('/');
var file = urlArray[urlArray.length - 1].split('.')[0];
switch (file) {
case 'tz':
mResponse = TIMEZONES;
break;
case 'apn_tz':
mResponse = APN_TZ;
break;
}
}
};
this.send = function() {
this.response = mResponse;
this.timeout = setTimeout(this.onload.bind(this));
};
this.abort = function() {
if (this.timeout) {
clearTimeout(this.clearTimeout);
}
};
}
mocksHelperForTimezones.attachTestHelpers();
suiteSetup(function(done) {
// we use the real XHR here, and mock it for the rest of the tests
loadBodyHTML('/index.html');
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
MockNavigatorSettings.mSettings['time.timezone'] = 'America/New_York';
MockNavigatorSettings.mSettings['time.timezone.user-selected'] = null;
realL10n = document.l10n;
document.l10n = MockL10n;
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realXHR = window.XMLHttpRequest;
window.XMLHttpRequest = MockXMLHttpRequest;
require('/shared/js/tz_select.js', done);
regionSelector = document.getElementById('tz-region');
citySelector = document.getElementById('tz-city');
});
suiteTeardown(function() {
navigator.mozMobileConnections = realMozMobileConnections;
navigator.mozSettings = realMozSettings;
document.l10n = realL10n;
window.XMLHttpRequest = realXHR;
});
test('> observing changes on time.timezone', function() {
assert.isNotNull(navigator.mozSettings.mObservers['time.timezone']);
});
test('> no previous user interaction, use DEFAULT timezone', function() {
assert.equal(navigator.mozSettings.mSettings['time.timezone'],
'America/New_York');
});
suite('> very first run, no user interaction', function() {
suite('> no SIM card', function() {
setup(function() {
IccHelper.setProperty('iccInfo', null);
});
test('we use DEFAULT value', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.equal(currentValues.region, 'tzRegion-America');
assert.equal(currentValues.city, 'New York');
assert.isTrue(needsConfirmation);
done();
}
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
});
suite('> SIM ready, connection avaiable', function() {
var conn;
setup(function() {
// we need a live connection for this test (network)
conn = new window.MockMobileconnection();
conn.voice = {
connected: true,
network: {
mcc: 466, // Asia / Taipei
mnc: 2
}
};
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, 0);
});
teardown(function() {
conn = null;
MockNavigatorMozMobileConnections.mRemoveMobileConnection();
});
test('get timezone from the connection', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.equal(currentValues.region, 'tzRegion-Australia');
assert.equal(currentValues.city, 'Sydney');
// When the timezone (and presumably the time) is loaded from
// network, it doesn't need to be confirmed by the user.
assert.isFalse(needsConfirmation);
done();
}
conn.voice.network.mcc = 505; // Australia/Sydney
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
test('if unknown MCC/MNC, use default', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
// DEFAULT is America/New_York
assert.equal(currentValues.region, 'tzRegion-America');
assert.equal(currentValues.city, 'New York');
assert.isTrue(needsConfirmation);
done();
}
conn.voice.network.mcc = 1; // unknown
conn.voice.network.mnc = 1; // unknown
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
});
suite('> SIM ready, no connection', function() {
var conn;
setup(function() {
// no connection
conn = new window.MockMobileconnection();
conn.voice = null;
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, 0);
// but access to the SIM data
IccHelper.setProperty('cardstate', 'ready');
});
teardown(function() {
conn = null;
MockNavigatorMozMobileConnections.mRemoveMobileConnection();
});
test('get timezone from SIM', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.equal(currentValues.region, 'tzRegion-Europe');
assert.equal(currentValues.city, 'London');
assert.isTrue(needsConfirmation);
done();
}
IccHelper.setProperty('iccInfo',{
mcc: 234, // Europe/London
mnc: 32
});
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
test('if unknown MCC/MNC, use default', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.equal(currentValues.region, 'tzRegion-America');
assert.equal(currentValues.city, 'New York');
assert.isTrue(needsConfirmation);
done();
}
IccHelper.setProperty('iccInfo',{
mcc: 0, // unknown
mnc: 0 // unknown
});
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
});
suite('> protected SIM card', function() {
var conn;
setup(function() {
// no connection
conn = new window.MockMobileconnection();
conn.voice = null;
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, 0);
// but access to the SIM data
IccHelper.setProperty('cardstate', 'pin_required');
});
teardown(function() {
MockNavigatorMozMobileConnections.mRemoveMobileConnection();
});
test('> default is used while waiting for access', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.lengthOf(IccHelper.mEventListeners.iccinfochange, 1);
assert.equal(currentValues.region, 'tzRegion-America');
assert.equal(currentValues.city, 'New York');
assert.isTrue(needsConfirmation);
done();
}
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
test('> reload when granted access to SIM info', function(done) {
var calls = 0;
function tzLoaded(timezone, needsConfirmation) {
calls++;
IccHelper.mTriggerEventListeners('iccinfochange', {});
// we expect 2 calls before finishing
if (calls === 2) {
var currentValues = getTextFromSelectors();
done(function check_asserts() {
assert.equal(currentValues.region, 'tzRegion-Europe');
assert.equal(currentValues.city, 'London');
assert.isTrue(needsConfirmation);
});
}
}
IccHelper.setProperty('iccInfo',{
mcc: 234, // Europe
mnc: 20 // London
});
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
});
});
suite('> user chose previously', function() {
setup(function() {
MockNavigatorSettings.mSettings['time.timezone.user-selected'] =
'Europe/Madrid';
});
teardown(function() {
MockNavigatorSettings.mSettings['time.timezone.user-selected'] = null;
});
test('> previous selection prevails', function(done) {
function tzLoaded(timezone, needsConfirmation) {
var currentValues = getTextFromSelectors();
assert.equal(currentValues.region, 'tzRegion-Europe');
assert.equal(currentValues.city, 'Madrid');
assert.isTrue(needsConfirmation);
done();
}
tzSelect(regionSelector, citySelector, tzLoaded, tzLoaded);
});
});
});

View file

@ -0,0 +1,279 @@
/* global Tutorial, FinishScreen, LazyLoader,
MocksHelper, MockL10n, MockNavigatormozApps,
MockNavigatorSettings */
'use strict';
require('/shared/js/lazy_loader.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('ftu/test/unit/mock_screenlayout.js');
requireApp('ftu/test/unit/mock_finish_screen.js');
requireApp('ftu/js/finish_screen.js');
requireApp('ftu/js/utils.js');
requireApp('ftu/js/tutorial_utils.js');
suite('Tutorial >', function() {
var mocksHelperForFTU = new MocksHelper([
'ScreenLayout',
'FinishScreen'
]).init();
mocksHelperForFTU.attachTestHelpers();
function mockConfig(stepCount) {
var steps = [];
for (; stepCount; stepCount--) {
steps.push({
video: '/style/images/tutorial/Homescreen.mp4',
l10nKey: 'tutorial-homescreen-tiny'
});
}
var config = {
'default': {
steps: steps
}
};
return config;
}
var realL10n;
var realMozApps;
var realMozSettings;
suiteSetup(function(done) {
realL10n = document.l10n;
document.l10n = MockL10n;
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
loadBodyHTML('/index.html');
requireApp('ftu/js/tutorial.js', done);
});
suiteTeardown(function() {
document.l10n = realL10n;
navigator.mozApps = realMozApps;
navigator.mozSettings = realMozSettings;
realL10n = null;
document.body.innerHTML = '';
});
teardown(function() {
MockNavigatormozApps.mTeardown();
});
test(' sanity test Tutorial', function() {
assert.equal(typeof Tutorial, 'object');
assert.equal(typeof Tutorial.loadConfig, 'function');
assert.equal(typeof Tutorial.init, 'function');
assert.equal(typeof Tutorial.start, 'function');
});
test(' sanity test mocks', function(done) {
this.sinon.stub(LazyLoader, 'getJSON')
.returns(Promise.resolve(mockConfig(2)));
Tutorial.loadConfig().then(onOutcome, onOutcome)
.then(done, done);
function onOutcome() {
assert.equal(Tutorial.config['default'].steps.length, 2);
}
});
suite(' lifecycle', function() {
teardown(function(done) {
Tutorial.reset().then(() => {
document.getElementById('tutorial').classList.remove('show');
done();
});
});
test('reset', function(done) {
function onOutcome() {
// config should be already loaded
Tutorial.init();
//
assert.ok(Tutorial.config);
}
function reset() {
return Tutorial.reset().then(() => {
assert.ok(!Tutorial.config);
});
}
Tutorial.loadConfig().then(onOutcome, onOutcome)
.then(reset, reset)
.then(done, done);
});
test('init before loadConfig', function() {
Tutorial.init();
assert.ok(!Tutorial.config, 'Tutorial.config not yet defined');
});
test('promised config', function(done) {
function onOutcome(arg) {
assert.isTrue(true, 'loadConfig promise was resolved');
}
function onReject(arg) {
assert.isFalse(1, 'loadConfig promise was rejected');
}
var result = Tutorial.loadConfig();
assert.ok(result && typeof result.then == 'function',
'loadConfig returned a thenable');
if (result) {
result.then(onOutcome, onReject).then(done, done);
} else {
done();
}
});
test('start during init', function(done) {
Tutorial.init();
Tutorial.start(function() {
done(function() {
assert.ok(Tutorial.config);
assert.isTrue(
document.getElementById('tutorial').classList.contains('show')
);
});
});
});
test('start after init', function(done) {
Tutorial.init(null, function() {
Tutorial.start(function() {
done(function() {
assert.ok(Tutorial.config);
assert.isTrue(
document.getElementById('tutorial').classList.contains('show')
);
});
});
});
});
test('start despite failure to load media', function(done) {
var tutorialWasInitialized = false;
var jsonMock = {
'default': {
steps: [{
video: '/style/images/tutorial/NotThere.mp4',
l10nKey: 'tutorial-homescreen-tiny'
}]
}
};
this.sinon.stub(LazyLoader, 'getJSON').returns(Promise.resolve(jsonMock));
window.addEventListener('tutorialinitialized', function() {
tutorialWasInitialized = true;
});
Tutorial.init();
Tutorial.start(function() {
done(function() {
assert.isTrue(tutorialWasInitialized, 'tutorialinitialized fired');
});
});
});
});
suite(' post-init', function() {
var getJSONStub;
suiteSetup(function(done) {
Tutorial.reset().then(() => {
getJSONStub = sinon.stub(LazyLoader, 'getJSON')
.returns(Promise.resolve(mockConfig(3)));
Tutorial.init();
Tutorial.start(function() {
done();
});
});
});
suiteTeardown(function() {
getJSONStub.restore();
});
test(' is shown properly after Tutorial.start', function() {
// Is the tutorial shown?
assert.isTrue(
document.getElementById('tutorial').classList.contains('show')
);
});
test(' check dataset after Tutorial.start', function() {
// Are we in Step 1?
assert.equal(
document.getElementById('tutorial').dataset.step,
1
);
});
test(' forward', function(done) {
Tutorial.next().then(done, done);
// Are we in Step 2?
assert.equal(
document.getElementById('tutorial').dataset.step,
2
);
});
test(' back', function(done) {
Tutorial.back().then(done, done);
// Are we in Step 1?
assert.equal(
document.getElementById('tutorial').dataset.step,
1
);
});
test(' text & src are the right ones for the current step (2)',
function(done) {
// Spy the l10n
this.sinon.spy(document.l10n, 'setAttributes');
// Move forwad again
function onNextLoaded() {
// Are we in Step 2?
assert.equal(
document.getElementById('tutorial').dataset.step,
2
);
// We are in step 2 and taking into account the current layout
assert.equal(document.l10n.setAttributes.args[0][1],
Tutorial.config['default'].steps[1].l10nKey);
// Now we check the element src.
// As we are in 'tiny' (default layout in the mock)
// the 2nd step should be notifications
var imgSRC = document.querySelector(
'#tutorial-step-media > *:not([hidden])'
).src;
assert.isTrue(imgSRC.contains('Homescreen.mp4'),
'Expected Homescreen.mp4 in ' + imgSRC);
}
Tutorial.next().then(onNextLoaded, onNextLoaded).then(done, done);
});
test(' hide the tutorial when done and move to FinishScreen', function() {
this.sinon.spy(FinishScreen, 'init');
// Call to 'done' method
Tutorial.done();
// Is the tutorial hidden now?
assert.isFalse(
document.getElementById('tutorial').classList.contains('show')
);
// Have we called to FinishScreen?
assert.isTrue(FinishScreen.init.calledOnce);
// Reset the spy
FinishScreen.init.reset();
});
});
});

View file

@ -0,0 +1,134 @@
/* global TutorialUtils, MockPromise, MockEventTarget */
'use strict';
require('/shared/test/unit/mocks/mock_event_target.js');
require('/shared/test/unit/mocks/mock_promise.js');
suite('TutorialUtils >', function() {
var fakeFetchPromise;
function MockVideoElement() {}
suiteSetup(function(done) {
loadBodyHTML('/index.html');
MockVideoElement.prototype = Object.create(MockEventTarget.prototype);
MockVideoElement.prototype.nodeName = 'VIDEO';
requireApp('ftu/js/tutorial_utils.js', done);
});
suiteTeardown(function() {
document.body.innerHTML = '';
});
setup(function() {
fakeFetchPromise = new MockPromise();
this.sinon.stub(window, 'fetch').returns(fakeFetchPromise);
});
test('sanity test TutorialUtils', function() {
assert.equal(typeof TutorialUtils, 'object');
assert.equal(typeof TutorialUtils.loadMedia, 'function');
assert.equal(typeof TutorialUtils.loadAndPlayMedia, 'function');
assert.equal(typeof TutorialUtils.fileExists, 'function');
assert.equal(typeof TutorialUtils.Sequence, 'function');
});
suite('fileExists', function() {
test('fileExists resolves success with boolean result', function(done) {
TutorialUtils.fileExists('/fake/resource').then((result) => {
assert.isTrue(result);
}).then(done, (ex) => { done(ex); });
fakeFetchPromise.mFulfillToValue({
'ok': true,
'status': 200
});
});
test('fileExists resolves error with boolean result', function(done) {
TutorialUtils.fileExists('/fake/resource').then((result) => {
assert.isFalse(result);
}).then(done, (ex) => { done(ex); });
fakeFetchPromise.mRejectToError({ });
});
});
suite('getBestAssetForDirection', function() {
var filesExist = {
'resource.mp4': true,
'resource-rtl.mp4': true,
'resource2.mp4': true,
'resource2-ltr.mp4': true,
};
setup(function() {
this.sinon.stub(TutorialUtils, 'fileExists', function(path) {
return Promise.resolve(filesExist[path]);
});
});
teardown(function() {
document.dir = '';
});
test('default dir, no ltr asset', function(done) {
TutorialUtils.getBestAssetForDirection('resource.mp4').then((result) => {
assert.equal(result, 'resource.mp4');
}).then(done, (ex) => { done(ex); });
});
test('RTL dir, with rtl asset', function(done) {
document.dir = 'rtl';
TutorialUtils.getBestAssetForDirection('resource.mp4').then((result) => {
assert.equal(result, 'resource-rtl.mp4');
}).then(done, (ex) => { done(ex); });
});
test('default dir, has ltr asset', function(done) {
TutorialUtils.getBestAssetForDirection('resource2.mp4').then((result) => {
assert.equal(result, 'resource2-ltr.mp4');
}).then(done, (ex) => { done(ex); });
});
});
suite('media handling', function() {
var mediaElement;
setup(function() {
mediaElement = new MockVideoElement();
mediaElement.nodeName = 'VIDEO';
mediaElement.load = this.sinon.stub();
});
test('loadMedia resolves on load success', function(done) {
TutorialUtils.loadMedia(mediaElement, 'fake/resource.mp4')
.then((evt) => {
assert.ok(evt, 'loadMedia promise resolved with an event');
}).then(done, (ex) => { done(ex); });
var fakeEvent = { type: 'canplay' };
mediaElement.dispatchEvent(fakeEvent);
});
test('loadMedia resolves on load failure', function(done) {
TutorialUtils.loadMedia(mediaElement, 'fake/resource.mp4')
.then((evt) => {
assert.ok(evt, 'loadMedia promise resolved with an event');
}).then(done, (ex) => { done(ex); });
var fakeEvent = { type: 'error' };
mediaElement.dispatchEvent(fakeEvent);
});
test('loadMediaAndPlay', function(done) {
this.sinon.stub(TutorialUtils, 'loadMedia', function() {
return Promise.resolve({});
});
mediaElement.play = this.sinon.stub();
TutorialUtils.loadAndPlayMedia(mediaElement, 'fake/resource.mp4')
.then((evt) => {
assert.ok(mediaElement.play.calledOnce);
}).then(done, (ex) => { done(ex); });
});
});
});

View file

@ -0,0 +1,579 @@
/* global MockFxAccountsIACHelper, MocksHelper, MockL10n, MockMozApps,
MockTzSelect, Navigation, UIManager, WifiManager, WifiUI,
MockSettingsListener, MockNavigatorSettings */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/mocks/mock_settings_listener.js');
require('/shared/js/component_utils.js');
require('/shared/elements/gaia_switch/script.js');
requireApp('ftu/js/ui.js');
requireApp('ftu/js/navigation.js');
requireApp('ftu/test/unit/mock_tutorial.js');
requireApp('ftu/test/unit/mock_mozapps.js');
requireApp('ftu/test/unit/mock_time_manager.js');
requireApp('ftu/test/unit/mock_wifi_manager.js');
requireApp('ftu/test/unit/mock_tz_select.js');
requireApp('ftu/test/unit/mock_operatorVariant.js');
requireApp('ftu/test/unit/mock_fx_accounts_iac_helper.js');
requireApp('ftu/test/unit/mock_utils.js');
requireApp('ftu/test/unit/mock_data_mobile.js');
var mocksHelperForUI = new MocksHelper([
'Tutorial',
'TimeManager',
'WifiUI',
'WifiManager',
'OperatorVariant',
'utils',
'DataMobile',
'SettingsListener'
]).init();
if (!window.tzSelect) {
window.tzSelect = null;
}
suite('UI Manager > ', function() {
var realL10n,
realMozApps,
realFxAccountsIACHelper,
realTzSelect,
realSettings,
realSettingsListener;
var mocksHelper = mocksHelperForUI;
suiteSetup(function() {
realL10n = document.l10n;
document.l10n = MockL10n;
realTzSelect = window.tzSelect;
window.tzSelect = MockTzSelect;
realMozApps = navigator.mozApps;
navigator.mozApps = MockMozApps;
realFxAccountsIACHelper = window.FxAccountsIACHelper;
window.FxAccountsIACHelper = MockFxAccountsIACHelper;
realSettings = navigator.mozSettings;
navigator.mozSettings = window.MockNavigatorSettings;
navigator.mozSettings.mSettings['geolocation.enabled'] = true;
realSettingsListener = window.SettingsListener;
window.SettingsListener = MockSettingsListener;
mocksHelper.suiteSetup();
loadBodyHTML('/index.html');
UIManager.init();
Navigation.init();
UIManager.activationScreen.classList.add('show');
window.location.hash = '#languages';
UIManager.splashScreen.classList.remove('show');
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
document.l10n = realL10n;
realL10n = null;
window.tzSelect = realTzSelect;
realTzSelect = null;
navigator.mozApps = realMozApps;
realMozApps = null;
window.FxAccountsIACHelper = realFxAccountsIACHelper;
realFxAccountsIACHelper = null;
navigator.mozSettings = realSettings;
window.SettingsListener = realSettingsListener;
});
suite('Date & Time >', function() {
suiteSetup(function() {
Navigation.currentStep = 4;
Navigation.manageStep();
});
suiteTeardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
});
suite('change Time Zone >', function() {
var timezoneTitle,
timezoneOverlay,
regionLabel,
cityLabel,
timeLabel,
FAKE_TIMEZONE;
var localizeSpy;
setup(function() {
FAKE_TIMEZONE = {
utcOffset: '+0:00',
region: 'fakeRegion',
city: 'fakeCity'
};
timezoneTitle = document.getElementById('time-zone-title');
timezoneOverlay = document.getElementById('time_zone_overlay');
regionLabel = document.getElementById('tz-region-label');
cityLabel = document.getElementById('tz-city-label');
timeLabel = document.getElementById('time-configuration-label');
localizeSpy = this.sinon.spy(document.l10n, 'setAttributes');
UIManager.setTimeZone(FAKE_TIMEZONE);
});
test('should set/clear timeZoneNeedsConfirmation flag', function() {
UIManager.setTimeZone(FAKE_TIMEZONE, true);
assert.isTrue(UIManager.timeZoneNeedsConfirmation);
UIManager.setTimeZone(FAKE_TIMEZONE, false);
assert.isFalse(UIManager.timeZoneNeedsConfirmation);
});
test('should localize title', function() {
assert.isTrue(localizeSpy.calledOnce);
assert.isTrue(localizeSpy.calledWith(timezoneTitle,
'timezoneTitle',
FAKE_TIMEZONE));
});
test('should update region', function() {
assert.equal(regionLabel.textContent, FAKE_TIMEZONE.region);
});
test('should highlight timezone in map', function() {
assert.equal(timezoneOverlay.className,
'UTC' + FAKE_TIMEZONE.utcOffset.replace(/[+:]/g, ''));
});
test('should update city', function() {
assert.equal(cityLabel.textContent, FAKE_TIMEZONE.city);
});
test('should format the time', function() {
assert.equal(timeLabel.textContent, (new Date()).toLocaleString(
navigator.languages,
{
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
));
});
});
});
suite('Geolocation section', function() {
setup(function() {
Navigation.currentStep = 5;
Navigation.manageStep();
});
suiteTeardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
});
test('initial value', function() {
assert.isTrue(MockNavigatorSettings.mSettings['geolocation.enabled']);
// we set initial value at suite startup
assert.isTrue(UIManager.geolocationSwitch.checked);
});
test('setting observer updates checked value', function() {
MockSettingsListener.mTriggerCallback('geolocation.enabled', false);
assert.isFalse(UIManager.geolocationSwitch.checked);
});
});
suite('Firefox Accounts section', function() {
var localizeSpy;
var nextButton;
var createAccountButton;
suiteSetup(function() {
Navigation.currentStep = 7;
Navigation.manageStep();
});
suiteTeardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
});
setup(function() {
localizeSpy = this.sinon.spy(document.l10n, 'setAttributes');
nextButton = document.getElementById('forward');
createAccountButton = document.getElementById('fxa-create-account');
});
teardown(function() {
document.l10n.setAttributes.restore();
});
suite('Verified Firefox Account login', function() {
setup(function() {
MockFxAccountsIACHelper.account = {
email: 'foo@bar.com',
verified: true
};
nextButton.setAttribute('data-l10n-id', 'skip');
UIManager.onFxAFlowDone();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should show correct success message', function() {
assert.isTrue(localizeSpy.calledOnce);
assert.equal('fxa-signed-in', localizeSpy.args[0][1]);
});
test('Should set correct label on button', function() {
var dataL10n = nextButton.getAttribute('data-l10n-id');
assert.equal(dataL10n, 'navbar-next');
});
test('Should disable create account button', function() {
assert.isTrue(createAccountButton.disabled);
});
});
suite('Unverified Firefox Account login', function() {
setup(function() {
MockFxAccountsIACHelper.account= {
email: 'foo@bar.com',
verified: false
};
nextButton.setAttribute('data-l10n-id', 'skip');
UIManager.onFxAFlowDone();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should show correct success message', function() {
assert.isTrue(localizeSpy.calledOnce);
assert.equal('fxa-email-sent', localizeSpy.args[0][1]);
});
test('Should set correct label on button', function() {
var dataL10n = nextButton.getAttribute('data-l10n-id');
assert.equal(dataL10n, 'navbar-next');
});
test('Should disable create account button', function() {
assert.isTrue(createAccountButton.disabled);
});
});
suite('Account login - getAccount no account', function() {
setup(function() {
createAccountButton.disabled = false;
nextButton.setAttribute('data-l10n-id', 'skip');
UIManager.onFxAFlowDone();
});
test('Should not show any success message', function() {
assert.isFalse(localizeSpy.calledOnce);
});
test('Button label should still be skip', function() {
var dataL10n = nextButton.getAttribute('data-l10n-id');
assert.equal(dataL10n, 'skip');
});
test('Should not disable create account button', function() {
assert.isFalse(createAccountButton.disabled);
});
});
suite('Account login - getAccount error', function() {
setup(function() {
MockFxAccountsIACHelper.getAccountError = 'WHATEVER';
createAccountButton.disabled = false;
nextButton.setAttribute('data-l10n-id', 'skip');
UIManager.onFxAFlowDone();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Button label should still be skip', function() {
var dataL10n = nextButton.getAttribute('data-l10n-id');
assert.equal(dataL10n, 'skip');
});
test('Should not disable create account button', function() {
assert.isFalse(createAccountButton.disabled);
});
});
suite('FTU initiates with a existing FxA login - happy path', function() {
setup(function() {
MockFxAccountsIACHelper.account = {
email: 'foo@bar.com',
verified: true
};
UIManager.skipFxA = false;
UIManager.checkInitialFxAStatus();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should not skip FxA', function() {
assert.isFalse(UIManager.skipFxA);
});
});
suite('FTU initiates with a existing FxA login - getAccount does not ' +
'give any results (or maybe it does but not in time)', function() {
setup(function() {
MockFxAccountsIACHelper.getAccountNoCallback = true;
UIManager.skipFxA = false;
UIManager.checkInitialFxAStatus();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should skip FxA', function() {
assert.isTrue(UIManager.skipFxA);
});
});
suite('FTU initiates with a existing FxA login - getAccount error - ' +
'logout ok', function() {
var logoutSpy;
setup(function() {
MockFxAccountsIACHelper.getAccountError = 'WHATEVER';
UIManager.skipFxA = false;
logoutSpy = this.sinon.spy(MockFxAccountsIACHelper, 'logout');
UIManager.checkInitialFxAStatus();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should logout', function() {
assert.isTrue(logoutSpy.calledOnce);
});
test('And not skip FxA', function() {
assert.isFalse(UIManager.skipFxA);
});
});
suite('FTU initiates with a existing FxA login - getAccount error - ' +
'logout no result or error', function() {
var logoutSpy;
setup(function() {
MockFxAccountsIACHelper.getAccountError = 'WHATEVER';
MockFxAccountsIACHelper.logoutNoCallback = true;
UIManager.skipFxA = false;
logoutSpy = this.sinon.spy(MockFxAccountsIACHelper, 'logout');
UIManager.checkInitialFxAStatus();
});
teardown(function() {
MockFxAccountsIACHelper.reset();
});
test('Should try to logout', function() {
assert.isTrue(logoutSpy.calledOnce);
});
test('But still skip FxA', function() {
assert.isTrue(UIManager.skipFxA);
});
});
});
suite('Browser Privacy section', function() {
var page,
input;
suiteSetup(function() {
page = document.getElementById('browser_privacy');
input = document.getElementById('newsletter-input');
Navigation.currentStep = 8;
Navigation.manageStep();
});
suiteTeardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
});
setup(function() {
sinon.spy(UIManager, 'scrollToElement');
});
teardown(function() {
UIManager.scrollToElement.restore();
});
test('Email input is scrolled when focused > ', function(done) {
var focusEvt = new CustomEvent('focus');
var resizeEvt = new CustomEvent('resize');
input.dispatchEvent(focusEvt);
window.dispatchEvent(resizeEvt);
setTimeout(function() {
assert.isTrue(UIManager.scrollToElement.calledOnce);
assert.isTrue(UIManager.scrollToElement.calledWith(page, input));
done();
}, 100); // there's a timeout on the code
});
});
suite('Change app theme', function() {
var meta;
suiteSetup(function() {
meta = document.createElement('meta');
meta.content = 'red';
meta.name = 'theme-color';
document.head.appendChild(meta);
});
teardown(function() {
document.head.removeChild(meta);
});
test('Should change the theme color of the app', function() {
UIManager.changeStatusBarColor('black');
assert.equal(meta.getAttribute('content'), 'black');
});
});
suite('Wifi section', function() {
setup(function() {
UIManager.init();
Navigation.currentStep = 3;
Navigation.manageStep();
this.sinon.spy(WifiManager, 'scan');
this.sinon.spy(WifiUI, 'joinNetwork');
this.sinon.spy(WifiUI, 'addHiddenNetwork');
this.sinon.spy(WifiUI, 'joinHiddenNetwork');
});
teardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
});
test('Refresh networks >', function() {
UIManager.wifiRefreshButton.click();
assert.isTrue(WifiManager.scan.calledWith(WifiUI.renderNetworks),
'should call for a scan of the networks');
});
test('Add hidden network >', function() {
UIManager.joinHiddenButton.click();
assert.isTrue(WifiUI.addHiddenNetwork.calledOnce,
'addHiddenNetwork should be called');
});
test('Join hidden network > ', function() {
// simulate we are on Add Hidden Wifi screen
window.location.hash = '#hidden-wifi-authentication';
UIManager.wifiJoinButton.disabled = false;
UIManager.wifiJoinButton.click();
assert.ok(WifiUI.joinHiddenNetwork.calledOnce,
'joinHiddenNetwork should be called');
});
test('Join hidden network > ', function() {
UIManager.wifiJoinButton.disabled = false;
UIManager.wifiJoinButton.click();
assert.ok(WifiUI.joinNetwork.calledOnce,
'joinNetwork should be called');
});
});
suite('Metrics Section', function() {
var MockSettings;
setup(function() {
MockNavigatorSettings.mSetup();
MockSettings = MockNavigatorSettings.mSettings;
UIManager.init();
Navigation.currentStepIndex = 7;
Navigation.manageStep();
// Default settings
MockSettings['hud.hide'] = false;
MockSettings['devtools.overlay'] = false;
});
teardown(function() {
Navigation.currentStep = 1;
Navigation.manageStep();
MockNavigatorSettings.mTeardown();
});
test('should set the HUD settings correctly for Enhanced >', function() {
var fakeEvent = {
target: {
id: 'metrics-enhanced',
value: 'Enhanced'
}
};
UIManager.handleEvent(fakeEvent);
assert.equal(MockSettings['hud.hide'], true);
assert.equal(MockSettings['devtools.overlay'], true);
assert.equal(MockSettings['metrics.selectedMetrics.level'], 'Enhanced');
});
test('should not modify HUD settings from default for Basic >', function() {
var fakeEvent = {
target: {
id: 'metrics-basic',
value: 'Basic'
}
};
UIManager.handleEvent(fakeEvent);
assert.equal(MockSettings['hud.hide'], false);
assert.equal(MockSettings['devtools.overlay'], false);
assert.equal(MockSettings['metrics.selectedMetrics.level'], 'Basic');
});
test('should not modify the HUD settings from default for None >',
function() {
var fakeEvent = {
target: {
id: 'metrics-none',
value: 'None'
}
};
UIManager.handleEvent(fakeEvent);
assert.equal(MockSettings['hud.hide'], false);
assert.equal(MockSettings['devtools.overlay'], false);
assert.equal(MockSettings['metrics.selectedMetrics.level'], 'None');
});
});
});

View file

@ -0,0 +1,657 @@
/* global MockL10n, MockMozWifiNetwork, MockNavigatorMozWifiManager,
MockFxAccountsIACHelper, MocksHelper, UIManager, utils,
WifiHelper, WifiManager, WifiUI */
'use strict';
require('/shared/test/unit/mocks/mock_l20n.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_wifi_manager.js');
requireApp('ftu/test/unit/mock_utils.js');
requireApp('ftu/test/unit/mock_moz_wifi_network.js');
requireApp('ftu/test/unit/mock_fx_accounts_iac_helper.js');
requireApp('ftu/js/wifi.js');
requireApp('ftu/js/ui.js');
require('/shared/js/wifi_helper.js');
require('/shared/test/unit/mocks/mock_settings_listener.js');
var mocksHelperForWifi = new MocksHelper([
'utils',
'MozWifiNetwork',
'SettingsListener'
]).init();
suite('wifi > ', function() {
var realL10n,
realMozWifiNetwork,
realMozWifiManager,
realFxAccountsIACHelper;
var fakeNetworks = [
{
ssid: 'Mozilla Guest',
bssid: 'xx:xx:xx:xx:xx:xx',
security: [],
capabilities: [],
relSignalStrength: 98,
connected: false
},
{
ssid: 'Livebox 6752',
bssid: 'xx:xx:xx:xx:xx:xx',
security: ['WEP'],
capabilities: [],
relSignalStrength: 89,
connected: false
},
{
ssid: 'Mozilla-G',
bssid: 'xx:xx:xx:xx:xx:xx',
security: ['WPA-EAP'],
capabilities: [],
relSignalStrength: 67,
connected: false
},
{
ssid: 'Freebox 8953',
bssid: 'xx:xx:xx:xx:xx:xx',
security: ['WPA2-PSK'],
capabilities: [],
relSignalStrength: 32,
connected: false
}
];
mocksHelperForWifi.attachTestHelpers();
suiteSetup(function() {
realL10n = document.l10n;
document.l10n = MockL10n;
realMozWifiNetwork = window.mozWifiNetwork;
window.mozWifiNetwork = MockMozWifiNetwork;
realMozWifiManager = navigator.mozWifiManager;
navigator.mozWifiManager = MockNavigatorMozWifiManager;
realFxAccountsIACHelper = window.FxAccountsIACHelper;
window.FxAccountsIACHelper = MockFxAccountsIACHelper;
loadBodyHTML('/index.html');
});
setup(function() {
WifiManager.init();
});
suiteTeardown(function() {
document.l10n = realL10n;
realL10n = null;
window.mozWifiNetwork = realMozWifiNetwork;
realMozWifiNetwork = null;
navigator.mozWifiManager = realMozWifiManager;
realMozWifiManager = null;
});
suite('Scan networks >', function() {
var clock = sinon.useFakeTimers();
setup(function() {
this.sinon.spy(utils.overlay, 'show');
});
teardown(function() {
clock.restore();
});
test('none available', function(done) {
var noNetworks = [];
MockNavigatorMozWifiManager.setNetworks(noNetworks);
WifiManager.scan(function(networks) {
assert.ok(utils.overlay.show.calledOnce, 'shows loading overlay');
assert.equal(networks.length, noNetworks.length,
'return zero networks');
assert.isDefined(document.getElementById('no-result-container'),
'show no networks message');
done();
});
});
test('some available', function(done) {
MockNavigatorMozWifiManager.setNetworks(fakeNetworks);
WifiManager.scan(function(networks) {
assert.ok(utils.overlay.show.calledOnce, 'shows loading overlay');
assert.isDefined(networks, 'return networks');
assert.isNotNull(networks, 'return valid networks');
assert.equal(networks, fakeNetworks, 'return existing networks');
done();
});
});
test('error while scanning', function(done) {
this.sinon.spy(console, 'error');
this.sinon.stub(MockNavigatorMozWifiManager, 'getNetworks')
.onFirstCall().returns({
set onerror(callback) {
this.error = {
name: 'error'
};
callback();
}
});
WifiManager.scan(function(networks) {
assert.ok(MockNavigatorMozWifiManager.getNetworks.calledOnce);
assert.ok(utils.overlay.show.called, 'shows loading overlay');
assert.equal(networks.length, 0, 'no networks returned');
assert.ok(console.error.calledOnce);
done();
});
});
test('should execute callback when scan fails', function() {
var fakeCallback = this.sinon.spy();
this.sinon.stub(MockNavigatorMozWifiManager, 'getNetworks')
.onFirstCall().returns({
set onerror(callback) {
this.error = {
name: 'error'
};
callback();
}
});
WifiManager.scan(fakeCallback);
assert.isTrue(fakeCallback.calledOnce);
});
test('timeout error', function(done) {
this.sinon.stub(MockNavigatorMozWifiManager, 'getNetworks').returns({});
this.sinon.spy(console, 'warn');
WifiManager.scan(function(networks) {
assert.ok(utils.overlay.show.called, 'shows loading overlay');
assert.isUndefined(networks, 'no networks returned');
assert.ok(console.warn.called);
done();
});
clock.tick(10000);
});
});
suite('Choose a network >', function() {
var network,
networkDOM,
fakeEvent;
setup(function() {
WifiManager.networks = fakeNetworks;
WifiUI.renderNetworks(fakeNetworks);
UIManager.activationScreen =
document.getElementById('activation-screen');
UIManager.mainTitle = document.getElementById('main-title');
UIManager.wifiJoinButton = document.getElementById('wifi-join-button');
UIManager.navBar = document.getElementById('nav-bar');
UIManager.passwordInput = document.getElementById('wifi_password');
network = fakeNetworks[0];
networkDOM = document.querySelector('li[data-ssid="' +
network.ssid + '"]');
fakeEvent = {
target: networkDOM
};
});
teardown(function() {
UIManager.activationScreen = null;
UIManager.mainTitle = null;
UIManager.wifiJoinButton = null;
UIManager.navBar = null;
UIManager.passwordInput = null;
});
test('Open network', function() {
this.sinon.stub(WifiHelper, 'isOpen').returns(true);
this.sinon.stub(WifiUI, 'connect');
WifiUI.chooseNetwork(fakeEvent);
assert.isTrue(WifiUI.connect.called, 'should try to connect directly');
});
test('Protected network', function() {
this.sinon.stub(WifiHelper, 'isOpen').returns(false);
WifiUI.chooseNetwork(fakeEvent);
assert.equal(UIManager.mainTitle.textContent, networkDOM.dataset.ssid,
'should show the network name as title');
assert.isTrue(UIManager.activationScreen.classList.contains('no-options'),
'should hide refresh button');
assert.isTrue(UIManager.navBar.classList.contains('secondary-menu'),
'should change different nav button');
});
test('Join button valid input', function() {
this.sinon.stub(WifiHelper, 'isValidInput').returns(true);
// default value. Must change after input event
UIManager.wifiJoinButton.disabled = true;
UIManager.passwordInput.dispatchEvent(new CustomEvent('input'));
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button should be enabled if the input is valid');
});
test('Join button invalid input', function() {
this.sinon.stub(WifiHelper, 'isValidInput').returns(false);
// button enabled. Must change after input event
UIManager.wifiJoinButton.disabled = false;
UIManager.passwordInput.dispatchEvent(new CustomEvent('input'));
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button should be disabled if the input is not valid');
});
});
suite('Add a hidden network >', function() {
setup(function() {
UIManager.init();
UIManager.activationScreen =
document.getElementById('activation-screen');
UIManager.mainTitle = document.getElementById('main-title');
UIManager.wifiJoinButton = document.getElementById('wifi-join-button');
UIManager.navBar = document.getElementById('nav-bar');
UIManager.hiddenWifiSsid = document.getElementById('hidden-wifi-ssid');
UIManager.hiddenWifiSecurity =
document.getElementById('hidden-wifi-security');
UIManager.hiddenWifiPasswordBox =
document.getElementById('hidden-wifi-password-box');
UIManager.hiddenWifiPassword =
document.getElementById('hidden-wifi-password');
UIManager.hiddenWifiIdentityBox =
document.getElementById('hidden-wifi-identity-box');
UIManager.hiddenWifiIdentity =
document.getElementById('hidden-wifi-identity');
WifiUI.addHiddenNetwork();
});
test('should show authentication screen', function() {
assert.isTrue(UIManager.activationScreen.classList.contains('no-options'),
'should hide refresh button');
assert.equal(UIManager.mainTitle.getAttribute('data-l10n-id'),
'authentication',
'should show correct title');
assert.isTrue(UIManager.navBar.classList.contains('secondary-menu'),
'should change different nav button');
assert.equal(UIManager.hiddenWifiSsid.value, '',
'should clean SSID input');
assert.equal(UIManager.hiddenWifiPassword.value, '',
'should clean Password input');
assert.equal(UIManager.hiddenWifiIdentity.value, '',
'should clean User input');
assert.isTrue(UIManager.wifiJoinButton.disabled,
'should disable Join button by default');
assert.equal(window.location.hash, '#hidden-wifi-authentication',
'should update the hash value');
});
test('add open network', function() {
// Check everything is ok when entering
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button should be disabled by default');
// Choose an Open security and simulate 'change' event
UIManager.hiddenWifiSecurity.options[0].selected = true;
UIManager.hiddenWifiSecurity.dispatchEvent(new CustomEvent('change'));
// Check UI changes for the security chosen
assert.isTrue(UIManager.hiddenWifiPasswordBox.classList
.contains('hidden'),
'should hide all security inputs');
// Prepare the conditions
UIManager.hiddenWifiSsid.value = 'testSSID';
// Simulate event for the check
UIManager.hiddenWifiSsid.dispatchEvent(new CustomEvent('keyup'));
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled when SSID not empty');
});
test('add protected network WEP', function() {
// Check everything is ok when entering
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button should be disabled by default');
// Choose WEP security and simulate 'change' event
UIManager.hiddenWifiSecurity.options[1].selected = true;
UIManager.hiddenWifiSecurity.dispatchEvent(new CustomEvent('change'));
// Check UI changes for the security chosen
assert.isFalse(UIManager.hiddenWifiPasswordBox.classList
.contains('hidden'),
'should show password input');
assert.isTrue(UIManager.hiddenWifiIdentityBox.classList
.contains('hidden'),
'should hide user input');
UIManager.hiddenWifiSsid.value = 'testSSID';
UIManager.hiddenWifiSsid.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled when SSID not empty');
UIManager.hiddenWifiPassword.value = 'I234'; // 4 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled when Password is not long enough');
UIManager.hiddenWifiPassword.value = 'I234S'; // 5 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
UIManager.hiddenWifiPassword.value = 'I234S67B90'; // 10 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled when not HEX 10 char password');
UIManager.hiddenWifiPassword.value = '123A567b90'; // 10 HEX char
// (0-9 A-F a-f)
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
UIManager.hiddenWifiPassword.value = '1234567890QW'; // 12 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is enabled');
UIManager.hiddenWifiPassword.value = '123A567b90QWE'; // 13 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
UIManager.hiddenWifiPassword.value = '123A567b90QWERTY'; // 16 ASCII char
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
// 29 ASCII char
UIManager.hiddenWifiPassword.value = '123A567b90QWERTYUIOPASDFGHJKL';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
// 32 ASCII char
UIManager.hiddenWifiPassword.value = '123A567b90QWERTYUIOPASDFGHJKLZXC';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled with 32 not HEX chars');
// 32 HEX char
UIManager.hiddenWifiPassword.value = '123A567b90ABCDEFabcdef1234567890';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
// 58 ASCII char
UIManager.hiddenWifiPassword.value = '123A567b90' +
'QWERTYUIOPASDFGHJKLZXCVBNM' +
'qwertyuiopasdfghjkl';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled with 58 not HEX chars');
// 58 HEX char
UIManager.hiddenWifiPassword.value = '123A567b90ABCDEFabcdef1234567890' +
'1234567890ABCDEFabcdef1234';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// All conditions valid
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled');
// 59 HEX char
UIManager.hiddenWifiPassword.value = '123A567b90ABCDEFabcdef1234567890' +
'1234567890ABCDEFabcdef12345';
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled for more than 58 characters');
});
test('add protected network WPA-PSK', function() {
// Check everything is ok when entering
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button should be disabled by default');
// Choose WPA-PSK security and simulate 'change' event
UIManager.hiddenWifiSecurity.options[2].selected = true;
UIManager.hiddenWifiSecurity.dispatchEvent(new CustomEvent('change'));
// Check UI changes for the security chosen
assert.isFalse(UIManager.hiddenWifiPasswordBox.classList
.contains('hidden'),
'should show password input');
assert.isTrue(UIManager.hiddenWifiIdentityBox.classList
.contains('hidden'),
'should hide user input');
UIManager.hiddenWifiSsid.value = 'testSSID';
UIManager.hiddenWifiSsid.dispatchEvent(new CustomEvent('keyup'));
// Not enough
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is enabled when SSID not empty');
UIManager.hiddenWifiPassword.value = 'QW3rtY7'; // 7 char password
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled when Password is not long enough');
UIManager.hiddenWifiPassword.value = 'QW3rtY78'; // 8 char password
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled when Password is at least 8 char long');
UIManager.hiddenWifiPassword.value = 'QW3rtY789'; // 8 char password
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with this condition
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled when Password is longer than 8 char');
});
test('add protected network WPA-EAP', function() {
// Check everything is ok when entering
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button should be disabled by default');
// Choose an Open security and simulate 'change' event
UIManager.hiddenWifiSecurity.options[3].selected = true;
UIManager.hiddenWifiSecurity.dispatchEvent(new CustomEvent('change'));
// Check UI changes for the security chosen
assert.isFalse(UIManager.hiddenWifiPasswordBox.classList
.contains('hidden'),
'should show password input');
assert.isFalse(UIManager.hiddenWifiIdentityBox.classList
.contains('hidden'),
'should show user input');
UIManager.hiddenWifiSsid.value = 'testSSID'; // any SSID
UIManager.hiddenWifiSsid.dispatchEvent(new CustomEvent('keyup'));
// Not enough
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is enabled when SSID not empty');
UIManager.hiddenWifiPassword.value = 'QW3rtY'; // any password
UIManager.hiddenWifiPassword.dispatchEvent(new CustomEvent('keyup'));
// Not enough with ssid and password filled
assert.isTrue(UIManager.wifiJoinButton.disabled,
'button is disabled when Identity not filled');
UIManager.hiddenWifiIdentity.value = 'user'; // any user
UIManager.hiddenWifiIdentity.dispatchEvent(new CustomEvent('keyup'));
// SSID + User + Password
assert.isFalse(UIManager.wifiJoinButton.disabled,
'button is enabled when all conditions ok');
});
});
suite('Join a network >', function() {
var testSSID,
testUser,
testPassword;
setup(function() {
testSSID = 'Mozilla-G';
testUser = 'testUser';
testPassword = 'testPassword';
this.sinon.stub(window.history, 'back');
this.sinon.stub(WifiManager, 'connect');
WifiUI.renderNetworks(fakeNetworks);
UIManager.hiddenWifiSsid =
document.getElementById('hidden-wifi-ssid');
UIManager.hiddenWifiIdentity =
document.getElementById('hidden-wifi-identity');
UIManager.hiddenWifiPassword =
document.getElementById('hidden-wifi-password');
UIManager.hiddenWifiSecurity =
document.getElementById('hidden-wifi-security');
});
teardown(function() {
// document.body.innerHTML = '';
UIManager.hiddenWifiSsid = null;
UIManager.hiddenWifiIdentity = null;
UIManager.hiddenWifiPassword = null;
UIManager.hiddenWifiSecurity = null;
});
test('from the list of detected networks', function() {
document.getElementById('wifi_ssid').value = testSSID;
document.getElementById('wifi_user').value = testUser;
document.getElementById('wifi_password').value = testPassword;
WifiUI.joinNetwork();
assert.isTrue(WifiManager.connect.calledWith(testSSID,
testPassword,
testUser),
'should try to connect with correct values');
assert.isTrue(window.history.back.called,
'UI should go back to the list');
});
test('that is hidden', function() {
UIManager.hiddenWifiSsid.value = testSSID;
UIManager.hiddenWifiIdentity.value = testUser;
UIManager.hiddenWifiPassword.value = testPassword;
UIManager.hiddenWifiSecurity.options[2].selected = true;
var oldNetworks = WifiManager.networks.length;
WifiUI.joinHiddenNetwork();
var currentNetworks = WifiManager.networks.length;
assert.isTrue(currentNetworks > oldNetworks,
'a new network is added to the list');
var hiddenNetwork = document.getElementById(testSSID);
assert.isNotNull(hiddenNetwork, 'hidden network should be rendered');
assert.isTrue(WifiManager.connect.calledWith(testSSID,
testPassword,
testUser),
'should try to connect with correct values');
assert.isTrue(window.history.back.called,
'UI should go back to the list');
});
suite('Update UI', function() {
var realWifiManagerOnScan;
setup(function() {
WifiUI.renderNetworks(fakeNetworks);
realWifiManagerOnScan = WifiManager.onScan;
WifiManager.onScan = null;
});
teardown(function() {
WifiManager.onScan = realWifiManagerOnScan;
});
test('Should update the previous connected network', function() {
var previousNetwork = fakeNetworks[0];
var currentNetwork = fakeNetworks[1];
var previousNetworkElement =
document.querySelector('li[data-ssid="' +
previousNetwork.ssid + '"]');
var previousNetworkSecurity =
previousNetworkElement.querySelector('p[data-security-level]');
var currentNetworkElement =
document.querySelector('li[data-ssid="' +
currentNetwork.ssid + '"]');
var currentNetworkSecurity =
currentNetworkElement.querySelector('p[data-security-level]');
WifiManager.api.onstatuschange({status: 'connected',
network: previousNetwork});
assert.isTrue(previousNetworkElement.classList.contains('connected'));
assert.equal(previousNetworkSecurity.dataset.l10nId,
'shortStatus-connected');
WifiManager.api.onstatuschange({status: 'disconnected',
network: previousNetwork});
// bug 1179703
WifiManager.api.onstatuschange({status: 'connecting',
network: previousNetwork});
WifiManager.api.onstatuschange({status: 'connected',
network: currentNetwork});
assert.isFalse(previousNetworkElement.classList.contains('connected'));
assert.isTrue(currentNetworkElement.classList.contains('connected'));
assert.equal(previousNetworkSecurity.dataset.l10nId,
'securityOpen');
assert.equal(currentNetworkSecurity.dataset.l10nId,
'shortStatus-connected');
});
test('Should move the current connected network to the top', function() {
var currentNetwork = fakeNetworks[2];
var currentNetworkElement =
document.querySelector('li[data-ssid="' +
currentNetwork.ssid + '"]');
var networksList = document.getElementById('networks-list');
WifiManager.api.onstatuschange({status: 'connected',
network: currentNetwork});
var firstChild = networksList.children[0];
assert.equal(currentNetworkElement, firstChild);
});
});
});
});