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

View file

@ -0,0 +1,7 @@
<gaia-header action="back" data-href="#root">
<h1 data-l10n-id="achievements"></h1>
</gaia-header>
<div>
<ul class="achievements-list"></ul>
</div>

View file

@ -0,0 +1,14 @@
<gaia-header action="back" data-href="#root">
<h1 data-l10n-id="addons-header"></h1>
</gaia-header>
<div>
<ul>
<li>
<a class="menu-item get-more-addons" href="#">
<span></span>
</a>
</li>
</ul>
<ul class="addon-list"></ul>
</div>

View file

@ -0,0 +1,22 @@
<div>
<ul>
<li>
<progress class="apps-space-bar" min="0" value="0" max="100"></progress>
</li>
<li>
<a> Total Space
<span class="apps-total-space"></span>
</a>
</li>
<li>
<a> Used
<span class="apps-used-space"></span>
</a>
</li>
<li>
<a> Left
<span class="apps-free-space"></span>
</a>
</li>
</ul>
</div>

View file

@ -0,0 +1,21 @@
<gaia-header action="back" data-href="#root">
<h1 data-l10n-id="browsingPrivacy-header"></h1>
</gaia-header>
<div>
<ul>
<li>
<button class="clear-history-button"
data-l10n-id="clear-browsing-history">
Clear browsing history
</button>
</li>
<li>
<button class="clear-private-data-button"
data-l10n-id="clear-cookies-and-stored-data">
Clear cookies and stored data
</button>
</li>
</ul>
</div>

View file

@ -0,0 +1,56 @@
<gaia-header action="back" data-href="#root">
<h1 data-l10n-id="callBarring-header"></h1>
</gaia-header>
<div>
<header id="header-cb-outgoing">
<h2 data-l10n-id="callBarring-outgoing"> Outgoing calls </h2>
</header>
<ul id="list-callBarring-outgoing">
<li id="li-cb-baoc" data-state="off">
<gaia-switch>
<label data-l10n-id="callBarring-baoc">All</label>
<details id="baoc-desc"></details>
</label>
</li>
<li id="li-cb-boic" data-state="off">
<gaia-switch>
<label data-l10n-id="callBarring-boic">International</label>
<details id="boic-desc"></details>
</label>
</li>
<li id="li-cb-boicExhc" data-state="off">
<gaia-switch>
<label data-l10n-id="callBarring-boicExhc">International except Home Country</label>
<details id="boicExhc-desc"></details>
</label>
</li>
</ul>
<header id="header-cb-incoming">
<h2 data-l10n-id="callBarring-incoming"> Incoming calls </h2>
</header>
<ul id="list-callBarring-incoming">
<li id="li-cb-baic" data-state="off">
<gaia-switch>
<label data-l10n-id="callBarring-baic">All</label>
<details id="baic-desc"></details>
</label>
</li>
<li id="li-cb-baicR" data-state="off">
<gaia-switch>
<label data-l10n-id="callBarring-baicR">All while roaming</label>
<details id="bairR-desc"></details>
</label>
</li>
</ul>
<header id="header-cb-passcode">
<h2 data-l10n-id="callBarring-passcode"> Passcode </h2>
</header>
<ul id="list-callBarring-passcode">
<li id="li-cb-pswd">
<a class="bb-button" data-href="#call-barring-passcode-change" data-l10n-id="callBarring-change-passcode">Change passcode</a>
</li>
</ul>
</div>

View file

@ -0,0 +1,22 @@
<gaia-menu hidden class="call-fdnList-action">
<button class="fdnContact"></button>
<button class="fdnAction-call"></button>
<button class="fdnAction-edit"></button>
</gaia-menu>
<button class="fdnAction-delete"></button>
<li class="fdn-enabled">
<gaia-switch>
<label></label>
<details></details>
</gaia-switch>
</li>
<li class="fdn-resetPIN2">
<label>
<button type="checkbox"></button>
</label>
</li>
<button class="fdnContact-submit"></button>
<h1 class="fdnContact-title"></h1>
<input class="fdnContact-name"/>
<input class="fdnContact-number"/>

View file

@ -0,0 +1,51 @@
<div>
<ul class="time-auto"></ul>
<ul class="time-manual">
<li>
<label>
<input type="date" class="date-picker" min="1970-1-1" max="2037-12-31"/>
</label>
<span class="clock-date"></span>
</li>
<li>
<label>
<input type="time" class="time-picker" />
</label>
<span class="clock-time"></span>
</li>
</ul>
<ul class="timezone">
<li class="timezone-picker">
<span class="button icon icon-dialog">
<select class="timezone-region"></select>
</span>
</li>
<li class="timezone-picker">
<span class="button icon icon-dialog">
<select class="timezone-city"></select>
</span>
</li>
<li class="timezone-info" hidden>
<span class="timezone-info-text"></span>
</li>
</ul>
<header>
<h2 data-l10n-id="time-format"></h2>
</header>
<ul class="time-format-auto" data-state="auto">
<li>
<gaia-switch checked>
<label data-l10n-id="useDefaultTimeFormat"></label>
</gaia-switch>
</li>
</ul>
<ul class="timeformat disabled">
<li>
<p data-l10n-id="time-format-time"></p>
<span class="button icon icon-dialog">
<select class="time-format-time"></select>
</span>
</li>
</ul>
</div>

View file

@ -0,0 +1,31 @@
<div>
<ul>
<li class="software-home-button">
<gaia-checkbox name="software-button.enabled">
<label data-l10n-id="software-home-button"></label>
</gaia-checkbox>
</li>
<li class="homegesture">
<gaia-checkbox name="homegesture.enabled">
<label data-l10n-id="home-gesture"></label>
</gaia-checkbox>
</li>
<li>
<gaia-checkbox class="homescreen-paging">
<label data-l10n-id="homescreen-paging"></label>
</gaia-checkbox>
</li>
</ul>
<ul>
<li>
<button class="ftuLauncher" data-l10n-id="launch-ftu"></button>
</li>
</ul>
<ul>
<li>
<gaia-switch name="devtools.unrestricted" class="reset-devtools">
<label data-l10n-id="full-devtools"></label>
</gaia-switch>
</li>
</ul>
</div>

View file

@ -0,0 +1,6 @@
<div>
<ul>
<li class="brightness-auto"></li>
<li class="brightness-manual"></li>
</ul>
</div>

View file

@ -0,0 +1,91 @@
<gaia-header action="back" data-href="#root">
<h1 data-l10n-id="fxsync"></h1>
</gaia-header>
<div class="fxsync-logged-out" hidden>
<ul role="presentation">
<li role="presentation" class="description">
<div class="fxsync-intro"></div>
<p data-l10n-id="fxsync-overview" id="fxsync-logged-out-description"></p>
</li>
<li role="presentation">
<button class="recommend fxsync-login">
<span data-l10n-id="fxsync-get-started"></span>
</button>
</li>
</ul>
</div>
<div class="fxsync-logged-in" hidden>
<ul role="presentation">
<li role="presentation">
<p class="description" data-l10n-id="fxsync-sync-user"></p>
<p class="explanation">
<span class="fxsync-user"></span>
</p>
</li>
<li role="presentation" class="fxsync-unverified description" hidden>
<p class="fxsync-warning" data-l10n-id="fxa-please-verify"></p>
</li>
<li role="presentation" class="fxsync-empty-account description" hidden>
<p class="fxsync-warning" data-l10n-id="fxsync-empty-account"></p>
</li>
<li role="presentation">
<label>
<button class="fxsync-logout">
<span data-l10n-id="fxsync-disable"></span>
</button>
</label>
</li>
<li role="presentation">
<button class="fxsync-sync-now" data-l10n-id="fxsync-sync-now"></button>
<p class="explanation fxsync-last-sync hidden">
<span data-l10n-id="fxsync-last-synced"></span>
</p>
</li>
</ul>
<header>
<h2 data-l10n-id="fxsync-collections">Collections</h2>
</header>
<ul role="presentation">
<li role="presentation" hidden>
<gaia-switch class="fxsync-collections-bookmarks"
name="sync.collections.bookmarks.enabled">
<label data-l10n-id="fxsync-collections-bookmarks"></label>
</gaia-switch>
</li>
<li role="presentation">
<gaia-switch class="fxsync-collections-history"
name="sync.collections.history.enabled">
<label data-l10n-id="fxsync-collections-history"></label>
</gaia-switch>
</li>
<li role="presentation" hidden>
<gaia-switch class="fxsync-collections-passwords"
name="sync.collections.passwords.enabled">
<label data-l10n-id="fxsync-collections-passwords"></label>
</gaia-switch>
</li>
</ul>
<header>
<h2 data-l10n-id="fxsync-legal"></h2>
</header>
<ul role="presentation">
<li role="presentation">
<label>
<button class="fxsync-tos">
<span data-l10n-id="fxsync-tos"></span>
</button>
</label>
</li>
<li role="presentation">
<label>
<button class="fxsync-privacy">
<span data-l10n-id="fxsync-privacy"></span>
</button>
</label>
</li>
</ul>
</div>

View file

@ -0,0 +1,15 @@
<div class="full-dev-mode-warning" hidden>
<gaia-confirm>
<gaia-buttons>
<button type="reset"></button>
<button type="submit"></button>
</gaia-buttons>
</gaia-confirm>
</div>
<div class="dev-menu-disabled-warning" hidden>
<gaia-confirm>
<gaia-buttons>
<button type="submit"></button>
</gaia-buttons>
</gaia-confirm>
</div>

View file

@ -0,0 +1,9 @@
<div class="warning-dialog">
<gaia-confirm>
<p class="warning-info"></p>
<gaia-buttons>
<button type="reset"></button>
<button type="submit"></button>
</gaia-buttons>
</gaia-confirm>
</div>

View file

@ -0,0 +1,9 @@
<div>
<div class="wallpaper">
<img class="wallpaper-preview"/>
</div>
<select name="grid.layout.cols"></select>
<div>
<span class="current-homescreen"></span>
</div>
</div>

View file

@ -0,0 +1,24 @@
<div>
<li>
<a id="appUsageByRegion">
</a>
</li>
<li>
<gaia-radio name="metricsGroup" id="metrics-basic" value="Basic">
<label data-l10n-id="basicMetricsRadio"></label>
<p data-l10n-id="basicMetricsExplain" class="metrics-explain"></p>
</gaia-radio>
</li>
<li>
<gaia-radio name="metricsGroup" id="metrics-enhanced" value="Enhanced">
<label data-l10n-id="enhancedMetricsRadio"></label>
<p data-l10n-id="enhancedMetricsExplain" class="metrics-explain"></p>
</gaia-radio>
</li>
<li>
<gaia-radio name="metricsGroup" id="metrics-none" value="None">
<label data-l10n-id="noneMetricsRadio"></label>
<p data-l10n-id="noneMetricsExplain" class="metrics-explain"></p>
</gaia-radio>
</li>
</div>

View file

@ -0,0 +1,19 @@
<div id="root">
<gaia-header id="main-header">
<h1 id="main-list-title"></h1>
<a id="activityDoneButton"></a>
</gaia-header>
<li id="call-settings"></li>
<li id="data-connectivity"></li>
<li id="messaging-settings"></li>
<li id="simSecurity-settings"></li>
<li id="simCardManager-settings"></li>
<li><a class="menuItem-bluetooth"</a></li>
<li class="nfc-settings" hidden></li>
<small id="fxa-desc" class="menu-item-desc"></small>
<a id="menuItem-fxa" class="menu-item" href="#fxa">Firefox Accounts</a>
<header id="icc-mainheader"></header>
<ul id="icc-entries"></ul>
<span id="menuItem-enableStorage"></span>
<li data-show-name="developer.menu.enabled" hidden></li>
</div>

View file

@ -0,0 +1,36 @@
<gaia-header action="back" id="feedback-header">
<h1 id="feedback-title"></h1>
</gaia-header>
<div>
<ul>
<li>
<textarea id="feedback-description" name="description" x-inputmode="latin-prose" dir="auto"></textarea>
</li>
<li>
<input id="email-enable" type="checkbox" />
</li>
<li id="feedback-emailbar" class="description" hidden="false">
<input id="feedback-email" type="email" />
</li>
<li>
<button id="feedback-send-btn" data-l10n-id="feedback-sendbtn"></button>
</li>
</ul>
</div>
<form role="dialog" data-type="confirm" hidden id="feedback-alert">
<section>
<p>
<strong id="feedback-alert-msg"></strong>
</p>
</section>
<menu>
<button class="full" id="feedback-alert-btn" data-l10n-id="ok">OK</button>
</menu>
</form>
<form role="dialog" data-type="confirm" hidden id="feedback-done">
<section>
</section>
<menu>
<button id="feedback-done-btn" class="full" data-l10n-id="done"></button>
</menu>
</form>

View file

@ -0,0 +1,6 @@
<div id="themes">
<div>
<ul class="theme-list">
</ul>
</div>
</div>

View file

@ -0,0 +1,31 @@
<div id="wifi-auth">
<div>
<input type="text" name="identity">
</div>
<div>
<input type="text" name="password">
</div>
<div>
<li class="auth-phase2">
<select></select>
</li>
</div>
<div>
<li class="server-certificate">
<select></select>
</li>
</div>
<li class="eap">
<select>
<option value="SIM"></option>
<option value="PEAP"></option>
<option value="TLS"></option>
<option value="TTLS"></option>
</select>
</li>
<li class="server-certificate-description"></li>
</div>
<div id="wifi">
<ul class="wifi-availableNetworks">
</ul>
</div>

View file

@ -0,0 +1,195 @@
/* global ActivityHandler */
'use strict';
require('/js/activity_handler.js');
mocha.globals(['ActivityHandler']);
suite('ActivityHandler', function() {
var originalMozSetMessageHandler;
var originalMozMobileConnections;
suiteSetup(function() {
originalMozSetMessageHandler = navigator.mozSetMessageHandler;
originalMozMobileConnections = navigator.mozMobileConnections;
});
setup(function() {
navigator.mozSetMessageHandler = function() {};
navigator.mozMobileConnections = [{}, {}];
});
suiteTeardown(function() {
navigator.mozSetMessageHandler = originalMozSetMessageHandler;
navigator.mozMobileConnections = originalMozMobileConnections;
});
teardown(function() {
ActivityHandler._readyPromise = null;
ActivityHandler._targetPanel = null;
ActivityHandler._currentActivity = null;
});
suite('ready', function() {
test('has pending messages', function(done) {
var mockSectionId = 'mockSection';
var mockActivity = {
source: {
data: {
section: mockSectionId
}
}
};
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(mockActivity);
});
this.sinon.stub(ActivityHandler, '_handleActivity', function() {
return { targetPanelId: mockSectionId };
});
this.sinon.stub(ActivityHandler, '_registerListener');
ActivityHandler.ready().then(function() {
assert.ok(navigator.mozSetMessageHandler.called,
'mozSetMessageHandler should be called');
assert.ok(ActivityHandler._handleActivity.calledWith(
mockActivity.source), '_handleActivity should be called');
assert.ok(ActivityHandler._registerListener.called,
'_registerListener should be called');
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('_handleActivity', function() {
var mockSectionId = 'mockSection';
setup(function() {
this.sinon.stub(ActivityHandler._handlers, 'configure', function() {
return { targetPanelId: mockSectionId };
});
});
test('should call to corresponding handler and set _targetPanelId ' +
'correctly', function() {
var mockActivitySource = {
name: 'configure'
};
ActivityHandler._handleActivity(mockActivitySource);
assert.equal(ActivityHandler._targetPanelId, mockSectionId);
});
});
suite('_handlers', function() {
suite('configure', function() {
var handler;
var mockSectionId = 'mockSection';
var mockOptions = { option: 'test' };
var mockSection;
setup(function() {
handler = ActivityHandler._handlers.configure;
mockSection = document.createElement('section');
mockSection.id = mockSectionId;
document.body.appendChild(mockSection);
});
teardown(function() {
document.body.innerHTML = '';
});
test('without section specified', function() {
var {targetPanelId} = handler({
data: {
section: null
}
});
assert.ok(targetPanelId === 'root', 'should return "root"');
});
test('with section other than root specified', function() {
var {targetPanelId} = handler({
data: {
section: mockSectionId
}
});
assert.ok(targetPanelId === mockSectionId,
'should return the specified section id');
assert.ok(mockSection.dataset.dialog, 'the dialog mode should be set');
});
test('with section other than root and options specified', function() {
var {targetPanelId, options} = handler({
data: {
section: mockSectionId,
options: mockOptions
}
});
assert.ok(targetPanelId === mockSectionId,
'should return the specified section id');
assert.ok(options === mockOptions,
'should return the specified options');
assert.ok(mockSection.dataset.dialog, 'the dialog mode should be set');
});
test('with root specified but without filter by', function() {
var rootSection = document.createElement('section');
rootSection.id = 'root';
document.body.appendChild(rootSection);
var {targetPanelId} = handler({
data: {
section: 'root'
}
});
assert.ok(targetPanelId === 'root', 'should "return root"');
assert.ok(document.body.dataset.filterBy === 'all',
'body.filterBy should be set');
});
test('with root specified and with filter by', function() {
var {targetPanelId} = handler({
data: {
section: 'root',
filterBy: 'connectivity'
}
});
assert.ok(targetPanelId === 'root', 'should "return root"');
assert.ok(document.body.dataset.filterBy === 'connectivity',
'body.filterBy should be set');
});
test('with invalid section id specified', function() {
var rootSection = document.createElement('section');
rootSection.id = 'root';
document.body.appendChild(rootSection);
var {targetPanelId} = handler({
data: {
section: 'invalid'
}
});
assert.ok(targetPanelId === 'root', 'should "return root"');
assert.ok(document.body.dataset.filterBy === 'all',
'body.filterBy should be set');
});
test('with valid section id but invalid section element', function() {
var invalidSection = document.createElement('div');
invalidSection.id = 'valid';
document.body.appendChild(invalidSection);
var {targetPanelId} = handler({
data: {
section: 'valid'
}
});
assert.ok(targetPanelId === 'root', 'should "return root"');
assert.ok(document.body.dataset.filterBy === 'all',
'body.filterBy should be set');
});
});
});
});

View file

@ -0,0 +1,264 @@
/* global MockNavigatorSettings, AirplaneModeHelper, Promise
*/
'use strict';
requireApp(
'settings/shared/test/unit/mocks/mock_navigator_moz_settings.js');
suite('AirplaneModeHelper > ', function() {
var realMozSettings;
var kEventName = 'statechange';
var kCommunicationKey = 'airplaneMode.enabled';
var kStatusKey = 'airplaneMode.status';
suiteSetup(function() {
realMozSettings = window.navigator.mozSettings;
window.navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
window.navigator.mozSettings = realMozSettings;
});
teardown(function() {
window.navigator.mozSettings.mTeardown();
AirplaneModeHelper._callbacks = [];
// After |mozSettings.mTeardown| is called, observers are cleared, we
// need to re-initialize the AirplaneModeHelper to add them back.
AirplaneModeHelper.init();
});
suite('init > ', function() {
setup(function(done) {
this.sinon.spy(navigator.mozSettings, 'addObserver');
require('/shared/js/airplane_mode_helper.js', done);
});
test('we added observers on ' + kStatusKey, function() {
assert.isFunction(navigator.mozSettings.mObservers[kStatusKey][0]);
});
});
suite('ready > ', function() {
var fakeCallback = sinon.spy();
suite('without _cachedStatus', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = '';
this.sinon.stub(AirplaneModeHelper, 'addEventListener');
AirplaneModeHelper.ready(fakeCallback);
});
test('we would register on eventListener', function() {
assert.ok(AirplaneModeHelper.addEventListener.called);
assert.isFalse(fakeCallback.called);
});
});
suite('with _cachedStatus', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'enabled';
AirplaneModeHelper.ready(fakeCallback);
});
test('we would execute the callback directly', function() {
assert.ok(fakeCallback.called);
});
});
suite('invoked times', function() {
function runTestSuite(withCachedStatus) {
return function() {
var readyCallback = sinon.spy();
setup(function() {
AirplaneModeHelper._cachedStatus =
withCachedStatus ? 'disabled' : '';
AirplaneModeHelper.ready(readyCallback);
});
test('we would execute the callback only once', function(done) {
function updateSettings(enabled, status) {
return new Promise(function(resolve) {
var obj = {};
obj[kCommunicationKey] = enabled;
obj[kStatusKey] = status;
var req = window.navigator.mozSettings.createLock().set(obj);
req.onsuccess = function() {
resolve();
};
});
}
// Enable airplane mode.
updateSettings(true, 'enabled').then(function() {
// Disable airplane mode.
return updateSettings(false, 'disabled');
}).then(function() {
assert.isTrue(readyCallback.calledOnce);
}).then(done, done);
});
};
}
suite('with _cachedStatus', runTestSuite(true));
suite('without _cachedStatus', runTestSuite(false));
});
});
suite('getStatus > ', function() {
var fakeStatus;
setup(function() {
fakeStatus = 'disabling';
AirplaneModeHelper._cachedStatus = fakeStatus;
});
test('getCurrentStatus returns right status', function() {
assert.equal(AirplaneModeHelper.getStatus(), fakeStatus);
});
});
suite('addEventListener > ', function() {
var fakeEventName = 'thisIsFakeEventName';
var fakeCallback = function() {};
setup(function() {
AirplaneModeHelper._callbacks = [];
});
suite('add event with wrong keyword', function() {
setup(function() {
AirplaneModeHelper.addEventListener(fakeEventName, fakeCallback);
});
test('can\'t add callbacks', function() {
assert.equal(AirplaneModeHelper._callbacks.length, 0);
});
});
suite('add event with right keyword', function() {
setup(function() {
AirplaneModeHelper.addEventListener(kEventName, fakeCallback);
});
test('can add callbacks', function() {
assert.equal(AirplaneModeHelper._callbacks.length, 1);
});
});
});
suite('removeEventListener > ', function() {
var fakeEventName = 'thisIsFakeEventName';
var fakeCallback = function() {};
setup(function() {
AirplaneModeHelper._callbacks = [fakeCallback];
});
suite('remove event with wrong keyword', function() {
setup(function() {
AirplaneModeHelper.removeEventListener(fakeEventName, fakeCallback);
});
test('can\'t remove callbacks', function() {
assert.equal(AirplaneModeHelper._callbacks.length, 1);
});
});
suite('remove event with right keyword', function() {
setup(function() {
AirplaneModeHelper.removeEventListener(kEventName, fakeCallback);
});
test('can remove callbacks', function() {
assert.equal(AirplaneModeHelper._callbacks.length, 0);
});
});
});
suite('setEnabled > ', function() {
suite('we would call ready first', function() {
setup(function() {
this.sinon.stub(AirplaneModeHelper, 'ready');
AirplaneModeHelper._cachedStatus = 'enabled';
AirplaneModeHelper.setEnabled(true);
});
test('to make sure _cachedStatus is ready', function() {
assert.ok(AirplaneModeHelper.ready.called);
});
});
suite('setEnabled(true) when enabling', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'enabling';
AirplaneModeHelper.setEnabled(true);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(false) when enabling', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'enabling';
AirplaneModeHelper.setEnabled(false);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(true) when disabling', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'disabling';
AirplaneModeHelper.setEnabled(true);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(false) when disabling', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'disabling';
AirplaneModeHelper.setEnabled(false);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(true) when enabled', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'enabled';
AirplaneModeHelper.setEnabled(true);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(false) when disabled', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'disabled';
AirplaneModeHelper.setEnabled(false);
});
test('will do nothing', function() {
assert.isUndefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(true) when disabled', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'disabled';
AirplaneModeHelper.setEnabled(true);
});
test('will change airplane mode status', function() {
assert.isDefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
suite('setEnabled(false) when enabled', function() {
setup(function() {
AirplaneModeHelper._cachedStatus = 'enabled';
AirplaneModeHelper.setEnabled(false);
});
test('will change airplane mode status', function() {
assert.isDefined(
window.navigator.mozSettings.mSettings[kCommunicationKey]);
});
});
});
});

View file

@ -0,0 +1,93 @@
/* global AppStarter, MockL10n */
'use strict';
require('/shared/test/unit/mocks/mock_l20n.js');
mocha.globals([
'InitialPanelHandler',
'LaunchContext',
'ActivityHandler'
]);
Object.defineProperty(document, 'readyState', {
value: 'loading',
configurable: true
});
require('/js/startup.js');
suite('AppStarter', function() {
var appStarter, realL10n;
suiteSetup(function() {
realL10n = document.l10n;
document.l10n = MockL10n;
});
suiteTeardown(function() {
document.l10n = realL10n;
});
setup(function() {
appStarter = AppStarter();
});
suite('start', function() {
var fakeInitialPanelId = 'fakePanelId';
var fakeInitialPanelHandler = {};
setup(function() {
this.sinon.stub(appStarter, '_getInitialPanelId', function() {
return Promise.resolve(fakeInitialPanelId);
});
this.sinon.stub(appStarter, '_showInitialPanel');
this.sinon.stub(appStarter, '_createLaunchContext');
this.sinon.stub(appStarter, '_loadAlameda');
this.sinon.stub(window, 'InitialPanelHandler', function() {
return fakeInitialPanelHandler;
});
});
test('should show correct initial panel', function(done) {
appStarter.start().then(function() {
sinon.assert.calledWith(
appStarter._showInitialPanel, fakeInitialPanelId);
}, function() {
// This function does not reject.
assert.isTrue(false, 'panel does not called with the correct panel id');
}).then(done, done);
});
test('should define launch context correctly', function(done) {
var fakeActivityHandler = {};
var realActivityHandler = window.ActivityHandler;
window.ActivityHandler = fakeActivityHandler;
appStarter.start().then(function() {
sinon.assert.calledWith(
appStarter._createLaunchContext, fakeInitialPanelId,
fakeInitialPanelHandler, fakeActivityHandler);
window.ActivityHandler = realActivityHandler;
}, function() {
// This function does not reject.
assert.isTrue(false, 'launch context is not defined correctly');
}).then(done, done);
});
test('should load alameda', function(done) {
appStarter.start().then(function() {
sinon.assert.called(appStarter._loadAlameda);
}, function() {
// This function does not reject.
assert.isTrue(false, 'failed to load alameda');
}).then(done, done);
});
test('should only be executed once', function() {
this.sinon.spy(appStarter._getInitialPanelId);
appStarter.start();
appStarter.start();
sinon.assert.calledOnce(appStarter._getInitialPanelId);
});
});
});

View file

@ -0,0 +1,99 @@
/* globals FxaMenu, loadBodyHTML, MockFxAccountsIACHelper, MockL10n */
'use strict';
// require helpers for managing html
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/html_imports.js');
// mocks and globals
require('mock_fx_accounts_iac_helper.js');
require('/shared/js/text_normalizer.js');
require('/shared/test/unit/mocks/mock_l20n.js');
// source the code we care about
requireApp('settings/js/firefox_accounts/menu.js');
suite('firefox accounts menu item > ', function() {
var suiteSandbox = sinon.sandbox.create(),
fxaDescEl,
setAttributesSpy,
realL10n;
suiteSetup(function() {
// attach mock html to page, so it inits without complaint
loadBodyHTML('./_root.html');
realL10n = document.l10n;
document.l10n = MockL10n;
});
suiteTeardown(function() {
suiteSandbox.restore();
document.l10n = realL10n;
document.body.innerHTML = '';
});
setup(function() {
setAttributesSpy = sinon.spy(document.l10n, 'setAttributes');
fxaDescEl = document.getElementById('fxa-desc');
});
teardown(function() {
MockFxAccountsIACHelper.resetListeners();
document.l10n.setAttributes.restore();
});
test('check the html loaded correctly', function() {
assert.isNotNull(fxaDescEl, 'failed to load settings page html');
});
test('show the correct status on FxaMenu init', function() {
MockFxAccountsIACHelper.setCurrentState({
email: 'init@ialization.com',
verified: true
});
// init app code
FxaMenu.init(MockFxAccountsIACHelper);
// test setAttributes was called with correct args
assert.deepEqual(setAttributesSpy.args[0], [
fxaDescEl,
'fxa-logged-in-text',
{ email: 'init@ialization.com' }
]);
});
test('show the correct status after onlogout event', function() {
MockFxAccountsIACHelper.setCurrentState(null);
FxaMenu.init(MockFxAccountsIACHelper);
MockFxAccountsIACHelper.fireEvent('onlogout');
assert.equal(fxaDescEl.getAttribute('data-l10n-id'), 'fxa-invitation');
});
test('show the correct status after onlogin event', function() {
MockFxAccountsIACHelper.setCurrentState({
email: 'on@log.in',
verified: false
});
FxaMenu.init(MockFxAccountsIACHelper);
MockFxAccountsIACHelper.fireEvent('onlogin');
assert.deepEqual(setAttributesSpy.args[0], [
fxaDescEl,
'fxa-confirm-email',
{ email: 'on@log.in' }
]);
});
test('show the correct status after onverified event', function() {
MockFxAccountsIACHelper.setCurrentState({
email: 'on@verifiedlog.in',
verified: true
});
FxaMenu.init(MockFxAccountsIACHelper);
MockFxAccountsIACHelper.fireEvent('onverified');
assert.deepEqual(setAttributesSpy.args[0], [
fxaDescEl,
'fxa-logged-in-text',
{ email: 'on@verifiedlog.in' }
]);
});
});

View file

@ -0,0 +1,51 @@
/* global InitialPanelHandler */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
mocha.globals([
'InitialPanelHandler'
]);
Object.defineProperty(document, 'readyState', {
value: 'loading',
configurable: true
});
require('/js/startup.js');
suite('InitialPanelHandler', function() {
var realMozSettings;
var initialPanelHandler;
var rootElement;
var callSettingsItem;
var callSettingsItemLink;
setup(function() {
loadBodyHTML('./_root.html');
rootElement = document.getElementById('root');
callSettingsItem = document.getElementById('call-settings');
callSettingsItemLink = document.createElement('a');
callSettingsItemLink.href = '#call';
callSettingsItem.appendChild(callSettingsItemLink);
realMozSettings = navigator.mozSettings;
navigator.mozSettings = null;
initialPanelHandler = InitialPanelHandler(rootElement);
});
teardown(function() {
document.body.innerHTML = '';
navigator.mozSettings = realMozSettings;
});
test('should save correct pending target panel', function() {
initialPanelHandler._addClickListeners();
callSettingsItemLink.onclick({
preventDefault: function() {}
});
assert.equal(initialPanelHandler.pendingTargetPanel, 'call');
});
});

View file

@ -0,0 +1,24 @@
define(function() {
'use strict';
function MockObservableArray() {
return {
array: []
};
}
var AddonManager = {
_addons: MockObservableArray(),
get addons() {
return this._addons;
},
getAddonTargets: function() {
return Promise.resolve();
},
canDelete: function(addon) {
return addon.instance.removable === true;
}
};
return AddonManager;
});

View file

@ -0,0 +1,31 @@
/* exported MockAirplaneModeHelper */
'use strict';
var MockAirplaneModeHelper = {
_enabled: false,
_status: 'disabled',
_callbacks: [],
setEnabled: function(enabled) {
if (enabled) {
this._status = 'enabled';
}
else {
this._status = 'disabled';
}
this._enabled = enabled;
},
ready: function(cb) {
if (cb) {
cb();
}
},
// mock with your own status
getStatus: function() {
return this._status;
},
addEventListener: function(callback) {
this._callbacks.push(callback);
},
removeEventListener: function() {
}
};

View file

@ -0,0 +1,14 @@
define(function() {
'use strict';
var AppsCache = {
_apps: [],
apps: function() {
return Promise.resolve(this._apps);
},
addEventListener: function() {},
removeEventListener: function() {}
};
return AppsCache;
});

View file

@ -0,0 +1,13 @@
/* exported MockAsyncStorage */
'use strict';
var MockAsyncStorage = {
user_data: {},
getItem: function(key, callback) {
callback(MockAsyncStorage.user_data[key]);
},
setItem: function(key, value) {
MockAsyncStorage.user_data[key] = value;
}
};

View file

@ -0,0 +1,11 @@
define(function() {
'use strict';
var DialogService = {
alert: function() {
return Promise.resolve();
}
};
return DialogService;
});

View file

@ -0,0 +1,49 @@
/*exported MockDownloadStore */
'use strict';
var MockDownloadStore = {
error: false,
downloads: [],
listeners: [],
getAll: function() {
var request = {
onsuccess: null
};
setTimeout(function() {
var event = {
target: {
result: this.downloads
}
};
request.onsuccess(event);
}.bind(this), 0);
return request;
},
add: function() {
},
remove: function() {
},
addListener: function(listener) {
var request = {
onsuccess: null
};
if (this.listeners.indexOf(listener) == -1) {
this.listeners.push(listener);
}
setTimeout(function() {
var event = { target: { result: {} }};
request.onsuccess(event);
}.bind(this), 0);
return request;
},
removeListener: function(listener) {
var index = this.listeners.indexOf(listener);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
};

View file

@ -0,0 +1,32 @@
'use strict';
/* exported MockDownloadUI */
var MockDownloadUI = {
show: function() {
return {
set onconfirm(cb) {cb();},
get onconfirm() {return;}
};
},
showActions: function() {
return {
set onconfirm(cb) {cb();},
get onconfirm() {return;},
get result() {
return {
name: 'open'
};
},
set result(value) {}
};
},
TYPE: {
STOP: 'stop',
STOPPED: 'stopped'
}
};

View file

@ -0,0 +1,18 @@
'use strict';
window.MockDsdsSettings = (function() {
var _iccCardIndexForCallSettings = 2;
function ds_getIccCardIndexForCallSettings() {
return _iccCardIndexForCallSettings;
}
function ds_setIccCardIndexForCallSettings(iccCardIndexForCallSettings) {
_iccCardIndexForCallSettings = iccCardIndexForCallSettings;
}
return {
getIccCardIndexForCallSettings: ds_getIccCardIndexForCallSettings,
setIccCardIndexForCallSettings: ds_setIccCardIndexForCallSettings
};
})();

View file

@ -0,0 +1,75 @@
/* exported MockFxAccountsIACHelper */
'use strict';
var MockFxAccountsIACHelper = (function() {
var listeners = {
'onlogin': [],
'onverified': [],
'onlogout': []
};
var currentState = null;
function getAccount(cb) {
cb(currentState);
}
function resendVerificationEmail(email, cb) {
cb();
}
function resetListeners() {
listeners.onlogin = [];
listeners.onverified = [];
listeners.onlogout = [];
}
function addEventListener(eventType, cb) {
if (!(eventType in listeners)) {
throw new Error('tried to add wrong event type');
}
listeners[eventType].push(cb);
}
function removeEventListener(eventType, cb) {
if (!(eventType in listeners)) {
throw new Error('tried to remove wrong event type');
}
for (var i = 0; i < listeners[eventType].length; i++) {
if (cb == listeners[eventType][i]) {
listeners[eventType].splice(i, 1);
}
}
}
function fireEvent(eventType) {
if (!(eventType in listeners)) {
throw new Error('tried to fire wrong event type');
}
for (var i = 0; i < listeners[eventType].length; i++) {
listeners[eventType][i](eventType);
}
}
function setCurrentState(x) {
currentState = x;
}
function getCurrentState() {
return currentState;
}
return {
getAccount: getAccount,
resendVerificationEmail: resendVerificationEmail,
setCurrentState: setCurrentState,
getCurrentState: getCurrentState,
addEventListener: addEventListener,
removeEventListener: removeEventListener,
fireEvent: fireEvent,
listeners: listeners,
resetListeners: resetListeners
};
})();

View file

@ -0,0 +1,20 @@
'use strict';
define(function() {
var MockHomescreenSettings = {
addEventListener: function(key, callback) {
if (!this._eventHandlers) {
this._eventHandlers = {};
}
this._eventHandlers[key] = callback;
},
put: () => {
},
get: () => {
return Promise.resolve('3');
},
setStoreName: () => {}
};
return MockHomescreenSettings;
});

View file

@ -0,0 +1,25 @@
/* global define */
define(function() {
'use strict';
var MockKeyboardContext = {
mKeyboards: null,
mEnabledLayouts: null,
mTeardown: function() {
this.mKeyboards = null;
this.mEnabledLayouts = null;
},
init: function(callback) {
callback();
},
keyboards: function(callback) {
callback(this.mKeyboards);
},
enabledLayouts: function(callback) {
callback(this.mEnabledLayouts);
},
defaultKeyboardEnabled: function(callback) {}
};
return MockKeyboardContext;
});

View file

@ -0,0 +1,21 @@
/* global define */
define(function() {
'use strict';
var ctor = function mockListView() {
if (ctor.mInnerFunction) {
return ctor.mInnerFunction.apply(this, arguments);
} else {
return {
set: function(newArray) {},
destroy: function() {}
};
}
};
ctor.mTeardown = function mockListViewTeardown() {
ctor.mInnerFunction = null;
};
return ctor;
});

View file

@ -0,0 +1,11 @@
define(function() {
'use strict';
var Messaging = {
injectCBSTemplate: function() {},
initCBS: function() {},
disableItems: function() {}
};
return Messaging;
});

View file

@ -0,0 +1,8 @@
'use strict';
/* exported MockMimeMapper */
var MockMimeMapper = {
guessTypeFromFileProperties: function(filename, mimetype) {
return mimetype;
}
};

View file

@ -0,0 +1,63 @@
'use strict';
/* exported MockMozApps */
var MockMozApps = {
mGetCurrentAppCallback: {
onsuccess: null
},
mGetAllAppsCallback: {
onsuccess: null
},
mApps: [],
mCurrentApp: null,
mAddToCurrentApp: function(app) {
MockMozApps.mCurrentApp = app;
MockMozApps.mApps.push(app);
},
mSetApps: function(apps) {
MockMozApps.mApps = apps;
},
mgmt: {
getAll: function() {
return MockMozApps.mGetAllAppsCallback;
},
mUninstallCallback: {
set onsuccess(callback) {
callback();
}
},
uninstall: function(app) {
for (var i = 0; i < MockMozApps.mApps.length; i++) {
if (MockMozApps.mApps[i].manifest.name && app.manifest.name &&
MockMozApps.mApps[i].manifest.name == app.manifest.name) {
MockMozApps.mApps.splice(i, 1);
break;
}
}
return this.mUninstallCallback;
},
setEnabled: function(app, enabled) {
app.enabled = enabled;
},
addEventListener: function() {},
removeEventListener: function() {},
getIcon: function() { return Promise.resolve(); }
},
getSelf: function() {
return MockMozApps.mGetCurrentAppCallback;
},
mTriggerGetCurrentAppCallback: function() {
MockMozApps.mGetCurrentAppCallback.onsuccess({
target: {
result: MockMozApps.mCurrentApp
}
});
},
mTriggerGetAllAppsCallback: function() {
MockMozApps.mGetAllAppsCallback.onsuccess({
target: {
result: MockMozApps.mApps
}
});
}
};

View file

@ -0,0 +1,17 @@
/*exported MockNavigatormozMobileMessage */
'use strict';
var MockNavigatormozMobileMessage = {
SMSC: '+4400000000',
getSmscAddress: function() {
return {
result: this.SMSC || '0',
set onsuccess(callback) {
callback.call(this);
},
set onerror(e) {}
};
}
};

View file

@ -0,0 +1,90 @@
'use strict';
(() => {
var observers = {};
var settings = {};
var removedObservers = {};
function mns_mLockSet(obj) {
var settingsRequest = {};
for (var key in obj) {
settings[key] = obj[key];
}
setTimeout(function() {
if (settingsRequest.onsuccess) {
settingsRequest.onsuccess();
}
});
return settingsRequest;
}
function mns_mLockGet(name) {
var settingsRequest = {
result: settings
};
setTimeout(function() {
if (settingsRequest.onsuccess) {
settingsRequest.onsuccess();
}
});
return settingsRequest;
}
function mns_addObserver(name, cb) {
observers[name] = observers[name] || [];
observers[name].push(cb);
}
function mns_removeObserver(name, cb) {
removedObservers[name] = removedObservers[name] || [];
removedObservers[name].push(cb);
}
function mns_createLock() {
return {
set: mns_mLockSet,
get: mns_mLockGet
};
}
function mns_mTriggerObservers(name, args) {
var theseObservers = observers[name];
if (! theseObservers) {
return;
}
theseObservers.forEach(function(func) {
func(args);
});
}
function mns_teardown() {
observers = {};
settings = {};
removedObservers = {};
}
window.MockNavigatorSettings = {
addObserver: mns_addObserver,
removeObserver: mns_removeObserver,
createLock: mns_createLock,
mTriggerObservers: mns_mTriggerObservers,
mTeardown: mns_teardown,
get mObservers() {
return observers;
},
get mSettings() {
return settings;
},
get mRemovedObservers() {
return removedObservers;
}
};
})();

View file

@ -0,0 +1,12 @@
/* global define */
define(function() {
'use strict';
return {
oneColumn: function(oldPanel, newPanel, callback) {
setTimeout(callback);
},
twoColumn: function(oldPanel, newPanel, callback) {
setTimeout(callback);
}
};
});

View file

@ -0,0 +1,14 @@
/* exported MockSettings */
/* global MockNavigatorSettings */
'use strict';
requireApp('settings/test/unit/mock_navigator_settings.js');
var MockSettings = {
currentPanel: '',
mSuiteSetup: function() {
this.mozSettings = MockNavigatorSettings;
},
mSetup: function() {
this.currentPanel = '';
}
};

View file

@ -0,0 +1,22 @@
/* global define */
define(function() {
'use strict';
var _settings = {};
var ctor = {
getSettings: function(callback) {
callback(_settings);
},
// custom settings
mockSettings: function(value) {
_settings = value;
},
mTeardown: function() {
_settings = {};
}
};
return ctor;
});

View file

@ -0,0 +1,19 @@
/* global define */
define(function() {
'use strict';
// As this module is simply a function, we are not able to mock the function
// in our tests after the module loaded. mInnerFunction provides a way that
// allows one loads the module be able to mock the behavior of the function.
var ctor = function mock_settings_panel() {
if (ctor.mInnerFunction) {
return ctor.mInnerFunction.apply(this, arguments);
}
};
ctor.mTeardown = function mock_settings_panel_teardown() {
ctor.mInnerFunction = null;
};
return ctor;
});

View file

@ -0,0 +1,15 @@
/* global define */
define(function() {
'use strict';
var MockSettingsService = {
navigate: function(panelId, options, callback) {
if (typeof callback === 'function') {
callback();
}
return;
},
back: function() {}
};
return MockSettingsService;
});

View file

@ -0,0 +1,11 @@
define(function() {
'use strict';
var MockSettingsUtils = {
loadTemplate: function(templatePanelId, callback) {
callback();
}
};
return MockSettingsUtils;
});

View file

@ -0,0 +1,7 @@
'use strict';
function MockSimPinDialog(idOrNode) {}
MockSimPinDialog.prototype.show = function(options) {
return options;
};

View file

@ -0,0 +1,42 @@
define(function(require) {
'use strict';
function MockSimUIModel(cardIndex) {
this._cardIndex = cardIndex;
// state list
this._enabled = true;
this._absent = false;
this._locked = false;
this._name = {
id: 'simWithIndex',
args: {
index: this._cardIndex + 1
}
};
this._number = '0123456789';
this._operator = {
text: 'Taiwan telecom'
};
}
MockSimUIModel.prototype = {
getInfo: function() {
return {
enabled: this._enabled,
absent: this._absent,
locked: this._locked,
name: this._name,
number: this._number,
operator: this._operator
};
},
setState: function(key, options) {
// we can use this to make sure we setState successfully
this._state = key;
}
};
return function ctor_mockSimUIModel(cardIndex) {
return new MockSimUIModel(cardIndex);
};
});

View file

@ -0,0 +1,13 @@
define(function(require) {
'use strict';
function MockTemplate(idOrNode) {
}
MockTemplate.prototype.interpolate = function(data, options) {
return data;
};
return MockTemplate;
});

View file

@ -0,0 +1,30 @@
/* global define */
define(function() {
'use strict';
var _mInnerFunction = null;
var ctor = function mock_panel_module() {
if (_mInnerFunction) {
return _mInnerFunction.apply(this, arguments);
}
};
// As this module is simply a function, we are not able to mock the function
// in our tests after the module loaded. mInnerFunction provides a way that
// allows one loads the module be able to mock the behavior of the function.
Object.defineProperty(ctor, 'mInnerFunction', {
configurable: true,
get: function() {
return _mInnerFunction;
},
set: function(value) {
_mInnerFunction = value;
}
});
ctor.mTeardown = function mock_panel_module_teardown() {
ctor._mInnerFunction = null;
};
return ctor;
});

View file

@ -0,0 +1,21 @@
'use strict';
(function(exports) {
var MockToaster = {
_mInited: false,
_lastToast: undefined,
isInitialized: function mt_isInitialized() {
return this._mInited;
},
showToast: function mt_showToast(options) {
this._lastToast = options;
},
mSetup: function mt_mSetup() {
this._mInited = true;
},
mTeardown: function mt_mTeardown() {
this._mInited = false;
}
};
exports.MockToaster = MockToaster;
}(window));

View file

@ -0,0 +1,12 @@
define(function() {
'use strict';
var Wifi = {
_elements: {
wifiAvailableNetworks: document.createElement('div'),
infoItem: document.createElement('div'),
scanItem: document.createElement('div')
}
};
return Wifi;
});

View file

@ -0,0 +1,20 @@
define(function() {
'use strict';
var MockWifiContext = {
_callbacks: [],
currentNetwork: {},
wps: {
selectedAp: '',
selectedMethod: '',
pin: ''
},
associateNetwork: function(network) {},
forgetNetwork: function(network) {},
addEventListener: function(callback) {
this._callbacks.push(callback);
}
};
return MockWifiContext;
});

View file

@ -0,0 +1,35 @@
define(function() {
'use strict';
var MockWifiUtils = {
_network: {},
wifiDialog: function(dialogID, options) {
var self = this;
if (options.callback) {
setTimeout(function() {
options.callback(self._network);
});
}
},
wifiConnect: function() {},
wifiDisconnect: function() {},
newExplanationItem: function(message) {
return document.createElement('div');
},
newListItem: function(options) {
var item = document.createElement('li');
item.setAttribute('network', options.network);
return item;
},
checkPassword: function() {
},
changeDisplay: function() {
},
getNetworkKey: function(network) {
return network ? network.ssid : '';
}
};
return MockWifiUtils;
});

View file

@ -0,0 +1,35 @@
/* global define */
define(function() {
'use strict';
var MockXMLHttpRequest = function() {
this.data = {};
this.timeout = null;
};
MockXMLHttpRequest.prototype = {
triggerReadyStateChange: function(status) {
this.status = status;
this.onreadystatechange();
},
triggerOnLoad: function(status) {
this.status = status;
this.onload();
},
open: function(requestType, requestUrl) {
this.data.requestType = requestType;
this.data.requestUrl = requestUrl;
},
setRequestHeader: function(type, value) {
this.data.header = {
type: type,
value: value
};
},
send: function(value) {
this.data.value = value;
}
};
return MockXMLHttpRequest;
});

View file

@ -0,0 +1,661 @@
'use strict';
suite('addons manager test > ', function() {
var realMozActivity;
var mockAppsCache;
var mockXMLHttpRequest;
var mockMozApps;
var mockMozActivity;
var AddonManager;
var MockApp;
var modules = [
'unit/mock_moz_apps',
'shared_mocks/mock_moz_activity',
'modules/addon_manager',
'shared_mocks/mock_xml_http_request',
'MockApp'
];
var map = {
'modules/addon_manager': {
'modules/navigator/mozApps': 'unit/mock_moz_apps',
'modules/apps_cache': 'unit/mock_apps_cache',
'mdoules/app': 'MockApp'
}
};
MockApp = function(app) {
return {
get enabled() {
return app.enabled;
},
get instance() {
return app;
}
};
};
// Check if an array of wrapped addons contains a specific un-wrapped addon.
function containsAddon(wrappedAddons, addon) {
return wrappedAddons.some((wrappedAddon) => {
return wrappedAddon.instance === addon;
});
}
var app1 = {
manifest: {
name: 'testAppName1',
},
manifestURL: 'testManifestURL_app1',
origin: 'app://testorigin1'
};
var app2 = {
manifest: {
name: 'testAppName2',
},
manifestURL: 'testManifestURL_app2',
origin: 'app://testorigin2'
};
var privilegedApp = {
manifest: {
type: 'privileged',
name: 'privilegedAppName',
},
manifestURL: 'testManifestURL_priv_app',
origin: 'app://privilegedorigin'
};
var certifiedApp = {
manifest: {
type: 'certified',
role: 'system',
name: 'certifiedAppName',
},
manifestURL: 'testManifestURL_cert_app',
origin: 'app://certifiedorigin'
};
var settingsApp = {
manifest: {
type: 'certified',
name: 'Settings',
launch_path: '/index.html#',
},
manifestURL: 'testManifestURL_settings_app',
origin: 'app://settings.gaiamobile.org'
};
var addon1 = {
manifest: {
role: 'addon',
name: 'testAddonName1',
// content_scripts field is internally copied from manifest.json to
// manifest.webapp
content_scripts: []
},
manifestURL: 'testManifestURL_addon1',
origin: 'app://testaddonorigin1'
};
var addon2 = {
manifest: {
role: 'addon',
name: 'testAddonName2',
// content_scripts field is internally copied from manifest.json to
// manifest.webapp
content_scripts: [{
matches: '<all_urls>',
js: [],
css: ['foo.css']
}]
},
manifestURL: 'testManifestURL_addon2',
origin: 'app://testaddonorigin2'
};
var obsoleteAddon = {
manifest: {
role: 'addon',
name: 'obsoleteAddon',
customizations: [{
css: ['foo.css'],
scripts: []
}]
},
manifestURL: 'testManifestURL_obsoleteAddon',
origin: 'app://testobsoleteaddonorigin'
};
var privilegedAddon = {
manifest: {
role: 'addon',
type: 'privileged',
name: 'privilegedAddonName',
// content_scripts field is internally copied from manifest.json to
// manifest.webapp
content_scripts: [{
matches: '<all_urls>',
js: ['addon.js'],
css: []
}]
},
manifestURL: 'testManifestURL_priv_addon',
origin: 'app://privilegedaddonorigin'
};
var certifiedAddon = {
manifest: {
role: 'addon',
type: 'certified',
name: 'certifiedAddonName',
// content_scripts field is internally copied from manifest.json to
// manifest.webapp
content_scripts: [{
matches: '<all_urls>',
js: ['addon.js'],
css: []
}]
},
manifestURL: 'testManifestURL_cert_addon',
origin: 'app://certifiedaddonorigin'
};
var settingsAddon = {
manifest: {
role: 'addon',
type: 'certified',
name: 'settingAddonName',
content_scripts: [{
matches: 'app://settings.gaiamobile.org/index.html*',
js: [],
css: []
}]
},
manifestURL: 'testManifestURL_settings_addon',
origin: 'app://settingsaddonorigin'
};
setup(function(done) {
var requireCtx = testRequire(['unit/mock_apps_cache'], map,
(MockAppsCache) => {
mockAppsCache = MockAppsCache;
mockAppsCache._apps = [
app1, app2, addon1, addon2, obsoleteAddon,
privilegedApp, privilegedAddon,
certifiedApp, certifiedAddon,
settingsApp, settingsAddon
];
this.sinon.spy(mockAppsCache, 'addEventListener');
this.sinon.spy(mockAppsCache, 'removeEventListener');
define('MockApp', function() {
return MockApp;
});
requireCtx(modules,
(MockMozApps, MockMozActivity, _AddonManager, MockXMLHttpRequest) => {
mockMozApps = MockMozApps;
mockMozActivity = MockMozActivity;
mockXMLHttpRequest = MockXMLHttpRequest;
mockMozApps.mSetApps([
app1, app2, addon1, addon2, obsoleteAddon,
privilegedApp, privilegedAddon,
certifiedApp, certifiedAddon,
settingsApp, settingsAddon
]);
this.sinon.spy(mockMozApps.mgmt, 'uninstall');
this.sinon.spy(mockMozApps.mgmt, 'setEnabled');
realMozActivity = window.MozActivity;
window.MozActivity = window.MockMozActivity;
window.MozActivity.mSetup();
AddonManager = _AddonManager;
done();
});
});
});
teardown(function() {
window.MockMozActivity.mTeardown();
window.MozActivity = realMozActivity;
});
test('AddonManager functions are all defined', function() {
assert.isFunction(AddonManager.isEnabled);
assert.isFunction(AddonManager.enableAddon);
assert.isFunction(AddonManager.disableAddon);
assert.isFunction(AddonManager.canDelete);
assert.isFunction(AddonManager.deleteAddon);
assert.isFunction(AddonManager.shareAddon);
assert.isFunction(AddonManager.getAddonTargets);
});
suite('AddonManager.addons', function() {
test('contains all the addons and no apps', function(done) {
function doCheck() {
var containsAll = [addon1, addon2, obsoleteAddon, privilegedAddon,
certifiedAddon, settingsAddon].every(addon =>
containsAddon(AddonManager.addons.array, addon));
if (containsAll && AddonManager.length === 6) {
done();
}
}
if (doCheck()) {
done();
} else {
AddonManager.addons.observe('insert', doCheck);
AddonManager.addons.observe('remove', doCheck);
AddonManager.addons.observe('reset', doCheck);
AddonManager.addons.observe('replace', doCheck);
}
});
});
suite('_isAddon', function() {
test('returns true for addons', function() {
assert.isTrue(AddonManager._isAddon(addon1));
assert.isTrue(AddonManager._isAddon(addon2));
assert.isTrue(AddonManager._isAddon(certifiedAddon));
assert.isTrue(AddonManager._isAddon(privilegedAddon));
assert.isTrue(AddonManager._isAddon(settingsAddon));
});
test('returns false for non-addons', function() {
assert.isFalse(AddonManager._isAddon(app1));
assert.isFalse(AddonManager._isAddon(app2));
assert.isFalse(AddonManager._isAddon(certifiedApp));
assert.isFalse(AddonManager._isAddon(privilegedApp));
assert.isFalse(AddonManager._isAddon(settingsApp));
});
});
suite('_isEnabled', function() {
test('only returns true for enabled addons', function() {
var wrappedAddon = MockApp(addon1);
var wrappedApp = MockApp(app1);
addon1.enabled = true;
assert.isTrue(AddonManager.isEnabled(wrappedAddon));
addon1.enabled = false;
assert.isFalse(AddonManager.isEnabled(wrappedAddon));
delete addon1.enabled;
assert.isFalse(AddonManager.isEnabled(wrappedAddon));
app1.enabled = true;
assert.isFalse(AddonManager.isEnabled(wrappedApp));
delete app1.enabled;
});
});
suite('_updateAddons', function() {
test('add the new addon to addons from WebIDE', function() {
// Installed with WebIDE, manifest exists
var newAddon = {
manifest: {
role: 'addon',
name: 'newAddon'
},
manifestURL: 'newAddonManifestURL',
origin: 'app://newAddonManifestURL',
addEventListener: function(evt, cb) {
cb({
type: evt,
application: this
});
}
};
assert.isFalse(containsAddon(AddonManager.addons.array, newAddon));
// Trigger an install event
mockAppsCache.addEventListener.args.forEach(listener => {
if (listener[0] === 'oninstall') {
// evt for the callback function
listener[1]({
application: newAddon,
type: 'install'
});
}
});
assert.isTrue(containsAddon(AddonManager.addons.array, newAddon));
});
test('add the new addon to addons from Marketplace', function() {
// Installed with Marketplace, manifest does not exist yet
var newAddon = {
manifestURL: 'newAddonManifestURL',
origin: 'app://newAddonManifestURL',
addEventListener: function(evt, cb) {
cb({
type: evt,
application: this
});
}
};
assert.isFalse(containsAddon(AddonManager.addons.array, newAddon));
// Trigger an install event
mockAppsCache.addEventListener.args.forEach(listener => {
if (listener[0] === 'oninstall') {
// evt for the callback function
// Once downloadsuccess is fired, manifest would be available
newAddon.manifest = {
role: 'addon',
name: 'newAddon'
};
listener[1]({
application: newAddon,
type: 'downloadsuccess'
});
}
});
assert.isTrue(containsAddon(AddonManager.addons.array, newAddon));
});
test('remove the addon from addons when "uninstall', function() {
assert.isTrue(containsAddon(AddonManager.addons.array, addon1));
// Trigger an uninstall event.
mockAppsCache.addEventListener.args.forEach(listener => {
if (listener[0] === 'onuninstall') {
listener[1]({
application: addon1,
type: 'uninstall'
});
}
});
assert.isFalse(containsAddon(AddonManager.addons.array, addon1));
});
test('only add the same addon once', function() {
assert.isTrue(containsAddon(AddonManager.addons.array, addon1));
AddonManager._updateAddons({
application: addon1,
type: 'install'
});
assert.ok(AddonManager.addons.array.filter((addon) => {
return addon.instance === addon1;
}).length === 1);
});
});
suite('_privilegeCheck', function() {
test('should return correct result', function() {
assert.isTrue(AddonManager._privilegeCheck(addon1, app1));
assert.isFalse(AddonManager._privilegeCheck(addon1, privilegedApp));
assert.isFalse(AddonManager._privilegeCheck(addon1, certifiedApp));
assert.isTrue(AddonManager._privilegeCheck(privilegedAddon, app1));
assert.isTrue(
AddonManager._privilegeCheck(privilegedAddon, privilegedApp));
assert.isTrue(
AddonManager._privilegeCheck(privilegedAddon, certifiedApp));
assert.isTrue(AddonManager._privilegeCheck(certifiedAddon, app1));
assert.isTrue(
AddonManager._privilegeCheck(certifiedAddon, privilegedApp));
assert.isTrue(
AddonManager._privilegeCheck(certifiedAddon, certifiedApp));
});
});
suite('enableAddon', function() {
test('does nothing for non-addons', function() {
var wrappedApp = MockApp(app1);
AddonManager.enableAddon(wrappedApp);
assert.equal(mockMozApps.mgmt.setEnabled.callCount, 0);
});
test('calls setEnabled for addons', function() {
var wrappedAddon = MockApp(addon1);
app1.enabled = false;
AddonManager.enableAddon(wrappedAddon);
assert.isTrue(mockMozApps.mgmt.setEnabled.calledWith(addon1, true));
});
});
suite('disableAddon', function() {
test('does nothing for non-addons',
function() {
var wrappedApp = MockApp(app1);
AddonManager.disableAddon(wrappedApp);
assert.equal(mockMozApps.mgmt.setEnabled.callCount, 0);
});
test('synchronously calls setEnabled for addons', function() {
var wrappedAddon = MockApp(addon1);
addon1.enabled = true;
AddonManager.disableAddon(wrappedAddon);
assert.isTrue(mockMozApps.mgmt.setEnabled.calledWith(addon1, false));
});
});
suite('canDelete', function() {
test('only returns true for removable addons', function() {
var wrappedAddon = MockApp(addon1);
var wrappedApp = MockApp(app1);
addon1.removable = true;
assert.isTrue(AddonManager.canDelete(wrappedAddon));
addon1.removable = false;
assert.isFalse(AddonManager.canDelete(wrappedAddon));
delete addon1.removable;
assert.isFalse(AddonManager.canDelete(wrappedAddon));
app1.removable = true;
assert.isFalse(AddonManager.canDelete(wrappedApp));
delete app1.removable;
});
});
suite('deleteAddon', function() {
test('rejects non-addons', function(done) {
var wrappedApp = MockApp(app1);
assert.isRejected(AddonManager.deleteAddon(wrappedApp)).notify(done);
});
test('rejects for non-removable addons', function(done) {
var wrappedAddon = MockApp(addon1);
addon1.removable = false;
assert.isRejected(AddonManager.deleteAddon(wrappedAddon)).notify(done);
});
test('resolves for removable addons and calls uninstall', function(done) {
var wrappedAddon = MockApp(addon1);
addon1.removable = true;
AddonManager.deleteAddon(wrappedAddon).then(function resolve() {
assert.isTrue(mockMozApps.mgmt.uninstall.calledWith(addon1));
done();
}, function reject(error) {
done(error);
});
});
});
suite('shareAddon', function() {
test('rejects non-addons', function(done) {
var wrappedApp = MockApp(app1);
assert.isRejected(AddonManager.shareAddon(wrappedApp)).notify(done);
});
test('starts a share activity and resolves', function(done) {
var wrappedAddon = MockApp(addon1);
var promise = AddonManager.shareAddon(wrappedAddon);
var activityRequest = window.MockMozActivity.calls.pop();
assert.equal(activityRequest.name, 'share');
assert.equal(activityRequest.data.type, 'app');
assert.equal(activityRequest.data.app, addon1.manifestURL);
assert.isFulfilled(promise).notify(done);
});
test('rejects if share activity fails', function(done) {
var wrappedAddon = MockApp(addon1);
var promise = AddonManager.shareAddon(wrappedAddon);
window.MockMozActivity.mTriggerOnError();
assert.isRejected(promise).notify(done);
});
});
suite('getAddonTargets', function() {
test('rejects non-addons', function(done) {
var wrappedApp = MockApp(app1);
assert.isRejected(AddonManager.getAddonTargets(wrappedApp)).notify(done);
});
test('returns no targets for obsolete addons',
function(done) {
var wrappedAddon = MockApp(obsoleteAddon);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [])
.notify(done);
});
test('returns no targets for addons with no content scripts',
function(done) {
var wrappedAddon = MockApp(addon1);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [])
.notify(done);
});
test('returns only unprivileged apps for unprivileged addons',
function(done) {
var wrappedAddon = MockApp(addon2);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [app1, app2])
.notify(done);
});
test('does returns certified + privileged apps for privileged addons',
function(done) {
var wrappedAddon = MockApp(privilegedAddon);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon),
[app1, app2, privilegedApp, certifiedApp, settingsApp]).notify(done);
});
test('returns any apps for certified addons', function(done) {
var wrappedAddon = MockApp(certifiedAddon);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon),
[app1, app2, privilegedApp, certifiedApp, settingsApp]).notify(done);
});
test('honors specific addon filters', function(done) {
var wrappedAddon = MockApp(addon2);
addon2.manifest.content_scripts[0].matches = 'app://testorigin1/';
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [app1])
.notify(done);
});
test('honors regexp addon filters', function(done) {
var wrappedAddon = MockApp(addon2);
addon2.manifest.content_scripts[0].matches = 'app://*/';
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [app1, app2])
.notify(done);
});
test('honors settings regexp addon filters', function(done) {
var wrappedAddon = MockApp(settingsAddon);
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [settingsApp])
.notify(done);
});
test('does not return addons even explicitly targeted', function(done) {
var wrappedAddon = MockApp(addon2);
addon2.manifest.content_scripts[0].matches = 'app://testaddonorigin1/';
assert.becomes(AddonManager.getAddonTargets(wrappedAddon), [])
.notify(done);
});
});
suite('getAddonDisableType', function() {
test('resolves to an empty string if no targets', function(done) {
var wrappedAddon = MockApp(addon1);
assert.becomes(AddonManager.getAddonDisableType(wrappedAddon), '')
.notify(done);
});
test('obsolete addon always resolves to an empty string if no targets',
function(done) {
var wrappedAddon = MockApp(obsoleteAddon);
assert.becomes(AddonManager.getAddonDisableType(wrappedAddon), '')
.notify(done);
});
test('resolves to an empty string if no scripts', function(done) {
var wrappedAddon = MockApp(addon2);
assert.becomes(AddonManager.getAddonDisableType(wrappedAddon), '')
.notify(done);
});
test('resolves to "restart" if it injects scripts', function(done) {
var wrappedAddon = MockApp(privilegedAddon);
privilegedAddon.manifest.content_scripts[0].exclude_matches =
'app://certifiedorigin/';
assert.becomes(AddonManager.getAddonDisableType(wrappedAddon), 'restart')
.notify(done);
});
test('resolves to "reboot" if addon injects into a system app',
function(done) {
var wrappedAddon = MockApp(certifiedAddon);
assert.becomes(AddonManager.getAddonDisableType(wrappedAddon), 'reboot')
.notify(done);
});
});
suite('findAddonByManifestURL', function() {
test('resolves to the correct addon object when matches', function(done) {
var targetManifestURL = 'testManifestURL_priv_addon';
var privAddon = AddonManager.addons.array.find((addon) => {
return addon.instance.manifestURL === targetManifestURL;
});
assert.becomes(AddonManager
.findAddonByManifestURL(targetManifestURL), privAddon).notify(done);
});
test('resolves to undefined when no addon matches', function(done) {
assert.becomes(AddonManager
.findAddonByManifestURL('fakeManifestURL'), undefined).notify(done);
});
});
suite('addonAffectsApp', function() {
test('resolves to true when an addon affects an app', function(done) {
addon2.manifest.content_scripts[0].matches = 'app://testorigin1/';
var wrappedAddon = MockApp(addon2);
assert.becomes(AddonManager
.addonAffectsApp(wrappedAddon, 'testManifestURL_app1'), true)
.notify(done);
});
test('obsolete addon always resolves to false', function(done) {
var wrappedAddon = MockApp(obsoleteAddon);
assert.becomes(AddonManager
.addonAffectsApp(wrappedAddon, 'testManifestURL_app1'), false)
.notify(done);
});
test('resolves to false when an addon does not affect an app',
function(done) {
var wrappedAddon = MockApp(addon1);
assert.becomes(AddonManager
.addonAffectsApp(wrappedAddon, 'testManifestURL_app1'), false)
.notify(done);
});
test('resolves to true when an addon match pattern matches an app',
function(done) {
var wrappedAddon = MockApp(settingsAddon);
assert.becomes(AddonManager
.addonAffectsApp(wrappedAddon, 'testManifestURL_settings_app'), true)
.notify(done);
});
});
});

View file

@ -0,0 +1,335 @@
'use strict';
suite('ApnList', function() {
var FAKE_APN_LIST_KEY = 'fakeKey';
var mockAsyncStorage;
var map = {
'*': {
'modules/async_storage': 'MockAsyncStorage',
'modules/apn/apn_utils': 'MockApnUtils'
}
};
var modules = [
'modules/apn/apn_list',
'modules/apn/apn_item',
'modules/async_storage',
'modules/apn/apn_utils'
];
setup(function(done) {
var requireCtx = testRequire([], map, function() {});
mockAsyncStorage = {};
define('MockAsyncStorage', function() {
return {
setItem: function() {
return Promise.resolve();
},
getItem: function(key) {
return Promise.resolve(mockAsyncStorage[key]);
}
};
});
define('MockApnUtils', function() {
return {
generateId: function() {},
clone: function() {}
};
});
requireCtx(modules,
function(ApnList, ApnItem, MockAsyncStorage, MockApnUtils) {
this.ApnList = ApnList;
this.ApnItem = ApnItem;
this.MockAsyncStorage = MockAsyncStorage;
this.MockApnUtils = MockApnUtils;
// Create mock apn items.
this.mockApnItems = [
{
id: '0',
category: 'category0'
},
{
id: '1',
category: 'category1'
}
];
this.apnList = new this.ApnList(FAKE_APN_LIST_KEY);
mockAsyncStorage = {};
mockAsyncStorage[FAKE_APN_LIST_KEY] = this.mockApnItems;
done();
}.bind(this));
});
suite('_add', function() {
test('add with category specified', function(done) {
var apn = {};
var category = 'category';
var randomId = 'randomId';
mockAsyncStorage = {};
this.sinon.stub(this.MockApnUtils, 'generateId', function() {
return randomId;
});
this.apnList._add(apn, category)
.then(function(apnId) {
var addedApnItem = this.apnList._apnItems[0];
assert.deepEqual(addedApnItem.apn, apn);
assert.equal(addedApnItem.id, randomId);
assert.equal(addedApnItem.category, category);
assert.equal(apnId, randomId);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('add without category specified', function(done) {
var apn = {};
var randomId = 'randomId';
mockAsyncStorage = {};
this.sinon.stub(this.MockApnUtils, 'generateId', function() {
return randomId;
});
this.apnList._add(apn)
.then(function(apnId) {
var addedApnItem = this.apnList._apnItems[0];
assert.deepEqual(addedApnItem.apn, apn);
assert.equal(addedApnItem.id, randomId);
assert.equal(addedApnItem.category, this.ApnItem.APN_CATEGORY.CUSTOM);
assert.equal(apnId, randomId);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('_remove', function() {
test('no existing apn items', function(done) {
mockAsyncStorage = {};
this.apnList._remove('000')
.then(function() {
// Should not resolve.
assert.isTrue(false);
}.bind(this), function(err) {
assert.equal(err, 'no apn items');
}).then(done, done);
});
test('remove with valid id', function(done) {
var removedApnId = this.mockApnItems[0].id;
this.apnList._remove(removedApnId)
.then(function() {
assert.ok(!this.apnList._apnItems.some(
(apnItem) => apnItem.id === removedApnId));
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('remove with invalid id', function(done) {
var removedApnId = 'invalidId';
this.apnList._remove(removedApnId)
.then(function() {
// Should not resolve.
assert.isTrue(false);
}.bind(this), function(err) {
assert.equal(err, 'apn not found');
}).then(done, done);
});
});
suite('_update', function() {
test('no existing apn items', function(done) {
mockAsyncStorage = {};
this.apnList._update('000', {})
.then(function() {
// Should not resolve.
assert.isTrue(false);
}.bind(this), function(err) {
assert.equal(err, 'no apn items');
}).then(done, done);
});
test('update with valid id', function(done) {
var updatedApnId = this.mockApnItems[0].id;
var apn = {
fakeKey: 'fakeValue'
};
this.apnList._update(updatedApnId, apn)
.then(function() {
assert.equal(this.apnList._apnItems[0].apn.fakeKey, apn.fakeKey);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('update with invalid id', function(done) {
var updatedApnId = 'invalidId';
this.apnList._update(updatedApnId, {})
.then(function() {
// Should not resolve.
assert.isTrue(false);
}.bind(this), function(err) {
assert.equal(err, 'apn not found');
}).then(done, done);
});
});
suite('items', function() {
test('with cached apn items', function(done) {
var that = this;
this.apnList._apnItems = this.mockApnItems;
this.apnList.items()
.then(function(result) {
assert.equal(result, that.mockApnItems);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('without cached apn items', function(done) {
var that = this;
this.apnList._apnItems = null;
mockAsyncStorage = {};
mockAsyncStorage[FAKE_APN_LIST_KEY] = this.mockApnItems;
this.apnList.items()
.then(function(apnItems) {
apnItems.forEach(function(apnItem, index) {
var mockApnItem = that.mockApnItems[index];
assert.equal(apnItem.id, mockApnItem.id);
assert.equal(apnItem.category, mockApnItem.category);
assert.equal(apnItem.apn, mockApnItem);
});
assert.equal(apnItems, that.apnList._apnItems);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('item', function() {
test('without id specified', function(done) {
this.apnList.item()
.then(function(apnItem) {
assert.isNull(apnItem);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('without existing apn items', function(done) {
mockAsyncStorage = {};
this.apnList.item('1')
.then(function(apnItem) {
assert.isNull(apnItem);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('with existing apn items', function(done) {
this.apnList._apnItems = this.mockApnItems;
this.apnList.item('1')
.then(function(apnItem) {
assert.equal(apnItem.id, '1');
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('add', function() {
test('should call to _schedule and _add correctly', function(done) {
var apn = {};
var category = 'category';
var apnClone = {};
this.sinon.stub(this.MockApnUtils, 'clone', function() {
return apnClone;
});
this.sinon.stub(this.apnList, '_add', function() {
return Promise.resolve();
});
this.sinon.spy(this.apnList, '_schedule');
this.apnList.add(apn, category)
.then(function() {
sinon.assert.calledWith(this.MockApnUtils.clone, apn);
sinon.assert.called(this.apnList._schedule);
sinon.assert.calledWith(this.apnList._add, apnClone, category);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('remove', function() {
test('should call to _schedule and _remove correctly', function(done) {
var apnId = '000';
this.sinon.stub(this.apnList, '_remove', function() {
return Promise.resolve();
});
this.sinon.spy(this.apnList, '_schedule');
this.apnList.remove(apnId)
.then(function() {
sinon.assert.called(this.apnList._schedule);
sinon.assert.calledWith(this.apnList._remove, apnId);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('update', function() {
test('should call to _schedule and _update correctly', function(done) {
var apnId = '000';
var apn = {};
var apnClone = {};
this.sinon.stub(this.MockApnUtils, 'clone', function() {
return apnClone;
});
this.sinon.stub(this.apnList, '_update', function() {
return Promise.resolve();
});
this.sinon.spy(this.apnList, '_schedule');
this.apnList.update(apnId, apn)
.then(function() {
sinon.assert.calledWith(this.MockApnUtils.clone, apn);
sinon.assert.called(this.apnList._schedule);
sinon.assert.calledWith(this.apnList._update, apnId, apnClone);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
});

View file

@ -0,0 +1,186 @@
'use strict';
suite('ApnSelections', function() {
var map = {
'*': {
'modules/settings_cache': 'MockSettingsCache'
}
};
var modules = [
'modules/apn/apn_selections',
'modules/apn/apn_const',
'modules/settings_cache'
];
var realMozSettings;
var mockMozSettings;
var realMobileConnections;
var mockMobileConnections;
function createMockApnSelections(apnTypes) {
var mockApnSelections = [];
function addMockApnSelection(apnType, i) {
mockApnSelection[apnType] = apnType + '_' + i + '_id';
}
for (var i = 0; i < mockMobileConnections.length; i++) {
var mockApnSelection = {};
apnTypes.forEach(addMockApnSelection);
mockApnSelections.push(mockApnSelection);
}
return mockApnSelections;
}
suiteSetup(function() {
mockMozSettings = {
_mObservers: {},
createLock: function() {
return {
set: function() {
var obj = {};
setTimeout(function() {
obj.onsuccess();
});
return obj;
}
};
},
addObserver: function(name, callback) {
this._mObservers[name] = this._mObservers[name] || [];
this._mObservers[name].push(callback);
},
mTriggerObservers: function(name, value) {
this._mObservers[name].forEach(function(callback) {
callback({
settingValue: value
});
});
}
};
realMozSettings = navigator.mozSettings;
navigator.mozSettings = mockMozSettings;
mockMobileConnections = [{}, {}];
realMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = mockMobileConnections;
});
suiteTeardown(function() {
navigator.mozSettings = realMozSettings;
navigator.mozMobileConnections = realMobileConnections;
});
setup(function(done) {
var that = this;
var requireCtx = testRequire([], map, function() {});
this.mockSettingsDB = {};
define('MockSettingsCache', function() {
return {
getSettings: function(callback) {
callback(that.mockSettingsDB);
}
};
});
requireCtx(modules,
function(ApnSelections, ApnConst, MockSettingsCache) {
this.ApnSelections = ApnSelections;
this.ApnConst = ApnConst;
this.MockSettingsCache = MockSettingsCache;
this.apnSelections = new this.ApnSelections();
this.mockApnSelections =
createMockApnSelections(this.ApnConst.APN_TYPES);
this.mockSettingsDB[this.ApnConst.APN_SELECTIONS_KEY] =
this.mockApnSelections;
done();
}.bind(this));
});
suite('get', function() {
test('should get correct apn selections', function(done) {
var isTheSameSelection = (function(selection1, selection2) {
return this.ApnConst.APN_TYPES.every(function(apnType) {
return selection1[apnType] === selection2[apnType];
});
}).bind(this);
var promises = [];
for (var i = 0; i < mockMobileConnections.length; i++) {
promises.push(this.apnSelections.get(i));
}
Promise.all(promises).then(function(results) {
results.forEach(function(result, index) {
assert.ok(isTheSameSelection(result, this.mockApnSelections[index]));
}, this);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('should get empty apn selections when apn selections is unavailable',
function(done) {
this.mockSettingsDB[this.ApnConst.APN_SELECTIONS_KEY] = null;
var isEmptySelection = (function(selection) {
return this.ApnConst.APN_TYPES.every(function(apnType) {
return selection[apnType] == null;
});
}).bind(this);
var promises = [];
for (var i = 0; i < mockMobileConnections.length; i++) {
promises.push(this.apnSelections.get(i));
}
Promise.all(promises).then(function(results) {
results.forEach(function(result) {
assert.ok(isEmptySelection(result));
}, this);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('should commit when the selection is changed', function(done) {
var FAKE_ACTIVE_APN_ID = '000';
this.sinon.spy(this.apnSelections, '_commit');
this.apnSelections.get(0).then(function(apnSelection) {
apnSelection['default'] = FAKE_ACTIVE_APN_ID;
sinon.assert.called(this.apnSelections._commit);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('clear', function() {
test('should clear selection correctly', function(done) {
this.apnSelections.clear(0).then(function() {
this.ApnConst.APN_TYPES.forEach(function(apnType) {
assert.isTrue(this.mockApnSelections[apnType] == null);
}, this);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
test('The ready promise should be set to null when the field of ' +
'apn.selections is cleared', function(done) {
this.apnSelections._ready().then(function() {
mockMozSettings
.mTriggerObservers(this.ApnConst.APN_SELECTIONS_KEY, null);
assert.equal(this._readyPromise, null);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,205 @@
'use strict';
suite('ApnSettings', function() {
var map = {
'*': {
'modules/apn/apn_utils': 'MockApnUtils',
'modules/settings_cache': 'MockSettingsCache'
}
};
var modules = [
'modules/apn/apn_settings',
'modules/apn/apn_const',
'modules/apn/apn_utils',
'modules/settings_cache'
];
var findTargetSettings = function(apnSettings, serviceId, apnType) {
return apnSettings._apnSettings[serviceId].find(function(apn) {
return apn.types.some((type) => apnType === type);
});
};
setup(function(done) {
var that = this;
var requireCtx = testRequire([], map, function() {});
this.mockSettingsDB = {};
define('MockSettingsCache', function() {
return {
getSettings: function(callback) {
callback(that.mockSettingsDB);
}
};
});
define('MockApnUtils', function() {
return {
clone: function() {},
isMatchedApn: function() {}
};
});
requireCtx(modules,
function(ApnSettings, ApnConst, MockApnUtils, MockSettingsCache) {
this.ApnSettings = ApnSettings;
this.ApnConst = ApnConst;
this.MockApnUtils = MockApnUtils;
this.MockSettingsCache = MockSettingsCache;
this.apnSettings = new this.ApnSettings();
this.mockApnSettings = [
[{ carrier: 'fakeCarrier1', types: ['default'] }],
[{ carrier: 'fakeCarrier2', types: ['mms'] }]
];
this.mockSettingsDB = {};
this.mockSettingsDB[this.ApnConst.APN_SETTINGS_KEY] =
this.mockApnSettings;
done();
}.bind(this));
});
suite('_ready', function() {
test('should init the settings correctly', function(done) {
this.sinon.stub(this.apnSettings, '_addObservers');
this.apnSettings._ready()
.then(function() {
assert.ok(this.apnSettings._isReady);
assert.deepEqual(this.apnSettings._apnSettings, this.mockApnSettings);
sinon.assert.called(this.apnSettings._addObservers);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('_update', function() {
var serviceId = 0;
var apnType = 'default';
test('update with apn', function(done) {
this.sinon.stub(this.MockApnUtils, 'isMatchedApn', function() {
return false;
});
var newApnSetting = { carrier: 'newCarrier', types: [apnType] };
this.apnSettings._update(serviceId, apnType, newApnSetting)
.then(function() {
var targetApnSettings =
findTargetSettings(this.apnSettings, serviceId, apnType);
assert.deepEqual(targetApnSettings, newApnSetting);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('update with null', function(done) {
this.apnSettings._update(serviceId, apnType, null)
.then(function() {
var targetApnSettings =
findTargetSettings(this.apnSettings, serviceId, apnType);
assert.ok(targetApnSettings === undefined);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('getAll', function() {
var serviceId = 0;
test('should return correct apn setting', function(done) {
this.apnSettings.getAll(serviceId)
.then(function(apnSetting) {
assert.deepEqual(apnSetting, this.mockApnSettings[serviceId]);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('get', function() {
var serviceId = 0;
var apnType = 'default';
test('should return correct apn setting', function(done) {
this.apnSettings.get(serviceId, apnType)
.then(function(apnSetting) {
var targetApnSettings =
findTargetSettings(this.apnSettings, serviceId, apnType);
assert.deepEqual(targetApnSettings, apnSetting);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
test('should return null if no existing setting', function(done) {
this.mockSettingsDB[this.ApnConst.APN_SETTINGS_KEY] = {};
this.apnSettings.get(serviceId, apnType)
.then(function(apnSetting) {
assert.isNull(apnSetting);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('update', function() {
var serviceId = 0;
var apnType = 'default';
test('should call to _schedule and _update correctly', function(done) {
var apn = {};
var apnClone = {};
this.sinon.stub(this.MockApnUtils, 'clone', function() {
return apnClone;
});
this.sinon.stub(this.apnSettings, '_commit', function() {
return Promise.resolve();
});
this.sinon.stub(this.apnSettings, '_update', function() {
return Promise.resolve();
});
this.sinon.spy(this.apnSettings, '_schedule');
this.apnSettings.update(serviceId, apnType, apn)
.then(function(apnSetting) {
sinon.assert.calledWith(this.MockApnUtils.clone, apn);
sinon.assert.called(this.apnSettings._schedule);
sinon.assert.calledWith(this.apnSettings._update, serviceId, apnType,
apnClone);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('restore', function() {
var serviceId = 0;
setup(function() {
this.sinon.stub(this.apnSettings, '_schedule', function() {
return Promise.resolve();
});
});
test('should restore to default apn settings correctly', function(done) {
this.apnSettings._apnSettings = [];
this.apnSettings.restore(serviceId)
.then(function() {
assert.deepEqual(this.apnSettings._apnSettings, this.mockApnSettings);
}.bind(this), function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
});

View file

@ -0,0 +1,50 @@
/* global MockGetDeviceStorage */
'use strict';
require('/shared/test/unit/mocks/mock_event_target.js');
require('/shared/test/unit/mocks/mock_dom_request.js');
require('/shared/test/unit/mocks/mock_navigator_getdevicestorage.js');
suite('AppStorage > ', function() {
var AppStorage;
var realDeviceStorage;
suiteSetup(function(done) {
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockGetDeviceStorage;
testRequire(['modules/app_storage'], function(app_storage) {
AppStorage = app_storage;
done();
});
});
suiteTeardown(function() {
navigator.getDeviceStorage = realDeviceStorage;
realDeviceStorage = null;
});
suite('_getSpaceInfo', function() {
setup(function() {
this.sinon.spy(AppStorage._appStorage, 'freeSpace');
this.sinon.spy(AppStorage._appStorage, 'usedSpace');
AppStorage._getSpaceInfo();
});
test('we would call freeSpace and usedSpace in _getSpaceInfo',
function() {
assert.ok(AppStorage._appStorage.freeSpace.called);
var req = AppStorage._appStorage.freeSpace.getCall(0).returnValue;
req.fireSuccess(100);
assert.equal(100, AppStorage.freeSize);
assert.ok(AppStorage._appStorage.usedSpace.called);
var req2 = AppStorage._appStorage.usedSpace.getCall(0).returnValue;
req2.fireSuccess(300);
assert.equal(300, AppStorage.usedSize);
assert.equal(400, AppStorage.totalSize);
assert.equal(75, AppStorage.usedPercentage);
});
});
});

View file

@ -0,0 +1,156 @@
/* global MockMozApps */
'use strict';
requireApp('settings/test/unit/mock_moz_apps.js');
suite('AppsCache', function() {
var modules = [
'modules/apps_cache'
];
var realMozApps;
var appsCache;
suiteSetup(function() {
realMozApps = window.navigator.mozApps;
});
suiteTeardown(function() {
window.navigator.mozApps = realMozApps;
});
setup(function(done) {
window.navigator.mozApps = MockMozApps;
var requireCtx = testRequire([], {}, function() {});
requireCtx(modules, function(AppsCache) {
appsCache = AppsCache;
done();
});
});
suite('apps', function() {
var apps = [
{ manifest: { role : 'homescreen' } },
{ manifest: { role : 'homescreen' } }
];
setup(function() {
this.sinon.stub(appsCache, '_init', function() {
return Promise.resolve();
});
appsCache._apps = apps;
});
test('we can get the same apps back', function(done) {
appsCache.apps().then(function(foundApps) {
assert.equal(foundApps, apps);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('_init', function() {
setup(function() {
this.sinon.stub(appsCache, '_initApps', function() {
return Promise.resolve();
});
this.sinon.stub(appsCache, '_initEvents');
});
test('related functions are executed', function(done) {
appsCache._init().then(function() {
assert.isTrue(appsCache._initApps.called);
assert.isTrue(appsCache._initEvents.called);
}, function() {
// This function does not reject.
assert.isTrue(false);
}).then(done, done);
});
});
suite('_initEvents', function() {
var apps = [
{ manifestURL: 'http://example.org/abc/manifest.webapp' },
{ manifestURL: 'http://example.com/manifest.webapp' },
{ manifestURL: 'http://example.com/manifest.webapp' }
];
setup(function() {
MockMozApps.mgmt.oninstall = this.sinon.spy();
MockMozApps.mgmt.onuninstall = this.sinon.spy();
appsCache._initEvents();
appsCache._apps = apps;
});
test('we will register two necessary events', function() {
assert.equal(MockMozApps.mgmt.oninstall.called);
assert.equal(MockMozApps.mgmt.onuninstall.called);
});
test('install should not add the same app multiple times', function() {
appsCache._apps = [
{ manifestURL: 'http://example.org/abc/manifest.webapp', name: 'Abc' }
];
MockMozApps.mgmt.oninstall({
application: {
manifestURL: 'http://example.org/abc/manifest.webapp',
name: 'Def'
}
});
assert.equal(appsCache._apps.length, 1);
assert.equal(appsCache._apps[0].name, 'Def');
});
test('uninstall should remove all apps with the same manifest', function() {
assert.equal(appsCache._apps.length, 3);
MockMozApps.mgmt.onuninstall({
application: { manifestURL: 'http://example.com/manifest.webapp' }
});
assert.equal(appsCache._apps.length, 1);
assert.equal(appsCache._apps[0].manifestURL,
'http://example.org/abc/manifest.webapp');
});
});
suite('_initApps', function() {
var testApps = [{}, {}];
setup(function() {
MockMozApps.mSetApps(testApps);
});
test('we will get apps', function(done) {
appsCache._initApps().then(function() {
assert.equal(appsCache._apps, testApps);
}).then(done, done);
MockMozApps.mTriggerGetAllAppsCallback();
});
});
suite('addEventListener', function() {
var testFunction = function() {};
setup(function() {
appsCache.addEventListener('oninstall', testFunction);
});
test('we did cache it', function() {
assert.equal(appsCache._eventHandlers.oninstall[0], testFunction);
});
});
suite('removeEventListener', function() {
var testFunction = function() {};
setup(function() {
appsCache._eventHandlers.oninstall.push(testFunction);
appsCache.removeEventListener('oninstall', testFunction);
});
test('we did remove it', function() {
assert.equal(appsCache._eventHandlers.oninstall.length, 0);
});
});
});

View file

@ -0,0 +1,82 @@
'use strict';
suite('DependencyGraph', function() {
suiteSetup(function(done) {
testRequire(['modules/base/dependency_graph'], (DependencyGraph) => {
this.DependencyGraph = DependencyGraph;
done();
});
});
setup(function() {
this.graph = this.DependencyGraph();
});
suite('getAllDependent', function() {
test('a->b, b->c', function() {
this.graph.addDependency('a', 'b');
this.graph.addDependency('b', 'c');
assert.sameMembers(this.graph.getAllDependent('a'), []);
assert.sameMembers(this.graph.getAllDependent('b'), ['a']);
assert.sameMembers(this.graph.getAllDependent('c'), ['a', 'b']);
});
test('b->c, c->a', function() {
this.graph.addDependency('b', 'c');
this.graph.addDependency('a', 'b');
assert.sameMembers(this.graph.getAllDependent('a'), []);
assert.sameMembers(this.graph.getAllDependent('b'), ['a']);
assert.sameMembers(this.graph.getAllDependent('c'), ['a', 'b']);
});
test('a->b, b->c, d->b', function() {
this.graph.addDependency('a', 'b');
this.graph.addDependency('b', 'c');
this.graph.addDependency('d', 'b');
assert.sameMembers(this.graph.getAllDependent('a'), []);
assert.sameMembers(this.graph.getAllDependent('b'), ['a', 'd']);
assert.sameMembers(this.graph.getAllDependent('c'), ['a', 'b', 'd']);
});
test('a->b, b->c, b->d, b->e, d->f, e->f', function() {
this.graph.addDependency('a', 'b');
this.graph.addDependency('b', 'c');
this.graph.addDependency('b', 'd');
this.graph.addDependency('b', 'e');
this.graph.addDependency('d', 'f');
this.graph.addDependency('e', 'f');
assert.sameMembers(this.graph.getAllDependent('a'), []);
assert.sameMembers(this.graph.getAllDependent('b'), ['a']);
assert.sameMembers(this.graph.getAllDependent('c'), ['a', 'b']);
assert.sameMembers(this.graph.getAllDependent('d'), ['a', 'b']);
assert.sameMembers(this.graph.getAllDependent('e'), ['a', 'b']);
assert.sameMembers(this.graph.getAllDependent('f'), ['a', 'b', 'd', 'e']);
});
});
suite('throws when a circular dependency is detected', function() {
test('a->b, b->a', function() {
this.graph.addDependency('a', 'b');
assert.throw(() => {
this.graph.addDependency('b', 'a');
}, Error, 'circular dependency detected! b -> a -> b');
});
test('a->b, c->a, b->c', function() {
this.graph.addDependency('a', 'b');
this.graph.addDependency('c', 'a');
assert.throw(() => {
this.graph.addDependency('b', 'c');
}, Error, 'circular dependency detected! b -> c -> a -> b');
});
test('a->b, c->d, d->a, b->c', function() {
this.graph.addDependency('a', 'b');
this.graph.addDependency('c', 'd');
this.graph.addDependency('d', 'a');
assert.throw(() => {
this.graph.addDependency('b', 'c');
}, Error, 'circular dependency detected! b -> c -> d -> a -> b');
});
});
});

View file

@ -0,0 +1,98 @@
'use strict';
suite('EventEmitter', function() {
suiteSetup(function(done) {
testRequire(['modules/base/event_emitter'], (EventEmitter) => {
this.EventEmitter = EventEmitter;
done();
});
});
suite('constructor', function() {
test('should have an array for each event', function() {
var emitter = this.EventEmitter(['event1', 'event2']);
assert.isArray(emitter._eventListeners.event1);
assert.isArray(emitter._eventListeners.event2);
});
test('should throw when no valid event names', function() {
assert.throw(() => {
this.EventEmitter();
}, Error, 'no valid registered events');
});
});
suite('addEventListener', function() {
setup(function() {
this.emitter = this.EventEmitter(['event1', 'event2']);
});
suite('should call to the listener when emitting the event', function() {
test('register a function', function() {
var listener = sinon.spy();
this.emitter.addEventListener('event1', listener);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledWith(listener, sinon.match({
type: 'event1',
detail: 'value1'
}));
});
test('register an object', function() {
var eventHandler = {
handleEvent: sinon.spy()
};
this.emitter.addEventListener('event1', eventHandler);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledWith(eventHandler.handleEvent, sinon.match({
type: 'event1',
detail: 'value1'
}));
});
test('should only call once when registering multiple times', function() {
var listener = sinon.spy();
this.emitter.addEventListener('event1', listener);
this.emitter.addEventListener('event1', listener);
this.emitter.addEventListener('event1', listener);
this.emitter.addEventListener('event1', listener);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledWith(listener, sinon.match({
type: 'event1',
detail: 'value1'
}));
sinon.assert.calledOnce(listener);
});
});
});
suite('removeEventListener', function() {
setup(function() {
this.emitter = this.EventEmitter(['event1', 'event2']);
});
suite('should not call to the listener when it is removed', function() {
test('the listener is a function', function() {
var listener = sinon.spy();
this.emitter.addEventListener('event1', listener);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledOnce(listener);
this.emitter.removeEventListener('event1', listener);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledOnce(listener);
});
test('the listener is an object', function() {
var eventHandler = {
handleEvent: sinon.spy()
};
this.emitter.addEventListener('event1', eventHandler);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledOnce(eventHandler.handleEvent);
this.emitter.removeEventListener('event1', eventHandler);
this.emitter._emitEvent('event1', 'value1');
sinon.assert.calledOnce(eventHandler.handleEvent);
});
});
});
});

View file

@ -0,0 +1,207 @@
'use strict';
suite('Module', function() {
suiteSetup(function(done) {
testRequire(['modules/base/module'], (Module) => {
this.Module = Module;
done();
});
});
suite('create', function() {
test('constructor is called when instantiation', function() {
var constructor = sinon.spy();
var Module = this.Module.create(constructor);
Module();
assert.ok(constructor.called);
});
test('throws when the constructor is invalid', function() {
assert.throw(function() {
this.Module.create({});
}, Error);
});
test('name of the module is the name of the constructor', function() {
var constructor = function FakeModule() {};
var Module = this.Module.create(constructor);
assert.equal(Module().$name, constructor.name);
});
});
suite('Extendibility', function() {
suite('extend', function() {
var A, B, C;
var instanceOfA, instanceOfB, instanceOfC;
function executeTest() {
test('have individual properties', function() {
// Default values are the same
assert.equal(instanceOfC.propertyA, instanceOfA.propertyA);
assert.equal(instanceOfC.propertyB, instanceOfB.propertyB);
// Check whether they are individual.
instanceOfC.propertyA = 'valueA2';
instanceOfC.propertyB = 'valueB2';
assert.equal(instanceOfA.propertyA, 'valueA');
assert.equal(instanceOfB.propertyB, 'valueB');
});
test('share the same function ', function() {
assert.equal(instanceOfC.funcA, instanceOfA.funcA);
assert.equal(instanceOfC.funcB, instanceOfB.funcB);
});
}
setup(function() {
instanceOfA = A();
instanceOfB = B();
instanceOfC = C();
});
suite('extend from a single module', function() {
suiteSetup(function() {
A = this.Module.create(function() {
this.propertyA = 'valueA';
});
A.prototype.funcA = function() { return this.propertyA; };
B = this.Module.create(function() {
this.super(A).call(this);
this.propertyB = 'valueB';
}).extend(A);
B.prototype.funcB = function() { return this.propertyB; };
C = this.Module.create(function() {
this.super(B).call(this);
}).extend(B);
});
executeTest();
});
suite('extend from multiple modules', function() {
suiteSetup(function() {
A = this.Module.create(function() {
this.propertyA = 'valueA';
});
A.prototype.funcA = function() { return this.propertyA; };
B = this.Module.create(function() {
this.propertyB = 'valueB';
});
B.prototype.funcB = function() { return this.propertyB; };
C = this.Module.create(function() {
this.super(A).call(this);
this.super(B).call(this);
}).extend(A, B);
});
executeTest();
});
});
suite('super', function() {
var ctorA, ctorB;
var A, B;
setup(function() {
ctorA = sinon.spy();
ctorB = sinon.spy();
A = this.Module.create(ctorA);
B = this.Module.create(ctorB).extend(A);
});
test('able to call to the constructors of the extended modules',
function() {
var C = this.Module.create(function() {
this.super(A).call(this);
this.super(B).call(this);
}).extend(A, B);
C();
assert.ok(ctorA.called);
assert.ok(ctorB.called);
});
});
suite('logging', function() {
var module;
function doLog(obj) {
obj.debug('debug');
obj.info('info');
obj.warn('warn');
obj.error('error');
}
setup(function() {
this.sinon.stub(console, 'log');
this.sinon.stub(console, 'info');
this.sinon.stub(console, 'warn');
this.sinon.stub(console, 'error');
var Module = this.Module.create(function A() {});
module = Module();
});
test('NONE', function() {
module._logLevel = this.Module.LOG_LEVEL.NONE;
doLog(module);
assert.ok(console.log.notCalled);
assert.ok(console.info.notCalled);
assert.ok(console.warn.notCalled);
assert.ok(console.error.notCalled);
});
test('DEBUG', function() {
module._logLevel = this.Module.LOG_LEVEL.DEBUG;
doLog(module);
assert.ok(console.log.called);
assert.ok(console.info.notCalled);
assert.ok(console.warn.notCalled);
assert.ok(console.error.notCalled);
});
test('INFO', function() {
module._logLevel = this.Module.LOG_LEVEL.INFO;
doLog(module);
assert.ok(console.log.called);
assert.ok(console.info.called);
assert.ok(console.warn.notCalled);
assert.ok(console.error.notCalled);
});
test('WARN', function() {
module._logLevel = this.Module.LOG_LEVEL.WARN;
doLog(module);
assert.ok(console.log.called);
assert.ok(console.info.called);
assert.ok(console.warn.called);
assert.ok(console.error.notCalled);
});
test('ERROR && ALL', function() {
module._logLevel = this.Module.LOG_LEVEL.ERROR;
doLog(module);
assert.ok(console.log.called);
assert.ok(console.info.called);
assert.ok(console.warn.called);
assert.ok(console.error.called);
module._logLevel = this.Module.LOG_LEVEL.ALL;
doLog(module);
assert.ok(console.log.called);
assert.ok(console.info.called);
assert.ok(console.warn.called);
assert.ok(console.error.called);
});
});
});
});

View file

@ -0,0 +1,135 @@
'use strict';
suite('TaskScheduler >', function() {
setup(function(done) {
testRequire(['modules/base/task_scheduler'], (TaskScheduler) => {
this.taskScheduler = TaskScheduler();
done();
});
});
test('_lock()', function() {
this.taskScheduler._lock();
assert.ok(this.taskScheduler.isLocked,
'we will set internal lock to make sure it\'s locked');
});
test('_unlock()', function() {
this.sinon.stub(this.taskScheduler, '_executeNextTask');
this.taskScheduler._unlock();
assert.ok(this.taskScheduler._executeNextTask.called,
'we will release internal lock');
});
suite('_executeNextTask()', function() {
suite('if isLocked', function() {
setup(function() {
this.sinon.stub(this.taskScheduler._tasks, 'shift');
this.taskScheduler.isLocked = true;
this.taskScheduler._executeNextTask();
});
test('do nothing', function() {
assert.isFalse(this.taskScheduler._tasks.shift.called);
});
});
suite('if not locked, and task is resolved here', function() {
setup(function() {
this.taskScheduler.isLocked = false;
this.sinon.stub(this.taskScheduler, '_unlock');
this.taskScheduler._tasks = [{
func: function() {
return Promise.resolve();
},
defer: {
resolve: Promise.resolve.bind(Promise),
reject: Promise.reject.bind(Promise)
}
}];
});
test('we will do following works', function(done) {
this.taskScheduler._executeNextTask().then(() => {
assert.isTrue(this.taskScheduler._unlock.called);
}).then(done, done);
});
});
suite('if not locked, and task is rejected here', function() {
setup(function() {
this.taskScheduler.isLocked = false;
this.sinon.stub(this.taskScheduler, '_unlock');
this.taskScheduler._tasks = [{
func: function() {
return Promise.reject();
},
defer: {
resolve: Promise.resolve.bind(Promise),
reject: Promise.reject.bind(Promise)
}
}];
});
test('we will do following works', function(done) {
this.taskScheduler._executeNextTask().then(() => {
assert.isTrue(this.taskScheduler._unlock.called);
}).then(done, done);
});
});
});
suite('_removeRedundantTasks()', function() {
test('we will drop same-type tasks', function() {
this.taskScheduler._tasks = [
{ cancelable: true, type: 'type1' },
{ cancelable: true, type: 'type1' },
{ cancelable: true, type: 'type1' },
{ cancelable: true, type: 'type2' }
];
var leftTasks = this.taskScheduler._removeRedundantTasks('type1');
assert.equal(leftTasks.length, 1);
assert.equal(leftTasks[0].type, 'type2');
});
test('we will keep cancelable:false tasks and drop same-type tasks',
function() {
this.taskScheduler._tasks = [
{ cancelable: false, type: 'type1' },
{ cancelable: false, type: 'type1' },
{ cancelable: true, type: 'type1' }, // this will be dropped
{ cancelable: true, type: 'type2' }
];
var leftTasks = this.taskScheduler._removeRedundantTasks('type1');
assert.equal(leftTasks.length, 3);
});
});
suite('enqueue()', function() {
setup(function() {
this.sinon.spy(this.taskScheduler, '_executeNextTask');
this.sinon.spy(this.taskScheduler, '_removeRedundantTasks');
});
suite('if no task.func', function() {
test('we will do nothing', function(done) {
this.taskScheduler.enqueue({}).then(() => {
assert.isFalse(this.taskScheduler._executeNextTask.called);
}).then(done, done);
});
});
suite('if we have task.func', function() {
test('we will do following works', function(done) {
this.taskScheduler.enqueue({
type: 'type1',
func: function() {
return Promise.resolve();
}
}).then(() => {
assert.isTrue(this.taskScheduler._executeNextTask.called);
assert.isTrue(this.taskScheduler._removeRedundantTasks.called);
}).then(done, done);
});
});
});
});

View file

@ -0,0 +1,157 @@
/* global define */
'use strict';
suite('Battery', function() {
var sandbox = sinon.sandbox.create();
var MockPlatformBattery;
var map = {
'modules/battery': {
'modules/navigator/battery': 'MockPlatformBattery'
}
};
// Load a new Batery instance
var loadBattery = function(callback) {
var requireCtx = testRequire([], map, function() {});
define('MockPlatformBattery', function() {
return MockPlatformBattery;
});
requireCtx(['modules/battery'], callback);
};
setup(function() {
MockPlatformBattery = {
charging: null,
level: null,
addEventListener: function() {}
};
});
teardown(function() {
sandbox.restore();
});
suite('return correct values when initialized', function() {
test('charging', function(done) {
MockPlatformBattery.charging = true;
MockPlatformBattery.level = 0.5;
MockPlatformBattery.chargingTime = 13337;
MockPlatformBattery.dischargingTime = Infinity;
loadBattery(function(Battery) {
assert.equal(Battery.level, 50);
assert.equal(Battery.state, 'charging');
assert.equal(Battery.chargingTime, 13337);
assert.equal(Battery.dischargingTime, Infinity);
done();
});
});
test('charged', function(done) {
MockPlatformBattery.charging = true;
MockPlatformBattery.level = 1;
loadBattery(function(Battery) {
assert.equal(Battery.level, 100);
assert.equal(Battery.state, 'charged');
done();
});
});
test('unplugged', function(done) {
MockPlatformBattery.charging = false;
MockPlatformBattery.level = 1;
loadBattery(function(Battery) {
assert.equal(Battery.level, 100);
assert.equal(Battery.state, 'unplugged');
done();
});
});
});
suite('status changes', function() {
test('should reflect level changes', function(done) {
sandbox.spy(MockPlatformBattery, 'addEventListener');
MockPlatformBattery.level = 0.5;
loadBattery(function(Battery) {
assert.equal(Battery.level, 50);
// change the level
MockPlatformBattery.level = 0.51;
// emit the event
MockPlatformBattery.addEventListener.args.some(function(arg) {
if (arg[0] === 'levelchange') {
arg[1].call();
return true;
}
});
assert.equal(Battery.level, 51);
done();
});
});
test('should reflect state changes', function(done) {
sandbox.spy(MockPlatformBattery, 'addEventListener');
MockPlatformBattery.charging = false;
MockPlatformBattery.level = 1;
loadBattery(function(Battery) {
assert.equal(Battery.state, 'unplugged');
// change the state
MockPlatformBattery.charging = true;
// emit the event
MockPlatformBattery.addEventListener.args.some(function(arg) {
if (arg[0] === 'chargingchange') {
arg[1].call();
return true;
}
});
assert.equal(Battery.state, 'charged');
done();
});
});
test('should reflect chargingTime changes', function(done) {
sandbox.spy(MockPlatformBattery, 'addEventListener');
MockPlatformBattery.charging = true;
MockPlatformBattery.level = 10;
MockPlatformBattery.chargingTime = 13337;
loadBattery(function(Battery) {
// change the state
MockPlatformBattery.chargingTime = 13337 - 1337;
// emit the event
MockPlatformBattery.addEventListener.args.some(function(arg) {
if (arg[0] === 'chargingtimechange') {
arg[1].call();
return true;
}
});
assert.equal(Battery.chargingTime, 13337 - 1337);
done();
});
});
test('should reflect dischargingTime changes', function(done) {
sandbox.spy(MockPlatformBattery, 'addEventListener');
MockPlatformBattery.charging = false;
MockPlatformBattery.level = 9;
MockPlatformBattery.dischargingTime = 13337;
loadBattery(function(Battery) {
// change the state
MockPlatformBattery.dischargingTime = 13337 - 1337;
// emit the event
MockPlatformBattery.addEventListener.args.some(function(arg) {
if (arg[0] === 'dischargingtimechange') {
arg[1].call();
return true;
}
});
assert.equal(Battery.dischargingTime, 13337 - 1337);
done();
});
});
});
});

View file

@ -0,0 +1,84 @@
'use strict';
suite('BluetoothAdapterManager', function() {
var adapterManager;
setup(function(done) {
var modules = [
'modules/bluetooth/bluetooth_adapter_manager'
];
var map = {
'modules/bluetooth': {
'modules/navigator/mozBluetooth': 'MockNavigatorBluetooth'
}
};
this.MockNavigatorBluetooth = {
defaultAdapter: {},
callbacks: {
'attributechanged': []
},
addEventListener: function(eventName, callback) {
this.callbacks[eventName].push(callback);
}
};
define('MockNavigatorBluetooth', function() {
return this.MockNavigatorBluetooth;
}.bind(this));
testRequire(modules, map, function(AdapterManager) {
adapterManager = AdapterManager;
done();
});
});
suite('_init > ', function() {
setup(function() {
this.sinon.stub(adapterManager, '_watchMozBluetoothAttributechanged');
this.sinon.stub(adapterManager, '_initDefaultAdapter');
});
test('mozBluetooth attributechanged should be watched, ' +
'defaultAdapter should be inited ', function() {
adapterManager._init();
assert.isTrue(adapterManager._watchMozBluetoothAttributechanged.called);
assert.isTrue(adapterManager._initDefaultAdapter.called);
});
});
suite('_watchMozBluetoothAttributechanged > ', function() {
var eventName, mockEvent;
setup(function() {
this.sinon.spy(this.MockNavigatorBluetooth, 'addEventListener');
eventName = 'attributechanged';
mockEvent = {
attrs: ['defaultAdapter']
};
});
test('"attributechanged" event should be watched ' +
'defaultAdapter should be inited while event is coming ', function() {
adapterManager._watchMozBluetoothAttributechanged();
assert.isTrue(
this.MockNavigatorBluetooth.addEventListener.calledWith(eventName));
// test 'attributechanged' event coming
this.MockNavigatorBluetooth.callbacks[eventName][0](mockEvent);
assert.equal(adapterManager.defaultAdapter,
this.MockNavigatorBluetooth.defaultAdapter);
});
});
suite('_initDefaultAdapter > ', function() {
setup(function() {
adapterManager.defaultAdapter = null;
});
test('defaultAdapter should be defined ', function() {
adapterManager._initDefaultAdapter();
assert.equal(adapterManager.defaultAdapter,
this.MockNavigatorBluetooth.defaultAdapter);
});
});
});

View file

@ -0,0 +1,126 @@
'use strict';
suite('BluetoothClassOfDeviceMapper > ', function() {
setup(function(done) {
var module = [
'modules/bluetooth/bluetooth_cod_mapper'
];
var map = {};
var requireCtx = testRequire([], map, function() {});
requireCtx(module, function(BtClassOfDeviceMapper) {
this.BtClassOfDeviceMapper = BtClassOfDeviceMapper;
done();
}.bind(this));
});
suite('getDeviceType > major device class: computer, phone, audio/video, ' +
'peripheral, imaging > ', function() {
var mockCodInput, expectedType;
suite('mapping in minor device class which is defined ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 4,
majorServiceClass: 555,
minorDeviceClass: 3
};
expectedType = 'audio-card';
});
test('should return device type = "audio-card" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
suite('mapping in minor device class which is reserved ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 4,
majorServiceClass: 555,
minorDeviceClass: 23
};
expectedType = 'audio-card';
});
test('should return device type = "audio-card" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
suite('mapping in minor device class which is not defined ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 6,
majorServiceClass: 555,
minorDeviceClass: 2
};
expectedType = '';
});
test('should return device type = "" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
});
suite('getDeviceType > major device class: network-wireless ', function() {
var mockCodInput, expectedType;
suite('mapping in major device class ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 3,
majorServiceClass: 555,
minorDeviceClass: 10
};
expectedType = 'network-wireless';
});
test('should return device type = "network-wireless" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
});
suite('getDeviceType > major device class: wearable, toy, health > ',
function() {
var mockCodInput, expectedType;
suite('not mapping in major device class, but mapping in ' +
'major service class: audio ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 8,
majorServiceClass: 256,
minorDeviceClass: 3
};
expectedType = 'audio-card';
});
test('should return device type = "audio-card" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
});
suite('getDeviceType > not mapping in any class ', function() {
var mockCodInput, expectedType;
suite('not mapping in any class ', function() {
setup(function() {
mockCodInput = {
majorDeviceClass: 8,
majorServiceClass: 555,
minorDeviceClass: 10
};
expectedType = '';
});
test('should return device type = "" ', function() {
assert.equal(this.BtClassOfDeviceMapper.getDeviceType(mockCodInput),
expectedType);
});
});
});
});

View file

@ -0,0 +1,553 @@
'use strict';
suite('BluetoothContext', function() {
var adapterManager;
var asyncStorage;
var connectionManager;
setup(function(done) {
var modules = [
'modules/bluetooth/bluetooth_adapter_manager',
'shared/async_storage',
'modules/bluetooth/bluetooth_connection_manager'
];
var map = {
'*': {
'modules/bluetooth/bluetooth_adapter_manager': 'MockAdapterManager',
'shared/async_storage': 'MockAsyncStorage'
}
};
this.MockAdapterManager = {
defaultAdapter: {
state: 'enabled',
addEventListener: function() {},
removeEventListener: function() {},
getPairedDevices: function() {return [];},
stopDiscovery: function() {},
connect: function() {},
disconnect: function() {},
getConnectedDevices: function() {}
},
observe: function() {}
};
define('MockAdapterManager', function() {
return this.MockAdapterManager;
}.bind(this));
this.MockAsyncStorage = {
getItem: function() {},
setItem: function() {},
removeItem: function() {}
};
define('MockAsyncStorage', function() {
return this.MockAsyncStorage;
}.bind(this));
testRequire(modules, map, function(AdapterManager, AsyncStorage,
ConnectionManager) {
adapterManager = AdapterManager;
asyncStorage = AsyncStorage;
connectionManager = ConnectionManager;
done();
});
});
suite('Init > ', function() {
setup(function() {
this.sinon.stub(adapterManager, 'observe');
this.sinon.stub(connectionManager, '_onDefaultAdapterChanged');
});
test('AdapterManager "defaultAdapter" property should be observed, ' +
'and access defaultAdapter from AdapterManager manually ', function() {
connectionManager._init();
assert.isTrue(adapterManager.observe.calledWith('defaultAdapter'));
assert.isTrue(connectionManager._onDefaultAdapterChanged.calledWith(
adapterManager.defaultAdapter));
});
});
suite('_onDefaultAdapterChanged > ', function() {
var newAdapter, oldAdapter;
setup(function() {
oldAdapter = {};
connectionManager._defaultAdapter = null;
this.sinon.stub(connectionManager, '_unwatchProfilesStatuschanged');
this.sinon.stub(connectionManager,
'_unwatchDefaultAdapterOnattributechanged');
this.sinon.stub(connectionManager, '_watchProfilesStatuschanged');
this.sinon.stub(connectionManager,
'_watchDefaultAdapterOnattributechanged');
this.sinon.stub(connectionManager, '_restoreConnection');
this.sinon.stub(connectionManager, '_resetConnectionInfo');
});
suite('changed with new/old adapter, ' +
'in case of having new adapter > ', function() {
suiteSetup(function() {
newAdapter = {
state: 'enabled'
};
});
test('should unwatch events from old adapter, ' +
'then watch events from new adapter ', function() {
connectionManager._onDefaultAdapterChanged(newAdapter, oldAdapter);
// test default adapter updated
assert.equal(connectionManager._defaultAdapter, newAdapter);
// test unwatch events
assert.isTrue(
connectionManager._unwatchProfilesStatuschanged.calledWith(
oldAdapter));
assert.isTrue(
connectionManager._unwatchDefaultAdapterOnattributechanged.calledWith(
oldAdapter));
// test watch events
assert.isTrue(
connectionManager._watchProfilesStatuschanged.calledWith(newAdapter));
assert.isTrue(
connectionManager._watchDefaultAdapterOnattributechanged.calledWith(
newAdapter));
// test restore connection
assert.isTrue(connectionManager._restoreConnection.called);
// test reset properties
assert.isFalse(connectionManager._resetConnectionInfo.called);
});
});
suite('changed with new/old adapter, ' +
'in case of new adapter is null > ', function() {
suiteSetup(function() {
newAdapter = null;
});
test('should unwatch events from old adapter, ' +
'then watch events from new adapter ', function() {
connectionManager._onDefaultAdapterChanged(newAdapter, oldAdapter);
// test default adapter updated
assert.isNull(connectionManager._defaultAdapter);
// test unwatch events
assert.isTrue(
connectionManager._unwatchProfilesStatuschanged.calledWith(
oldAdapter));
assert.isTrue(
connectionManager._unwatchDefaultAdapterOnattributechanged.calledWith(
oldAdapter));
// test watch events
assert.isFalse(
connectionManager._watchProfilesStatuschanged.calledWith(newAdapter));
assert.isFalse(
connectionManager._watchDefaultAdapterOnattributechanged.calledWith(
newAdapter));
// test restore connection
assert.isFalse(connectionManager._restoreConnection.called);
// test reset properties
assert.isTrue(connectionManager._resetConnectionInfo.called);
});
});
});
suite('_initConnectedDevicesInfo > ', function() {
suite('has default adapter, init connected devices info ', function() {
var mockAdapter, mockGetConnectedDevicesFromPlatformPromise,
mockConnectedDevicesByProfile;
setup(function() {
mockAdapter = {};
connectionManager._defaultAdapter = mockAdapter;
mockConnectedDevicesByProfile = {};
mockGetConnectedDevicesFromPlatformPromise =
Promise.resolve(mockConnectedDevicesByProfile);
this.sinon.stub(connectionManager,
'_getConnectedDevicesFromPlatform').returns(
mockGetConnectedDevicesFromPlatformPromise);
this.sinon.stub(connectionManager, '_constructDeviceItemsMap');
});
test('_constructDeviceItemsMap() should be called while ' +
'_getConnectedDevicesFromPlatform is resolved ',
function(done) {
connectionManager._initConnectedDevicesInfo().then(function() {
assert.isTrue(
connectionManager._getConnectedDevicesFromPlatform.called);
mockGetConnectedDevicesFromPlatformPromise.then(() => {
assert.isTrue(
connectionManager._constructDeviceItemsMap.calledWith(
mockConnectedDevicesByProfile));
}, () => {
//reject case
});
}, function() {
// reject case
}).then(done, done);
});
});
suite('has no default adapter, init connected devices info ', function() {
setup(function() {
connectionManager._defaultAdapter = null;
this.sinon.stub(connectionManager, '_getConnectedDevicesFromPlatform');
this.sinon.stub(connectionManager, '_constructDeviceItemsMap');
});
test('_getConnectedDevicesFromPlatform() should not be called, ' +
'_constructDeviceItemsMap() should not be called',
function(done) {
connectionManager._initConnectedDevicesInfo().then(function() {
// resolve case
}, function() {
assert.isFalse(
connectionManager._getConnectedDevicesFromPlatform.called);
assert.isFalse(
connectionManager._constructDeviceItemsMap.called);
}).then(done, done);
});
});
});
suite('_constructDeviceItemsMap > ', function() {
var mockConnectedDevices, expectedConectionDeviceInfo;
setup(function() {
mockConnectedDevices = {
'hfp': [{address: 'AA:BB:CC:00:11:22'}],
'a2dp': [{address: 'AA:BB:CC:00:11:22'}]
};
expectedConectionDeviceInfo = {
address: mockConnectedDevices.hfp[0].address,
connected: true,
profileID: 'hfp',
device: mockConnectedDevices.hfp[0]
};
this.sinon.stub(connectionManager, '_initConnectedDevicesCache');
});
test('_initConnectedDevicesCache should be called ', function() {
connectionManager._constructDeviceItemsMap(mockConnectedDevices);
assert.isTrue(connectionManager._initConnectedDevicesCache.called);
assert.deepEqual(connectionManager._initConnectedDevicesCache.args[0][0],
expectedConectionDeviceInfo);
});
});
suite('_initConnectedDevicesCache > ', function() {
var mockConnectedDevicesInfo, options;
suite('the updating device has info > ', function() {
setup(function() {
mockConnectedDevicesInfo = {
'AA:BB:CC:00:11:22': {
'device': {},
'connectedProfiles': {
'hfp': false,
'a2dp': true
}
}
};
options = {
address: 'AA:BB:CC:00:11:22',
connected: true,
profileID: 'hfp',
device: {}
};
connectionManager._connectedDevicesInfo = mockConnectedDevicesInfo;
this.sinon.stub(connectionManager,
'_hasConnectedProfileByAddress').returns(true);
});
test('the specific profile of connected device info should be ' +
'updated, Ex: "hfp": true ', function() {
var newCache = connectionManager._initConnectedDevicesCache(options);
assert.isTrue(newCache[options.address].connectedProfiles.hfp);
});
});
suite('the updating device has no info > ', function() {
setup(function() {
mockConnectedDevicesInfo = {};
options = {
address: 'AA:BB:CC:00:11:22',
connected: true,
profileID: 'hfp',
device: {}
};
connectionManager._connectedDevicesInfo = mockConnectedDevicesInfo;
this.sinon.stub(connectionManager,
'_hasConnectedProfileByAddress').returns(true);
});
test('the specific profile of connected device info should be ' +
'updated, Ex: "hfp": true, "device": options.device ', function() {
var newCache = connectionManager._initConnectedDevicesCache(options);
assert.isTrue(newCache[options.address].connectedProfiles.hfp);
assert.equal(newCache[options.address].device, options.device);
});
});
suite('the updating device has no connected profile > ', function() {
setup(function() {
mockConnectedDevicesInfo = {
'AA:BB:CC:00:11:22': {
'device': {},
'connectedProfiles': {
'hfp': true,
'a2dp': false
}
}
};
options = {
address: 'AA:BB:CC:00:11:22',
connected: false,
profileID: 'hfp',
device: {}
};
connectionManager._connectedDevicesInfo = mockConnectedDevicesInfo;
this.sinon.stub(connectionManager,
'_hasConnectedProfileByAddress').returns(false);
});
test('the device info should be removed from cache ', function() {
var newCache = connectionManager._initConnectedDevicesCache(options);
assert.isUndefined(newCache[options.address]);
});
});
});
suite('getConnectedDevices > ', function() {
suite('already requested getConnectedDevices() with promise > ',
function() {
var mockPromise;
setup(function() {
mockPromise = new Promise(function() {});
connectionManager._getConnectedDevicesPromise = mockPromise;
});
test('should return the requested promise..', function() {
assert.equal(connectionManager.getConnectedDevices(), mockPromise);
});
});
suite('have not requested getConnectedDevices() with promise > ',
function() {
var mockConnectedDevicesInfoPromise, mockConnectedDevicesInfo;
setup(function() {
connectionManager._getConnectedDevicesPromise = null;
mockConnectedDevicesInfo = {'AA:BB:CC:00:11:22': {}};
connectionManager._connectedDevicesInfo = mockConnectedDevicesInfo;
mockConnectedDevicesInfoPromise = Promise.resolve();
this.sinon.stub(connectionManager,
'_initConnectedDevicesInfo').returns(mockConnectedDevicesInfoPromise);
});
test('should return connected device cached while promise resolve ',
function(done) {
connectionManager.getConnectedDevices().then((connectedDevicesInfo) => {
assert.equal(connectedDevicesInfo, mockConnectedDevicesInfo);
}, () => {
// reject case
}).then(done, done);
});
});
suite('have not requested getConnectedDevices() with promise > ',
function() {
var mockConnectedDevicesInfoPromise;
setup(function() {
connectionManager._getConnectedDevicesPromise = null;
mockConnectedDevicesInfoPromise = Promise.reject();
this.sinon.stub(connectionManager,
'_initConnectedDevicesInfo').returns(mockConnectedDevicesInfoPromise);
});
test('should clean up instance of cache while promise reject ',
function(done) {
connectionManager.getConnectedDevices().then(() => {
assert.isNull(connectionManager._getConnectedDevicesPromise);
}, () => {
// reject case
}).then(done, done);
});
});
});
suite('_resetConnectionInfo > ', function() {
setup(function() {
connectionManager.connectingAddress = 'AA:BB:CC:00:11:22';
connectionManager._getConnectedDevicesPromise = Promise.resolve();
});
test('connecting address, instance of cache should be reset ', function() {
connectionManager._resetConnectionInfo();
assert.isNull(connectionManager.connectingAddress);
assert.isNull(connectionManager._getConnectedDevicesPromise);
});
});
suite('_watchDefaultAdapterOnattributechanged > ', function() {
var mockAdapter;
setup(function() {
mockAdapter = {
addEventListener: function() {}
};
this.sinon.stub(mockAdapter, 'addEventListener');
this.sinon.stub(connectionManager, '_onAdapterAttributeChanged');
});
test('attributechanged should be registered callback function', function() {
connectionManager._watchDefaultAdapterOnattributechanged(mockAdapter);
assert.equal(mockAdapter.addEventListener.args[0][0], 'attributechanged');
mockAdapter.addEventListener.args[0][1]();
assert.isTrue(connectionManager._onAdapterAttributeChanged.calledWith(
mockAdapter));
});
});
suite('_unwatchDefaultAdapterOnattributechanged > ', function() {
var mockAdapter;
setup(function() {
mockAdapter = {
removeEventListener: function() {}
};
this.sinon.stub(mockAdapter, 'removeEventListener');
this.sinon.stub(connectionManager, '_onAdapterAttributeChanged');
});
test('onattributechanged should be unregistered ', function() {
connectionManager._unwatchDefaultAdapterOnattributechanged(mockAdapter);
assert.equal(mockAdapter.removeEventListener.args[0][0],
'attributechanged');
assert.equal(mockAdapter.removeEventListener.args[0][1],
connectionManager._onAdapterAttributeChanged);
});
});
suite('_onAdapterAttributeChanged > ', function() {
var mockAdapter, evt;
setup(function() {
mockAdapter = {
state: 'enabled'
};
});
suite('state changed in enabled ', function() {
setup(function() {
evt = {
attrs: ['state']
};
this.sinon.stub(connectionManager, '_restoreConnection');
});
test('_restoreConnection() should be called ', function() {
connectionManager._onAdapterAttributeChanged(mockAdapter, evt);
assert.isTrue(connectionManager._restoreConnection.called);
});
['enabling', 'disabling', 'disabled'].forEach(function(state) {
test('_restoreConnection() should not be called when ' + state,
function() {
mockAdapter.state = state;
connectionManager._onAdapterAttributeChanged(mockAdapter, evt);
assert.isTrue(connectionManager._restoreConnection.notCalled);
});
});
});
});
suite('_watchProfilesStatuschanged > ', function() {
var mockAdapter;
setup(function() {
mockAdapter = {};
});
test('profile status callback of adapter should be registered', function() {
connectionManager._watchProfilesStatuschanged(mockAdapter);
assert.isDefined(mockAdapter.onhfpstatuschanged);
assert.isDefined(mockAdapter.ona2dpstatuschanged);
});
});
suite('_unwatchProfilesStatuschanged > ', function() {
var mockAdapter;
setup(function() {
mockAdapter = {
onhfpstatuschanged: function() {},
ona2dpstatuschanged: function() {}
};
});
test('profile status callback of adapter should be null ', function() {
connectionManager._unwatchProfilesStatuschanged(mockAdapter);
assert.isNull(mockAdapter.onhfpstatuschanged);
assert.isNull(mockAdapter.ona2dpstatuschanged);
});
});
suite('_onProfileStatuschangeHandler > ', function() {
var mockProfileID, mockEvt, resultOptions;
setup(function() {
mockProfileID = 'hfp';
mockEvt = {
address: 'AA:BB:CC:00:11:22',
status: true
};
resultOptions = {
address: mockEvt.address,
connected: mockEvt.status,
profileID: mockProfileID
};
this.sinon.stub(connectionManager, '_updateConnectionStatus');
});
test('_updateConnectionStatus() should be called with options', function() {
connectionManager._onProfileStatuschangeHandler(mockProfileID, mockEvt);
assert.isTrue(
connectionManager._updateConnectionStatus.calledWith(resultOptions));
});
});
suite('_recordConnection > ', function() {
var action, address;
var itemKey = 'device.connected';
suite('action: set > ', function() {
setup(function() {
action = 'set';
address = 'AA:BB:CC:00:11:22';
this.sinon.stub(asyncStorage, 'setItem');
this.sinon.stub(asyncStorage, 'removeItem');
});
test('should set item in async storage ', function() {
connectionManager._recordConnection(action, address);
assert.equal(asyncStorage.setItem.args[0][0], itemKey);
assert.equal(asyncStorage.setItem.args[0][1], address);
assert.isFalse(asyncStorage.removeItem.called);
});
});
suite('action: remove > ', function() {
var mockAdapter;
setup(function() {
action = 'remove';
address = 'AA:BB:CC:00:11:22';
mockAdapter = {
state: 'enabled'
};
connectionManager._defaultAdapter = mockAdapter;
this.sinon.stub(asyncStorage, 'setItem');
this.sinon.stub(asyncStorage, 'removeItem');
});
test('should remove item in async storage ', function() {
connectionManager._recordConnection(action, address);
assert.isTrue(asyncStorage.removeItem.calledWith(itemKey));
assert.isFalse(asyncStorage.setItem.called);
});
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,195 @@
'use strict';
suite('BluetoothDevice', function() {
var bluetoothDevice, mockBtClassOfDeviceMapper, mockDeviceObj,
defaultConnectionStatus;
setup(function(done) {
var modules = [
'modules/bluetooth/bluetooth_device',
'modules/bluetooth/bluetooth_cod_mapper'
];
var map = {
'modules/bluetooth': {
'modules/bluetooth/bluetooth_cod_mapper': 'MockBtClassOfDeviceMapper'
}
};
mockBtClassOfDeviceMapper = {
getDeviceType: function() {return '';}
};
define('MockBtClassOfDeviceMapper', function() {
return mockBtClassOfDeviceMapper;
});
mockDeviceObj = {
onattributechanged: {},
name: 'device-01',
paired: false,
address: 'AA:BB:CC:00:11:22',
cod: {}
};
defaultConnectionStatus = 'disconnected';
testRequire(modules, map, function(BluetoothDevice) {
bluetoothDevice = BluetoothDevice;
done();
});
});
suite('ctor > ', function() {
var bluetoothDeviceCreated;
setup(function() {
bluetoothDeviceCreated = bluetoothDevice(mockDeviceObj);
});
suite('create > ', function() {
test('Will create BluetoothDevice with "name", "paired", ' +
'"address", "type", "connectionStatus", "profiles", ' +
'and "descriptionText" properties. Define getter "data", ' +
'function "updateConnectionInfo", "updateDescriptionText".',
function() {
assert.equal(bluetoothDeviceCreated.name, mockDeviceObj.name);
assert.equal(bluetoothDeviceCreated.paired, mockDeviceObj.paired);
assert.equal(bluetoothDeviceCreated.address, mockDeviceObj.address);
assert.isDefined(bluetoothDeviceCreated.type);
assert.equal(bluetoothDeviceCreated.connectionStatus,
defaultConnectionStatus);
assert.isNull(bluetoothDeviceCreated.profiles);
assert.isDefined(bluetoothDeviceCreated.descriptionText);
assert.isDefined(bluetoothDeviceCreated.data);
assert.isDefined(bluetoothDeviceCreated.updateConnectionInfo);
assert.isDefined(bluetoothDeviceCreated.updateDescriptionText);
});
});
suite('updateConnectionInfo > ', function() {
var mockOptions;
setup(function() {
mockOptions = {
connectionStatus: 'connected',
profiles: {
'hfp': true,
'a2dp': false
}
};
});
test('"connectionStatus" property should be "connected"', function() {
bluetoothDeviceCreated.updateConnectionInfo(mockOptions);
assert.equal(bluetoothDeviceCreated.connectionStatus,
mockOptions.connectionStatus);
assert.deepEqual(bluetoothDeviceCreated.profiles,
mockOptions.profiles);
});
});
suite('updateDescriptionText > ', function() {
suite('"paired" property changed > ', function() {
var newPairedState;
setup(function() {
newPairedState = 'pairing';
bluetoothDeviceCreated.paired = newPairedState;
});
test('"descriptionText" property should be "pairing" ', function() {
assert.equal(bluetoothDeviceCreated.descriptionText, newPairedState);
});
});
suite('"connectionStatus" property changed > ', function() {
var newConnectionStatus;
setup(function() {
newConnectionStatus = 'connecting';
bluetoothDeviceCreated.paired = true;
bluetoothDeviceCreated.connectionStatus = newConnectionStatus;
});
test('"descriptionText" property should be "connecting" ', function() {
assert.equal(bluetoothDeviceCreated.descriptionText,
newConnectionStatus);
});
});
suite('"profiles" property changed > ', function() {
var newProfiles, expectedDescriptionText;
setup(function() {
newProfiles = {
'hfp': true,
'a2dp': true
};
expectedDescriptionText = 'connectedWithDeviceMedia';
bluetoothDeviceCreated.paired = true;
bluetoothDeviceCreated.connectionStatus = 'connected';
bluetoothDeviceCreated.profiles = newProfiles;
});
test('"descriptionText" property should be "connectedWithDeviceMedia" ',
function() {
assert.equal(bluetoothDeviceCreated.descriptionText,
expectedDescriptionText);
});
});
});
suite('event > name ', function() {
var mockEvt, newName;
setup(function() {
mockEvt = {
attrs: ['name']
};
newName = 'device-02';
mockDeviceObj.name = newName;
});
test('"onattributechanged" event coming with ' +
'name property changed ', function() {
mockDeviceObj.onattributechanged(mockEvt);
assert.equal(bluetoothDeviceCreated.name, newName);
});
});
suite('event > paired ', function() {
var mockEvt, newPairedState;
setup(function() {
mockEvt = {
attrs: ['paired']
};
newPairedState = true;
mockDeviceObj.paired = newPairedState;
});
test('"onattributechanged" event coming with ' +
'paired property changed ', function() {
mockDeviceObj.onattributechanged(mockEvt);
assert.equal(bluetoothDeviceCreated.paired, newPairedState);
});
});
suite('event > cod ', function() {
var mockEvt, newCODObject, mockType;
setup(function() {
mockEvt = {
attrs: ['cod']
};
mockType = 'audio-card';
newCODObject = {};
mockDeviceObj.cod = newCODObject;
this.sinon.stub(mockBtClassOfDeviceMapper, 'getDeviceType').returns(
mockType);
});
test('"onattributechanged" event coming with ' +
'cod property changed ', function() {
mockDeviceObj.onattributechanged(mockEvt);
assert.isTrue(mockBtClassOfDeviceMapper.getDeviceType.calledWith(
newCODObject));
assert.equal(bluetoothDeviceCreated.type, mockType);
});
});
});
});

View file

@ -0,0 +1,140 @@
'use strict';
suite('Date & Time > ', function() {
var realSettings, realTime;
var DateTime;
// keys
var _kClockAutoEnabled = 'time.clock.automatic-update.enabled';
var _kClockAutoAvailable = 'time.clock.automatic-update.available';
var _kTimezoneAutoAvailable = 'time.timezone.automatic-update.available';
var _kTimezone = 'time.timezone';
var _kUserSelected = 'time.timezone.user-selected';
suiteSetup(function(done) {
var modules = [
'shared_mocks/mock_navigator_moz_settings',
'modules/date_time'
];
var maps = {
'*': {}
};
testRequire(modules, maps, function(
MockNavigatorSettings, module) {
// mock settings
realSettings = window.navigator.mozSettings;
window.navigator.mozSettings = MockNavigatorSettings;
// mock time
var MockTime = {
set: function() {}
};
realTime = window.navigator.mozTime;
window.navigator.mozTime = MockTime;
DateTime = module;
done();
});
});
suiteTeardown(function() {
window.navigator.mozTime = realTime;
window.navigator.mozSettings = realSettings;
});
suite('initiation', function() {
setup(function() {
this.sinon.stub(DateTime, '_attachListeners');
this.sinon.stub(DateTime, '_getDefaults');
DateTime._init();
});
test('_attachListeners and _getDefaults are called ' +
'while get the instance', function() {
assert.ok(DateTime._attachListeners.called);
assert.ok(DateTime._getDefaults.called);
});
});
suite('attach listeners', function() {
setup(function() {
this.sinon.spy(window.navigator.mozSettings, 'addObserver');
DateTime._attachListeners();
});
test('settings value observed', function() {
assert.ok(window.navigator.mozSettings.addObserver.calledWith(
_kClockAutoEnabled));
assert.ok(window.navigator.mozSettings.addObserver.calledWith(
_kClockAutoAvailable));
assert.ok(window.navigator.mozSettings.addObserver.calledWith(
_kTimezoneAutoAvailable));
assert.ok(window.navigator.mozSettings.addObserver.calledWith(
_kTimezone));
assert.ok(window.navigator.mozSettings.addObserver.calledWith(
_kUserSelected));
});
});
suite('Change system time', function() {
setup(function() {
this.sinon.stub(DateTime._mozTime, 'set');
});
test('Set Date', function() {
DateTime.setTime('date', '2013-05-21');
var firstArg = DateTime._mozTime.set.firstCall.args[0];
assert.ok(firstArg.getYear(), 2013);
assert.ok(firstArg.getMonth(), 5 - 1);
assert.ok(firstArg.getDate(), 21);
});
test('Set clock', function() {
DateTime.setTime('time', '9:12');
var firstArg = DateTime._mozTime.set.firstCall.args[0];
assert.ok(firstArg.getHours(), 9);
assert.ok(firstArg.getMinutes(), 12);
});
});
suite('Date and time autoUpdate', function() {
setup(function() {
this.sinon.stub(DateTime, '_autoUpdateDate');
this.sinon.stub(DateTime, '_autoUpdateTime');
});
test('Auto Update Date and time', function() {
DateTime._autoUpdateDateTime();
assert.ok(DateTime._autoUpdateDate.called);
assert.ok(DateTime._autoUpdateTime.called);
});
});
suite('Date autoUpdate', function() {
setup(function() {
this.sinon.spy(DateTime, '_autoUpdateDate');
this.sinon.stub(window, 'setTimeout');
});
test('Auto Update Date and time', function() {
DateTime._autoUpdateDate();
assert.ok(window.setTimeout.called);
assert.ok(DateTime._autoUpdateDate.called);
});
});
suite('Time autoUpdate', function() {
setup(function() {
this.sinon.spy(DateTime, '_autoUpdateTime');
this.sinon.stub(window, 'setTimeout');
});
test('Auto Update Date and time', function() {
DateTime._autoUpdateTime();
assert.ok(window.setTimeout.called);
assert.ok(DateTime._autoUpdateTime.called);
});
});
});

View file

@ -0,0 +1,40 @@
'use strict';
suite('Defer > ', function() {
var defer;
setup(function(done) {
testRequire([
'modules/defer'
], {}, function(Defer) {
defer = Defer();
done();
});
});
test('with promise property', function() {
assert.isTrue(defer.promise instanceof Promise);
});
test('with resolve method', function(done) {
var message = 'Resolved';
defer.resolve(message);
Promise.resolve(defer.promise).then(function(resolvedMessage) {
assert.equal(message, resolvedMessage);
}, function() {
// This will never be called
assert.isTrue(false);
}).then(done, done);
});
test('with reject method', function(done) {
var message = 'Rejected';
defer.reject(message);
Promise.resolve(defer.promise).then(function() {
// This will never be called
assert.isTrue(false);
}, function(rejectedMessage) {
assert.equal(message, rejectedMessage);
}).then(done, done);
});
});

View file

@ -0,0 +1,95 @@
'use strict';
suite('BaseDialog > ', function() {
var baseDialog;
setup(function(done) {
var requireCtx = testRequire([], {}, function() {});
requireCtx([
'modules/dialog/base_dialog'
], function(BaseDialog) {
var panelDOM = document.createElement('div');
baseDialog = new BaseDialog(panelDOM, {});
done();
});
});
suite('_getWrapL10nObject > ', function() {
test('input is string', function() {
var fakeL10nId = 'Fake';
var result = baseDialog._getWrapL10nObject(fakeL10nId);
assert.equal(typeof result, 'object');
assert.equal(result.id, fakeL10nId);
assert.isNull(result.args);
});
test('input is object, but forgot to put id', function() {
this.sinon.spy(baseDialog, '_getWrapL10nObject');
var fakeL10nObject = {args: {}};
try {
baseDialog._getWrapL10nObject(fakeL10nObject);
} catch(e) {
assert.ok(true, 'we wil throw error');
}
});
test('input is object, but no args', function() {
var fakeL10nObject = {id: 'Fake'};
var result = baseDialog._getWrapL10nObject(fakeL10nObject);
assert.equal(result.id, fakeL10nObject.id);
assert.isNull(result.args, 'we will set null on it');
});
test('input is object, with id and args', function() {
var fakeL10nObject = {id: 'Fake', args: {}};
var result = baseDialog._getWrapL10nObject(fakeL10nObject);
assert.equal(result.id, fakeL10nObject.id);
assert.equal(result.args, fakeL10nObject.args);
});
});
suite('cleanup > ', function() {
setup(function() {
this.sinon.stub(baseDialog, '_updateTitle');
this.sinon.stub(baseDialog, '_updateSubmitButton');
this.sinon.stub(baseDialog, '_updateCancelButton');
this.sinon.stub(baseDialog.panel.classList, 'remove');
});
suite('if this is custom dialog', function() {
setup(function() {
baseDialog.DIALOG_CLASS = 'panel-dialog';
baseDialog.cleanup();
});
test('we would not restore its title and button', function() {
assert.isFalse(baseDialog._updateTitle.called);
assert.isFalse(baseDialog._updateSubmitButton.called);
assert.isFalse(baseDialog._updateCancelButton.called);
assert.equal(baseDialog.panel.classList.remove.getCall(0).args[0],
baseDialog.DIALOG_CLASS);
assert.equal(baseDialog.panel.classList.remove.getCall(1).args[0],
baseDialog.TRANSITION_CLASS);
});
});
suite('if this is system-wise dialog', function() {
setup(function() {
baseDialog.DIALOG_CLASS = 'alert-dialog';
baseDialog.cleanup();
});
test('we would restore its title and button', function() {
assert.isTrue(baseDialog._updateTitle.called);
assert.isTrue(baseDialog._updateSubmitButton.called);
assert.isTrue(baseDialog._updateCancelButton.called);
assert.equal(baseDialog.panel.classList.remove.getCall(0).args[0],
baseDialog.DIALOG_CLASS);
assert.equal(baseDialog.panel.classList.remove.getCall(1).args[0],
baseDialog.TRANSITION_CLASS);
});
});
});
});

View file

@ -0,0 +1,217 @@
suite('DialogManager > ', function() {
'use strict';
var mockLazyLoader;
var mockPanelCache;
var dialogManager;
var map = {
'*': {
'modules/panel_cache': 'PanelCache',
'shared/lazy_loader': 'shared_mocks/mock_lazy_loader',
}
};
setup(function(done) {
define('PanelCache', function() {
return {
reset: function() {},
get: function() {}
};
});
testRequire([
'PanelCache',
'shared_mocks/mock_lazy_loader',
'modules/dialog_manager'
], map, function(MockPanelCache, MockLazyLoader, DialogManager) {
mockPanelCache = MockPanelCache;
mockLazyLoader = MockLazyLoader;
dialogManager = DialogManager;
done();
});
});
suite('_transit', function() {
var panel;
var dialog;
var clock;
setup(function() {
clock = this.sinon.useFakeTimers();
panel = document.createElement('div');
dialog = {
panel: panel
};
this.sinon.spy(panel, 'addEventListener');
this.sinon.spy(panel, 'removeEventListener');
});
test('to open', function(done) {
dialogManager._transit('open', dialog).then(function() {
clock.tick(150);
assert.isTrue(dialog.panel.classList.contains('current'));
assert.isTrue(dialog.panel.removeEventListener.called);
}).then(done, done);
panel.addEventListener.getCall(0).args[1]({
propertyName: 'visibility'
});
});
test('to close', function(done) {
dialogManager._transit('close', dialog).then(function() {
clock.tick(150);
assert.isFalse(dialog.panel.classList.contains('current'));
assert.isTrue(dialog.panel.removeEventListener.called);
}).then(done, done);
panel.addEventListener.getCall(0).args[1]({
propertyName: 'visibility'
});
});
});
suite('_open', function() {
var dialog;
var panel;
setup(function() {
panel = {
beforeShow: function() {
return Promise.resolve();
},
show: function() {
return Promise.resolve();
}
};
dialog = {
init: this.sinon.stub(),
initUI: this.sinon.stub(),
bindEvents: this.sinon.stub(),
panel: panel
};
this.sinon.stub(dialogManager, '_loadPanel', function() {
return Promise.resolve();
});
this.sinon.stub(dialogManager, '_getPanel', function() {
return panel;
});
this.sinon.stub(dialogManager, '_transit', function() {
return Promise.resolve();
});
});
test('we would follow the calling sequence', function(done) {
dialogManager._open(dialog).then(function() {
assert.isTrue(dialog.initUI.called);
assert.isTrue(dialog.bindEvents.called);
}).then(done, done);
});
});
suite('_close', function() {
var dialog;
var panel;
var options;
var result;
setup(function() {
options = {
_type: 'submit'
};
panel = {
beforeHide: function() {
return Promise.resolve();
},
onSubmit: function() {
return Promise.resolve(result);
},
onCancel: function() {
return Promise.resolve(result);
},
hide: function() {
return Promise.resolve();
}
};
dialog = {
panel: panel,
cleanup: this.sinon.stub(),
getResult: function() {
return result;
},
};
this.sinon.stub(dialogManager, '_loadPanel', function() {
return Promise.resolve();
});
this.sinon.stub(dialogManager, '_getPanel', function() {
return panel;
});
this.sinon.stub(dialogManager, '_transit', function() {
return Promise.resolve();
});
});
test('we would follow the calling sequence', function(done) {
dialogManager._close(dialog, options).then(function() {
assert.isTrue(dialog.cleanup.called);
}).then(done, done);
});
test('if validation failed, we would call reject', function(done) {
panel.onSubmit = function() {
return Promise.reject();
};
dialogManager._close(dialog, options).then(function() {
// This should not be called
assert.isTrue(false);
}, function() {
// we will get rejected here
assert.isTrue(true);
}).then(done, done);
});
test('if dialog is not a prompt, we would get result from hide',
function(done) {
result = 'This is a result from panel.hide()';
dialogManager._close(dialog, options).then(function(gotResult) {
assert.equal(gotResult, result);
}).then(done, done);
});
test('if dialog is a prompt, we would get result', function(done) {
dialog.DIALOG_CLASS = 'prompt-dialog';
result = 'This is a result from dialog.getResult()';
dialogManager._close(dialog, options).then(function(gotResult) {
assert.equal(gotResult, result);
}).then(done, done);
});
test('if submit button is clicked, onSubmit will get called',
function(done) {
options._type = 'submit';
this.sinon.spy(panel, 'onSubmit');
dialogManager._close(dialog, options).then(function() {
assert.ok(panel.onSubmit.called);
}).then(done, done);
});
test('if cancel button is clicked, onCancel will get called',
function(done) {
options._type = 'cancel';
this.sinon.spy(panel, 'onCancel');
dialogManager._close(dialog, options).then(function() {
assert.ok(panel.onCancel.called);
}).then(done, done);
});
});
});

View file

@ -0,0 +1,177 @@
suite('DialogService > ', function() {
'use strict';
var mockSettings;
var mockDialogManager;
var dialogService;
var modules = [
'unit/mock_settings',
'DialogManager',
'modules/dialog_service'
];
var map = {
'*': {
'modules/dialog/panel_dialog': 'Panel',
'modules/dialog/alert_dialog': 'Panel',
'modules/dialog/confirm_dialog': 'Panel',
'modules/dialog/prompt_dialog': 'Panel',
'modules/defer': 'Defer',
'modules/dialog_manager': 'DialogManager',
'settings': 'unit/mock_settings'
}
};
setup(function(done) {
define('Panel', function() {
return function() {
return {};
};
});
define('Defer', function() {
return function() {
var defer = {};
defer.promise = new Promise(function(resolve, reject) {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
};
});
define('DialogManager', function() {
return {
open: function() {},
close: function() {}
};
});
var requireCtx = testRequire([], map, function() {});
requireCtx(modules,
function(MockSettings, MockDialogManager, DialogService) {
mockSettings = MockSettings;
mockDialogManager = MockDialogManager;
dialogService = DialogService;
done();
});
});
suite('show', function() {
var panelId;
var userOptions;
setup(function() {
panelId = 'fakePanelId';
userOptions = {};
});
suite('when navigating', function() {
setup(function() {
dialogService._navigating = true;
Promise.resolve(dialogService.show(panelId, userOptions));
});
test('We will catch the defer object', function() {
var pendingRequest = dialogService._pendingRequests.pop();
assert.equal(pendingRequest.panelId, panelId);
assert.equal(pendingRequest.userOptions, userOptions);
});
});
suite('not navigating, but there is already one same dialog', function() {
setup(function() {
mockSettings.currentPanel = '#' + panelId;
dialogService._navigating = false;
});
test('We will reject directly', function(done) {
dialogService.show(panelId, userOptions)
.then(function() {
// this line should not be executed
assert.isTrue(false);
}, function(reason) {
assert.equal(reason, 'You are showing the same panel #' + panelId);
}).then(done, done);
});
});
suite('not navigating, would open dialog', function() {
setup(function() {
this.sinon.stub(document, 'getElementById', function() {
return document.createElement('div');
});
this.sinon.stub(mockDialogManager, 'open');
this.sinon.stub(mockDialogManager, 'close');
dialogService._navigating = false;
dialogService.show(panelId, userOptions);
});
test('will do following works', function() {
assert.isTrue(dialogService._navigating);
assert.isTrue(mockDialogManager.open.called);
});
});
suite('not navigating, would open dialog. If click on cancel button',
function() {
var promise;
var resolvedResult = 'Fake Result';
setup(function() {
this.sinon.stub(dialogService, '_execPendingRequest');
this.sinon.stub(document, 'getElementById', function() {
return document.createElement('div');
});
this.sinon.stub(mockDialogManager, 'close', function() {
return Promise.resolve(resolvedResult);
});
dialogService._navigating = false;
promise = dialogService.show(panelId, userOptions);
userOptions.onWrapCancel();
});
test('will do following works', function(done) {
promise.then(function(result) {
assert.equal(result.type, 'cancel');
assert.equal(result.value, resolvedResult);
assert.isTrue(dialogService._execPendingRequest.called);
assert.isFalse(dialogService._navigating);
}, function() {
assert.isTrue(false);
}).then(done, done);
});
});
suite('not navigating, would open dialog. If click on submit button',
function() {
var promise;
var resolvedResult = 'Fake Result';
setup(function() {
this.sinon.stub(dialogService, '_execPendingRequest');
this.sinon.stub(document, 'getElementById', function() {
return document.createElement('div');
});
this.sinon.stub(mockDialogManager, 'close', function() {
return Promise.resolve(resolvedResult);
});
dialogService._navigating = false;
promise = dialogService.show(panelId, userOptions);
userOptions.onWrapSubmit();
});
test('will do following works', function(done) {
promise.then(function(result) {
assert.equal(result.type, 'submit');
assert.equal(result.value, resolvedResult);
assert.isTrue(dialogService._execPendingRequest.called);
assert.isFalse(dialogService._navigating);
}, function() {
assert.isTrue(false);
}).then(done, done);
});
});
});
});

View file

@ -0,0 +1,306 @@
/* globals MocksHelper, MockKeyboardHelper */
'use strict';
require('/shared/test/unit/mocks/mock_keyboard_helper.js');
require('/shared/test/unit/mocks/mock_manifest_helper.js');
suite('KeyboardContext', function() {
var mockHelper = new MocksHelper([
'KeyboardHelper',
'ManifestHelper'
]).init();
mockHelper.attachTestHelpers();
suiteSetup(function(done) {
var map = {
'modules/keyboard_context': {
'shared/keyboard_helper': 'shared_mocks/mock_keyboard_helper',
'shared/manifest_helper': 'shared_mocks/mock_manifest_helper'
}
};
testRequire(['modules/keyboard_context'], map, (function(KeyboardContext) {
this.KeyboardContext = KeyboardContext;
done();
}).bind(this));
});
/*
* The raw data used in this test please refer to mock_keyboard_helper.js
*
* Keyboards is an observable array of keyboard objects.
* A keyboard object contains the following properties:
* - name,
description,
launchPath,
layouts,
app: an instance of App
*
* A layout object is a Observable and contains the following properties:
* - name,
appName,
description,
types,
enabled
*/
var keyboards = null;
var enabledLayouts = null;
var keyboardApp1 = null;
var keyboardApp2 = null;
var keyboardApp3 = null;
var layouts = null;
setup(function(callback) {
this.KeyboardContext.init();
this.KeyboardContext.keyboards((function(_keyboards) {
keyboards = _keyboards;
var keybordsArray = keyboards.array;
keyboardApp1 = keybordsArray[0];
keyboardApp2 = keybordsArray[1];
keyboardApp3 = keybordsArray[2];
layouts = {
'0': {
'0': keyboardApp1.layouts[0],
'1': keyboardApp1.layouts[1]
},
'1': {
'0': keyboardApp2.layouts[0]
},
'2': {
'0': keyboardApp3.layouts[0]
}
};
this.KeyboardContext.enabledLayouts(function(_enabledLayouts) {
enabledLayouts = _enabledLayouts;
callback();
});
}).bind(this));
});
teardown(function() {
keyboards = null;
enabledLayouts = null;
});
suite('keyboards', function() {
test('Keyboard count', function() {
assert.isNotNull(keyboardApp1);
assert.isNotNull(keyboardApp2);
assert.isNotNull(keyboardApp3);
});
test('Keyboard name', function() {
assert.equal(keyboardApp1.name, 'app1');
assert.equal(keyboardApp2.name, 'app2');
assert.equal(keyboardApp3.name, 'app3');
});
test('Keyboard description', function() {
assert.equal(keyboardApp1.description, 'app1');
assert.equal(keyboardApp2.description, 'app2');
assert.equal(keyboardApp3.description, 'app3');
});
test('Keyboard launch path', function() {
assert.equal(keyboardApp1.launchPath, '/settings.html');
assert.equal(keyboardApp2.launchPath, '/settings.html');
assert.equal(keyboardApp3.launchPath, '/settings.html');
});
test('Keyboard layout count', function() {
assert.equal(keyboardApp1.layouts.length, 2);
assert.equal(keyboardApp2.layouts.length, 1);
assert.equal(keyboardApp3.layouts.length, 1);
});
test('Layout count', function() {
// app1
assert.isNotNull(layouts[0][0]);
assert.isNotNull(layouts[0][1]);
// app2
assert.isNotNull(layouts[1][0]);
// app3
assert.isNotNull(layouts[2][0]);
});
test('Layout name', function() {
// app1
assert.equal(layouts[0][0].name, 'layout1');
assert.equal(layouts[0][1].name, 'layout2');
// app2
assert.equal(layouts[1][0].name, 'layout1');
// app3
assert.equal(layouts[2][0].name, 'layout1');
});
test('Layout app name', function() {
// app1
assert.equal(layouts[0][0].appName, 'app1');
assert.equal(layouts[0][1].appName, 'app1');
// app2
assert.equal(layouts[1][0].appName, 'app2');
// app3
assert.equal(layouts[2][0].appName, 'app3');
});
test('Layout description', function() {
// app1
assert.equal(layouts[0][0].description, 'layout1');
assert.equal(layouts[0][1].description, 'layout2');
// app2
assert.equal(layouts[1][0].description, 'layout1');
// app3
assert.equal(layouts[2][0].description, 'layout1');
});
test('Layout type', function() {
// app1
assert.equal(layouts[0][0].types.length, 2);
assert.equal(layouts[0][0].types[0], 'url');
assert.equal(layouts[0][0].types[1], 'text');
assert.equal(layouts[0][1].types.length, 1);
assert.equal(layouts[0][1].types[0], 'number');
// app2
assert.equal(layouts[1][0].types.length, 1);
assert.equal(layouts[1][0].types[0], 'url');
// app3
assert.equal(layouts[2][0].types.length, 1);
assert.equal(layouts[2][0].types[0], 'number');
});
});
suite('enabledLayouts', function() {
setup(function() {
this.checkDefaults =
this.sinon.stub(MockKeyboardHelper, 'checkDefaults');
});
var isActuallyEnabled = function(targetLayout) {
var found = false;
enabledLayouts.forEach(function(layout) {
if (layout === targetLayout) {
found = true;
}
});
return found;
};
test('Default enabled layout', function(callback) {
this.KeyboardContext.enabledLayouts(function(_enabledLayouts) {
var layout1 = _enabledLayouts.get(0);
var layout2 = _enabledLayouts.get(1);
assert.equal(_enabledLayouts.length, 2);
assert.equal(layout1.appName, layouts[0][0].appName);
assert.equal(layout1.name, layouts[0][0].name);
assert.equal(layout2.appName, layouts[2][0].appName);
assert.equal(layout2.name, layouts[2][0].name);
callback();
});
});
// The following tests do a series of actions and check if the layouts are
// enabled/disabled accordingly.
test('Disable app1 layout1', function() {
var targetLayout = layouts[0][0];
targetLayout.enabled = false;
MockKeyboardHelper.saveToSettings();
assert.isFalse(isActuallyEnabled(targetLayout));
assert.isTrue(this.checkDefaults.called);
});
test('Enable app1 layout1', function() {
var targetLayout = layouts[0][0];
targetLayout.enabled = true;
MockKeyboardHelper.saveToSettings();
assert.isTrue(isActuallyEnabled(targetLayout));
// don't check defaults when enabling a keyboard
assert.isFalse(this.checkDefaults.called);
});
test('Disable app1 layout1', function() {
var targetLayout = layouts[0][0];
targetLayout.enabled = false;
MockKeyboardHelper.saveToSettings();
assert.isFalse(isActuallyEnabled(targetLayout));
assert.isTrue(this.checkDefaults.called);
});
test('Enable app1 layout2', function() {
var targetLayout = layouts[0][1];
targetLayout.enabled = true;
MockKeyboardHelper.saveToSettings();
assert.isTrue(isActuallyEnabled(targetLayout));
// don't check defaults when enabling a keyboard
assert.isFalse(this.checkDefaults.called);
});
test('Enable app2 layout1', function() {
var targetLayout = layouts[1][0];
targetLayout.enabled = true;
MockKeyboardHelper.saveToSettings();
assert.isTrue(isActuallyEnabled(targetLayout));
// don't check defaults when enabling a keyboard
assert.isFalse(this.checkDefaults.called);
});
test('Disable app3 layout1', function() {
var targetLayout = layouts[2][0];
targetLayout.enabled = false;
MockKeyboardHelper.saveToSettings();
assert.isFalse(isActuallyEnabled(targetLayout));
assert.isTrue(this.checkDefaults.called);
});
test('Disable app2 layout1', function() {
var targetLayout = layouts[1][0];
targetLayout.enabled = false;
MockKeyboardHelper.saveToSettings();
assert.isFalse(isActuallyEnabled(targetLayout));
assert.isTrue(this.checkDefaults.called);
});
suite('Checks defaults', function() {
var missingTypes;
var targetLayout;
var targetRawLayout;
var defaultCallback;
setup(function() {
missingTypes = [];
targetLayout = layouts[0][0];
targetRawLayout = MockKeyboardHelper.layouts[0];
defaultCallback = this.sinon.spy();
this.KeyboardContext.defaultKeyboardEnabled(defaultCallback,
missingTypes);
});
test('calls checkDefaults', function() {
targetLayout.enabled = false;
assert.isTrue(this.checkDefaults.called);
});
test('calls callback with enabled layout', function() {
targetLayout.enabled = false;
this.checkDefaults.yield([targetRawLayout], [missingTypes]);
assert.ok(defaultCallback.calledWith(targetRawLayout));
// The layout should be re-enabled.
assert.isTrue(targetLayout.enabled);
});
});
});
});

View file

@ -0,0 +1,79 @@
/* global MockGetDeviceStorage, MockGetDeviceStorages, MockDOMRequest */
'use strict';
require('/shared/test/unit/mocks/mock_event_target.js');
require('/shared/test/unit/mocks/mock_dom_request.js');
require('/shared/test/unit/mocks/mock_navigator_getdevicestorage.js');
require('/shared/test/unit/mocks/mock_navigator_getdevicestorages.js');
suite('MediaStorage > ', function() {
var MediaStorage;
var realDeviceStorage, realDeviceStorages;
var modules = [
'modules/media_storage'
];
var map = {
'*': {
'shared/settings_listener': 'shared_mocks/mock_settings_listener'
}
};
suiteSetup(function() {
realDeviceStorages = navigator.getDeviceStorages;
navigator.getDeviceStorages = MockGetDeviceStorages;
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockGetDeviceStorage;
});
suiteTeardown(function() {
navigator.getDeviceStorages = realDeviceStorages;
realDeviceStorages = null;
navigator.getDeviceStorage = realDeviceStorage;
realDeviceStorage = null;
});
setup(function(done) {
var requireCtx = testRequire([], map, function() {});
requireCtx(modules, function(media_storage) {
MediaStorage = media_storage;
done();
});
});
suite('_updateMediaStorageInfo', function() {
setup(function() {
this.sinon.stub(MediaStorage, '_updateVolumeState');
});
test('when no _defaultMediaVolume',
function() {
MediaStorage._defaultMediaVolume = null;
MediaStorage._updateMediaStorageInfo();
assert.ok(MediaStorage._updateVolumeState.calledWith(null,
'unavailable'));
});
test('when has _defaultMediaVolume', function() {
MediaStorage._defaultMediaVolume = MockGetDeviceStorage();
this.sinon.stub(MediaStorage._defaultMediaVolume, 'available')
.returns(MockDOMRequest);
MediaStorage._updateMediaStorageInfo();
assert.ok(MediaStorage._defaultMediaVolume.available.called);
});
});
suite('_getDefaultVolume', function() {
setup(function() {
this.sinon.spy(navigator, 'getDeviceStorages');
});
test('call navigator.getDeviceStorages to get default volume',
function() {
MediaStorage._getDefaultVolume();
assert.ok(navigator.getDeviceStorages.calledWith('sdcard'));
});
});
});

View file

@ -0,0 +1,71 @@
/* global MockNavigatorSettings */
'use strict';
requireApp('settings/shared/test/unit/mocks/mock_navigator_moz_settings.js');
suite('messaging', function() {
var map = {
'*': {
'shared/icc_helper': 'IccHelper',
'shared/async_storage': 'unit/mock_async_storage',
'modules/settings_utils': 'unit/mock_settings_utils',
'shared/settings_listener': 'shared_mocks/mock_settings_listener'
}
};
var realSettings;
var messaging;
var mockSettingsUtils;
var mockIccHelper;
suiteSetup(function() {
realSettings = window.navigator.mozSettings;
window.navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
window.navigator.mozSettings = realSettings;
});
setup(function(done) {
var requireCtx = testRequire([], map, function() {});
mockIccHelper = {
addEventListener: function() {},
cardState: null
};
define('IccHelper', function() {
return mockIccHelper;
});
requireCtx([
'modules/messaging',
'unit/mock_settings_utils'
], function(Messaging, MockSettingsUtils) {
messaging = Messaging;
mockSettingsUtils = MockSettingsUtils;
done();
});
});
suite('disable items based on card states', function() {
var fakePanel = {};
setup(function() {
this.sinon.stub(messaging, '_disableItems');
});
test('card state: ready would enable items', function() {
mockIccHelper.cardState = 'ready';
messaging.disableItems(fakePanel);
assert.isTrue(messaging._disableItems.calledWith(fakePanel, false));
});
test('card state: non-ready would disable items', function() {
mockIccHelper.cardState = 'unknown';
messaging.disableItems(fakePanel);
assert.isTrue(messaging._disableItems.calledWith(fakePanel, true));
});
});
});

View file

@ -0,0 +1,68 @@
/* global MockNavigatorMozMobileConnections */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
suite('NetworkInfo', function() {
var supportedNetworkInfo;
var _mobileConnection;
var mockNetworkType;
var realMozMobileConnections;
var modules = [
'modules/mobile/supported_network_info'
];
var map = {
'*': {
'modules/mobile/supported_network_type': 'MockNetworkType'
}
};
var mockSupportedNetworkTypes = [
'lte/wcdma/gsm',
'lte/wcdma',
'gsm',
'wcdma',
'wcdma/gsm-auto',
'wcdma/gsm',
'lte'
];
setup(function(done) {
var requireCtx = testRequire([], map, function() {});
mockNetworkType = {
SupportedNetworkTypeHelper: function(supportedNetworkTypes) {
return mockSupportedNetworkTypes;
}
};
define('MockNetworkType', function() {
return mockNetworkType;
});
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
_mobileConnection = MockNavigatorMozMobileConnections[0];
_mobileConnection.supportedNetworkTypes = mockSupportedNetworkTypes;
requireCtx(modules, function(SupportedNetworkInfo) {
supportedNetworkInfo = SupportedNetworkInfo;
done();
});
});
teardown(function() {
_mobileConnection = null;
navigator.mozMobileConnections = realMozMobileConnections;
});
suite('SupportedNetworkTypeHelper is called', function() {
test('single hardware support type', function() {
this.sinon.spy(mockNetworkType, 'SupportedNetworkTypeHelper');
this.sinon.spy(supportedNetworkInfo, '_getMobileConnectionIndex');
supportedNetworkInfo.getSupportedNetworkInfo(_mobileConnection);
assert.ok(supportedNetworkInfo._getMobileConnectionIndex
.calledWith(_mobileConnection));
assert.ok(mockNetworkType.SupportedNetworkTypeHelper
.calledWith(_mobileConnection.supportedNetworkTypes));
});
});
});

View file

@ -0,0 +1,248 @@
'use strict';
suite('NetworkType', function() {
var SupportedNetworkTypeHelper;
suiteSetup(function(done) {
var modules = [
'modules/mobile/supported_network_type'
];
var maps = {};
testRequire(modules, maps, function(module) {
SupportedNetworkTypeHelper = module.SupportedNetworkTypeHelper;
done();
});
});
suite('SupportedNetworkTypeHelper', function() {
var hardwareSupportedTypes = ['gsm', 'wcdma', 'cdma', 'evdo', 'lte'];
suite('single hardware support type', function() {
suite('should have correct support value', function() {
hardwareSupportedTypes.forEach(function(hardwareSupportedType) {
test(hardwareSupportedType, function() {
var helper = SupportedNetworkTypeHelper([hardwareSupportedType]);
hardwareSupportedTypes.forEach(function(targetType) {
assert.equal(helper[targetType],
hardwareSupportedType === targetType);
});
});
});
});
suite('networkTypes', function() {
hardwareSupportedTypes.forEach(function(hardwareSupportedType) {
test(hardwareSupportedType, function() {
var helper = SupportedNetworkTypeHelper([hardwareSupportedType]);
assert.deepEqual(helper.networkTypes, [hardwareSupportedType]);
});
});
});
suite('l10nIdForType()', function() {
suiteSetup(function() {
this.expectedl10nIds = {
'gsm': 'operator-networkType-2G',
'wcdma': 'operator-networkType-3G',
'cdma': 'operator-networkType-CDMA',
'evdo': 'operator-networkType-EVDO',
'lte': 'operator-networkType-LTE'
};
});
hardwareSupportedTypes.forEach(function(hardwareSupportedType) {
test(hardwareSupportedType, function() {
var helper = SupportedNetworkTypeHelper([hardwareSupportedType]);
assert.equal(helper.l10nIdForType(hardwareSupportedType),
this.expectedl10nIds[hardwareSupportedType]);
});
});
});
});
suite('multiple hardware support types', function() {
suite('networkTypes', function() {
test('gsm, wcdma', function() {
var helper = SupportedNetworkTypeHelper(['gsm', 'wcdma']);
assert.deepEqual(helper.networkTypes, [
'wcdma/gsm',
'gsm',
'wcdma',
'wcdma/gsm-auto'
]);
});
test('evdo, cdma', function() {
var helper = SupportedNetworkTypeHelper(['evdo', 'cdma']);
assert.deepEqual(helper.networkTypes, [
'cdma/evdo',
'cdma',
'evdo'
]);
});
test('gsm, wcdma, evdo, cdma', function() {
var helper =
SupportedNetworkTypeHelper(['gsm', 'wcdma', 'evdo', 'cdma']);
assert.deepEqual(helper.networkTypes, [
'wcdma/gsm',
'gsm',
'wcdma',
'wcdma/gsm-auto',
'cdma/evdo',
'cdma',
'evdo',
'wcdma/gsm/cdma/evdo'
]);
});
test('gsm, wcdma, lte', function() {
var helper = SupportedNetworkTypeHelper(['gsm', 'wcdma', 'lte']);
assert.deepEqual(helper.networkTypes, [
'wcdma/gsm',
'gsm',
'wcdma',
'wcdma/gsm-auto',
'lte/wcdma/gsm',
'lte',
'lte/wcdma'
]);
});
test('evdo, cdma, lte', function() {
var helper = SupportedNetworkTypeHelper(['evdo', 'cdma', 'lte']);
assert.deepEqual(helper.networkTypes, [
'cdma/evdo',
'cdma',
'evdo',
'lte/cdma/evdo',
'lte'
]);
});
test('gsm, wcdma, evdo, cdma, lte', function() {
var helper =
SupportedNetworkTypeHelper(['gsm', 'wcdma', 'evdo', 'cdma', 'lte']);
assert.deepEqual(helper.networkTypes, [
'wcdma/gsm',
'gsm',
'wcdma',
'wcdma/gsm-auto',
'cdma/evdo',
'cdma',
'evdo',
'wcdma/gsm/cdma/evdo',
'lte/cdma/evdo',
'lte/wcdma/gsm',
'lte/wcdma/gsm/cdma/evdo',
'lte',
'lte/wcdma'
]);
});
});
suite('l10nIdForType()', function() {
test('gsm, wcdma', function() {
var helper = SupportedNetworkTypeHelper(['gsm', 'wcdma']);
assert.equal(helper.l10nIdForType('wcdma/gsm'),
'operator-networkType-prefer3G');
assert.equal(helper.l10nIdForType('gsm'), 'operator-networkType-2G');
assert.equal(helper.l10nIdForType('wcdma'),
'operator-networkType-3G');
assert.equal(helper.l10nIdForType('wcdma/gsm-auto'),
'operator-networkType-auto');
});
test('evdo, cdma', function() {
var helper = SupportedNetworkTypeHelper(['evdo', 'cdma']);
assert.equal(helper.l10nIdForType('cdma/evdo'),
'operator-networkType-auto');
assert.equal(helper.l10nIdForType('cdma'),
'operator-networkType-CDMA');
assert.equal(helper.l10nIdForType('evdo'),
'operator-networkType-EVDO');
});
test('gsm, wcdma, evdo, cdma', function() {
var helper =
SupportedNetworkTypeHelper(['gsm', 'wcdma', 'evdo', 'cdma']);
assert.equal(helper.l10nIdForType('wcdma/gsm'),
'operator-networkType-preferWCDMA');
assert.equal(helper.l10nIdForType('gsm'), 'operator-networkType-GSM');
assert.equal(helper.l10nIdForType('wcdma'),
'operator-networkType-WCDMA');
assert.equal(helper.l10nIdForType('wcdma/gsm-auto'),
'operator-networkType-auto-WCDMA-GSM');
assert.equal(helper.l10nIdForType('cdma/evdo'),
'operator-networkType-auto-CDMA-EVDO');
assert.equal(helper.l10nIdForType('cdma'),
'operator-networkType-CDMA');
assert.equal(helper.l10nIdForType('evdo'),
'operator-networkType-EVDO');
assert.equal(helper.l10nIdForType('wcdma/gsm/cdma/evdo'),
'operator-networkType-auto');
});
test('gsm, wcdma, lte', function() {
var helper = SupportedNetworkTypeHelper(['gsm', 'wcdma', 'lte']);
assert.equal(helper.l10nIdForType('wcdma/gsm'),
'operator-networkType-prefer3G');
assert.equal(helper.l10nIdForType('gsm'), 'operator-networkType-2G');
assert.equal(helper.l10nIdForType('wcdma'),
'operator-networkType-3G');
assert.equal(helper.l10nIdForType('wcdma/gsm-auto'),
'operator-networkType-auto-2G-3G');
assert.equal(helper.l10nIdForType('lte'), 'operator-networkType-LTE');
assert.equal(helper.l10nIdForType('lte/wcdma/gsm'),
'operator-networkType-auto');
assert.equal(helper.l10nIdForType('lte/wcdma'),
'operator-networkType-auto-3G-4G');
});
test('evdo, cdma, lte', function() {
var helper = SupportedNetworkTypeHelper(['evdo', 'cdma', 'lte']);
assert.equal(helper.l10nIdForType('cdma/evdo'),
'operator-networkType-auto-CDMA-EVDO');
assert.equal(helper.l10nIdForType('cdma'),
'operator-networkType-CDMA');
assert.equal(helper.l10nIdForType('evdo'),
'operator-networkType-EVDO');
assert.equal(helper.l10nIdForType('lte'), 'operator-networkType-LTE');
assert.equal(helper.l10nIdForType('lte/cdma/evdo'),
'operator-networkType-auto');
});
test('gsm, wcdma, evdo, cdma, lte', function() {
var helper =
SupportedNetworkTypeHelper(['gsm', 'wcdma', 'evdo', 'cdma', 'lte']);
assert.equal(helper.l10nIdForType('wcdma/gsm'),
'operator-networkType-preferWCDMA');
assert.equal(helper.l10nIdForType('gsm'), 'operator-networkType-GSM');
assert.equal(helper.l10nIdForType('wcdma'),
'operator-networkType-WCDMA');
assert.equal(helper.l10nIdForType('wcdma/gsm-auto'),
'operator-networkType-auto-WCDMA-GSM');
assert.equal(helper.l10nIdForType('cdma/evdo'),
'operator-networkType-auto-CDMA-EVDO');
assert.equal(helper.l10nIdForType('cdma'),
'operator-networkType-CDMA');
assert.equal(helper.l10nIdForType('evdo'),
'operator-networkType-EVDO');
assert.equal(helper.l10nIdForType('wcdma/gsm/cdma/evdo'),
'operator-networkType-auto-WCDMA-GSM-CDMA-EVDO');
assert.equal(helper.l10nIdForType('lte'),
'operator-networkType-LTE');
assert.equal(helper.l10nIdForType('lte/wcdma'),
'operator-networkType-auto-LTE-WCDMA');
assert.equal(helper.l10nIdForType('lte/cdma/evdo'),
'operator-networkType-auto-LTE-CDMA-EVDO');
assert.equal(helper.l10nIdForType('lte/wcdma/gsm'),
'operator-networkType-auto-LTE-WCDMA-GSM');
assert.equal(helper.l10nIdForType('lte/wcdma/gsm/cdma/evdo'),
'operator-networkType-auto');
});
});
});
});
});

View file

@ -0,0 +1,621 @@
/* global define */
'use strict';
// The views are very tightly coupled to models, rather than reimplement model
// behavior in a mock, rely on tested interfaces on the model instead
suite('ListView', function() {
// Define a shim for ObservableArray that is to be loaded in ListView.
// Then we are able to creat a stub on it.
suiteSetup(function(done) {
// Use the shim in ListView.
var ObservableArrayShimName = 'observable_array_shim' + Date.now();
var map = {
'modules/mvvm/list_view': {
'modules/mvvm/observable_array': ObservableArrayShimName
}
};
// XXX: Create a new require context.
// We can only define the mock module after retrieving the context.
var requireCtx = testRequire([], map, function() {});
// Define the mock module.
define(ObservableArrayShimName, function() {
var ctor = function observable_array_shim() {
return ctor.mInnerFunction.apply(this, arguments);
};
ctor.mInnerFunction = function() {};
ctor.mTeardown = function observable_array_shim_teardown() {
ctor.mInnerFunction = function() {};
};
return ctor;
});
requireCtx([
'modules/mvvm/observable',
'modules/mvvm/observable_array',
'modules/mvvm/list_view',
ObservableArrayShimName
], (function(Observable, ObservableArray, ListView, ObservableArrayShim) {
this.Observable = Observable;
this.ListView = ListView;
this.ObservableArrayShim = ObservableArrayShim;
// stub the real ObservableArray to create spies on observe method
// Redirect the shim to the ObservableArray stub.
this.ObservableArray = sinon.stub(ObservableArrayShim,
'mInnerFunction', function() {
var result = ObservableArray.apply(this, arguments);
sinon.spy(result, 'addEventListener');
sinon.spy(result, 'removeEventListener');
return result;
});
done();
}).bind(this));
});
teardown(function() {
this.ObservableArray.reset();
});
suiteTeardown(function() {
this.ObservableArray.restore();
});
setup(function() {
this.array = [];
this.observableArray = this.ObservableArray(this.array);
});
suite('ListView(<ul>, ObservableArray, function)', function() {
setup(function() {
this.container = document.createElement('ul');
this.template = (function(item, recycle) {
if (recycle && this.template.recycle) {
return recycle;
}
return document.createElement('li');
}).bind(this);
this.sinon.spy(this, 'template');
this.listView = this.ListView(
this.container, this.observableArray, this.template
);
});
test('.element', function() {
assert.equal(this.listView.element, this.container);
});
test('Listens to ObservableArray', function() {
var addEventListener = this.observableArray.addEventListener;
assert.ok(addEventListener.calledWith('insert'));
assert.ok(addEventListener.calledWith('remove'));
assert.ok(addEventListener.calledWith('replace'));
assert.ok(addEventListener.calledWith('reset'));
});
suite('Add Multiple Elements', function() {
setup(function() {
// add 3 items
for (var count = 0; count < 3; count++) {
this.observableArray.push({ count: count });
}
});
// use forEach instead of for to keep 'count' in a closure
[0, 1, 2].forEach(function(count) {
suite('item ' + count, function() {
setup(function() {
this.item = this.array[count];
});
test('kept order', function() {
assert.equal(this.item.count, count);
});
test('called template', function() {
assert.ok(this.template.calledWith(this.item));
});
test('template returned element is in correct position', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[count], li);
});
});
});
suite('Replace Element', function() {
setup(function() {
this.template.reset();
this.item = { replacement: true };
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.observableArray.set(1, this.item);
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElements[1])
);
});
test('only called template method once', function() {
assert.equal(this.template.callCount, 1);
});
test('replaced element', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[1], li);
});
test('leaves other elements alone', function() {
assert.equal(this.container.children[0], this.originalElements[0]);
assert.equal(this.container.children[2], this.originalElements[2]);
});
});
suite('Replace Element (w/recycle)', function() {
setup(function() {
this.template.recycle = true;
this.template.reset();
this.item = { replacement: true };
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.observableArray.set(1, this.item);
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElements[1])
);
});
test('only called template method once', function() {
assert.equal(this.template.callCount, 1);
});
test('same element still in place', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(li, this.originalElements[1], 'returned original');
assert.equal(this.container.children[1], this.originalElements[1]);
});
test('leaves other elements alone', function() {
assert.equal(this.container.children[0], this.originalElements[0]);
assert.equal(this.container.children[2], this.originalElements[2]);
});
});
suite('Reset With Shorter Array', function() {
setup(function() {
this.template.reset();
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.originalArray = this.array;
this.array = [{ reset: 1 }, { reset: 2 }];
this.observableArray.reset(this.array);
});
// use forEach instead of for to store 'index' in closure
[0, 1].forEach(function(index) {
suite('item ' + index, function() {
setup(function() {
this.item = this.array[index];
this.originalElement = this.originalElements[index];
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElement)
);
});
test('replaced element', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[index], li);
});
});
});
test('removed extra elements', function() {
assert.equal(this.container.children.length, 2);
assert.equal(this.originalElements[2].parentNode, null);
});
});
suite('Reset With Longer Array', function() {
setup(function() {
this.template.reset();
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.originalArray = this.array;
this.array = [{ reset: 1 }, { reset: 2 }, { reset: 3 }, { reset: 4 }];
this.observableArray.reset(this.array);
});
// use forEach instead of for to store 'index' in closure
[0, 1, 2].forEach(function(index) {
suite('item ' + index, function() {
setup(function() {
this.item = this.array[index];
this.originalElement = this.originalElements[index];
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElement)
);
});
test('replaced element', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[index], li);
});
});
});
suite('item 3', function() {
setup(function() {
this.item = this.array[3];
});
test('called template without recycled element', function() {
assert.ok(this.template.calledWith(this.item));
assert.equal(
this.template.withArgs(this.item).args[0][1], undefined
);
});
test('added extra element', function() {
assert.equal(this.container.children.length, 4);
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[3], li);
});
});
});
suite('reset with .set([{},{}])', function() {
setup(function() {
this.template.reset();
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.originalArray = this.array;
this.array = [{ reset: 1 }, { reset: 2 }];
this.listView.set(this.array);
});
test('creates and watches an ObservableArray', function() {
var withNew = this.ObservableArray.withArgs(this.array);
assert.ok(withNew.called);
var addEventListener = withNew.returnValues[0].addEventListener;
assert.ok(addEventListener.calledWith('insert'));
assert.ok(addEventListener.calledWith('remove'));
assert.ok(addEventListener.calledWith('replace'));
assert.ok(addEventListener.calledWith('reset'));
});
// use forEach instead of for to store 'index' in closure
[0, 1].forEach(function(index) {
suite('item ' + index, function() {
setup(function() {
this.item = this.array[index];
this.originalElement = this.originalElements[index];
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElement)
);
});
test('replaced element', function() {
var li = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[index], li);
});
});
});
test('removed extra elements', function() {
assert.equal(this.container.children.length, 2);
assert.equal(this.originalElements[2].parentNode, null);
});
});
suite('.enabled = false', function() {
setup(function() {
this.template.reset();
this.originalElements =
Array.prototype.slice.call(this.container.children);
this.listView.enabled = false;
});
// test a bunch of things that modify the array
[function pop() {
this.observableArray.pop();
}, function push() {
this.observableArray.push({});
}, function replace() {
this.observableArray.set(1, {});
}, function reset() {
this.array = [{}];
this.observableArray.reset(this.array);
}, function set() {
this.array = [{}];
this.listView.set(this.array);
}].forEach(function(method) {
suite(method.name, function() {
setup(method);
test('does not call template', function() {
assert.equal(this.template.called, 0);
});
test('does not change elements', function() {
for (var i = 0; i < this.container.children.length; i++) {
assert.equal(this.container.children[i],
this.originalElements[i]);
}
});
suite('.enabled = true', function() {
setup(function() {
this.listView.enabled = true;
});
test('calls template for each item in array', function() {
this.array.forEach(function(item, index) {
assert.ok(this.template.calledWith(
item, this.originalElements[index]
), 'array[' + index + ']');
}, this);
});
test('elements are put in the right place', function() {
this.array.forEach(function(item, index) {
var template = this.template.withArgs(item);
assert.equal(
this.container.children[index], template.returnValues[0],
'element[' + index + ']'
);
}, this);
});
});
});
});
});
});
});
suite('ListView(<div>, ObservableArray, function)', function() {
setup(function() {
this.container = document.createElement('div');
// ensure content inside the container at creation doesn't interfere
this.container.appendChild(document.createElement('span'));
this.template = (function(item, recycle) {
if (recycle && this.template.recycle) {
return recycle;
}
return document.createElement('div');
}).bind(this);
this.sinon.spy(this, 'template');
this.observableArray.push({ count: 0 });
this.listView = this.ListView(
this.container, this.observableArray, this.template
);
});
test('.element', function() {
assert.equal(this.listView.element, this.container);
});
test('Listens to ObservableArray', function() {
var addEventListener = this.observableArray.addEventListener;
assert.ok(addEventListener.calledWith('insert'));
assert.ok(addEventListener.calledWith('remove'));
assert.ok(addEventListener.calledWith('replace'));
assert.ok(addEventListener.calledWith('reset'));
});
test('Initial Element doesnt recycle', function() {
var item = this.array[0];
var filtered = this.template.withArgs(item);
// ensures that if there happens to be an element in the creation
// of the ListView, it is not used as a "recycled" element
assert.ok(filtered.callCount, 1);
assert.equal(filtered.args[0][1], undefined);
});
suite('Add Multiple Elements', function() {
setup(function() {
// add 2 more items
for (var count = 1; count < 3; count++) {
this.observableArray.push({ count: count });
}
});
// use forEach instead of for to keep 'count' in a closure
[1, 2].forEach(function(count) {
suite('item ' + count, function() {
setup(function() {
this.item = this.array[count];
});
test('kept order', function() {
assert.equal(this.item.count, count);
});
test('called template', function() {
assert.ok(this.template.calledWith(this.item));
});
test('template returned element is in correct position', function() {
var div = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[count], div);
});
});
});
suite('Replace Element', function() {
setup(function() {
this.template.reset();
this.item = { replacement: true };
this.originalElements = [].slice.call(this.container.children);
this.observableArray.set(1, this.item);
});
test('called template with recycled element', function() {
assert.ok(
this.template.calledWith(this.item, this.originalElements[1])
);
});
test('only called template method once', function() {
assert.equal(this.template.callCount, 1);
});
test('replaced element', function() {
var div = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[1], div);
});
test('leaves other elements alone', function() {
assert.equal(this.container.children[0], this.originalElements[0]);
assert.equal(this.container.children[2], this.originalElements[2]);
});
});
suite('Remove Element', function() {
setup(function() {
this.template.reset();
this.originalElements = [].slice.call(this.container.children);
this.observableArray.pop();
});
test('removed last element', function() {
assert.equal(this.originalElements[2].parentNode, null);
});
test('leaves other elements alone', function() {
assert.equal(this.container.children[0], this.originalElements[0]);
assert.equal(this.container.children[1], this.originalElements[1]);
});
});
suite('Insert Element', function() {
setup(function() {
this.originalElements = [].slice.call(this.container.children);
this.item = {};
this.template.reset();
this.observableArray.splice(0, 0, this.item);
});
test('only called template method once', function() {
assert.equal(this.template.callCount, 1);
});
test('inserted element', function() {
var div = this.template.withArgs(this.item).returnValues[0];
assert.equal(this.container.children[0], div);
});
test('shifts other elements', function() {
assert.equal(this.container.children[1], this.originalElements[0]);
assert.equal(this.container.children[2], this.originalElements[1]);
assert.equal(this.container.children[3], this.originalElements[2]);
});
});
});
});
suite('observable hooks', function() {
setup(function() {
this.observable = this.Observable({ test: 1 });
this.observableArray = this.ObservableArray([this.observable]);
this.hook = this.sinon.spy();
this.template = (function(data, recycle, helper) {
helper.observeAndCall(data, {
test: this.hook
});
return recycle || document.createElement('li');
}).bind(this);
this.container = document.createElement('ul');
this.sinon.spy(this, 'template');
this.listView = this.ListView(
this.container, this.observableArray, this.template
);
});
test('hook called at creation', function() {
assert.equal(this.hook.callCount, 1);
});
test('hook called when observable changes', function() {
this.hook.reset();
this.observable.test = false;
assert.equal(this.hook.callCount, 1);
});
test('unobserves when removing', function() {
this.sinon.spy(this.observable, 'unobserve');
this.observableArray.splice(0, 1);
assert.ok(this.observable.unobserve.calledWith(this.hook));
});
test('unobserves when replacing', function() {
this.sinon.spy(this.observable, 'unobserve');
this.sinon.spy(this.observable, 'observe');
this.observableArray.set(0, this.observable);
// should unobserve
assert.ok(this.observable.unobserve.calledWith(this.hook));
// and also observe
assert.ok(this.observable.observe.calledWith('test', this.hook));
// but should unobserve first
assert.ok(
this.observable.unobserve.calledBefore(this.observable.observe)
);
});
});
suite('destroy', function() {
setup(function() {
this.observableArray = this.ObservableArray([1, 2, 3]);
this.container = document.createElement('div');
this.listView = this.ListView(
this.container, this.observableArray, function() {
return document.createElement('div');
}
);
});
test('removes children', function() {
this.listView.destroy();
assert.equal(this.container.children.length, 0);
});
test('sanity check', function() {
assert.equal(this.observableArray.removeEventListener.callCount, 0);
assert.equal(this.container.children.length, 3);
});
test('.element', function() {
this.listView.destroy();
assert.equal(this.listView.element, null);
});
test('unbinds from observeable', function() {
this.listView.destroy();
// called call removeEventListener with a method
['insert', 'remove', 'replace', 'reset'].forEach((eventName, index) => {
assert.isTrue(
this.observableArray.removeEventListener.calledWith(eventName));
assert.isFunction(
this.observableArray.removeEventListener.args[index][1]);
});
});
test('creating another ListView destroys old', function() {
this.observableArray.reset();
this.sinon.spy(this.listView, 'destroy');
// create a new one, we don't care about what happens to it.
this.ListView(
this.container, this.observableArray, function() {
return document.createElement('div');
}
);
// destroys old view
assert.ok(this.listView.destroy.called);
// unbinds from observable
assert.ok(this.observableArray.removeEventListener.called);
// re-observes
assert.ok(this.observableArray.removeEventListener.called);
// and make sure its done in the right order
assert.ok(
this.observableArray.removeEventListener.calledBefore(
this.observableArray.removeEventListener
)
);
});
});
});

View file

@ -0,0 +1,231 @@
'use strict';
suite('ObservableArray', function() {
var methods = [
'forEach', 'push', 'pop', 'splice', 'set', 'get', 'reset', 'observe'
];
suiteSetup(function(done) {
testRequire(['modules/mvvm/observable_array'], (function(ObservableArray) {
this.ObservableArray = ObservableArray;
done();
}).bind(this));
});
setup(function() {
this.array = methods.slice();
this.observable = this.ObservableArray(this.array);
});
suite('shape: ObservableArray(array)', function() {
methods.forEach(function(method) {
test('.' + method + ' is a function', function() {
assert.equal(typeof this.observable[method], 'function');
});
});
test('.length is a number', function() {
assert.equal(typeof this.observable.length, 'number');
});
test('.array is the original array', function() {
assert.equal(this.observable.array, this.array);
});
});
test('get()', function() {
assert.ok(this.array.length);
for (var index = 0; index < this.array.length; index++) {
assert.equal(this.observable.get(index), this.array[index],
'index: ' + index);
}
});
suite('observable actions', function() {
var events = ['insert', 'remove', 'replace', 'reset'];
function checkSpies(expected) {
events.forEach(function(event) {
var expectedCount = expected[event] || 0;
test(event + ' called ' + expectedCount + ' times', function() {
assert.equal(this.spies[event].args.length, expectedCount);
});
});
}
function checkArgs(event) {
var args = [].slice.call(arguments, 1);
test(event + ' called with correct arguments', function() {
var calls = this.spies[event].args;
assert.deepEqual(calls[calls.length - 1], args);
});
}
function resetSpies(environment) {
events.forEach(function(event) {
this.spies[event].reset();
}, environment);
}
setup(function() {
this.spies = {};
events.forEach(function(event) {
this.spies[event] = this.sinon.spy();
this.observable.addEventListener(event, this.spies[event]);
}, this);
});
suite('pop()', function() {
setup(function() {
this.observable.pop();
});
checkSpies({ remove: 1 });
checkArgs('remove', {
type: 'remove',
detail: {
index: methods.length - 1,
count: 1,
items: ['observe']
}
});
test('modifies array', function() {
var expected = methods.slice();
expected.pop();
assert.deepEqual(this.array, expected);
});
});
suite('push()', function() {
setup(function() {
this.observable.push('test');
});
checkSpies({ insert: 1 });
checkArgs('insert', {
type: 'insert',
detail: {
index: methods.length,
count: 1,
items: ['test']
}
});
test('modifies array', function() {
var expected = methods.slice();
expected.push('test');
assert.deepEqual(this.array, expected);
});
});
suite('splice(1, 2)', function() {
setup(function() {
this.observable.splice(1, 2);
});
checkSpies({ remove: 1 });
checkArgs('remove', {
type: 'remove',
detail: {
index: 1,
count: 2,
items: ['push', 'pop']
}
});
test('modifies array', function() {
var expect = methods.slice();
expect.splice(1, 2);
assert.deepEqual(this.array, expect);
});
});
suite('splice(1, 2, 3, 4)', function() {
setup(function() {
this.observable.splice(1, 2, 3, 4);
});
checkSpies({ remove: 1, insert: 1 });
checkArgs('remove', {
type: 'remove',
detail: {
index: 1,
count: 2,
items: ['push', 'pop']
}
});
checkArgs('insert', {
type: 'insert',
detail: {
index: 1,
count: 2,
items: [3, 4]
}
});
test('modifies array', function() {
var expected = methods.slice();
expected.splice(1, 2, 3, 4);
assert.deepEqual(this.array, expected);
});
});
suite('splice(2, 0, 2)', function() {
setup(function() {
this.observable.splice(2, 0, 2);
});
checkSpies({ insert: 1 });
checkArgs('insert', {
type: 'insert',
detail: {
index: 2,
count: 1,
items: [2]
}
});
test('modifies array', function() {
var expected = methods.slice();
expected.splice(2, 0, 2);
assert.deepEqual(this.array, expected);
});
});
suite('set(7, true)', function() {
setup(function() {
this.observable.set(7, true);
});
checkSpies({ replace: 1 });
checkArgs('replace', {
type: 'replace',
detail: {
index: 7,
oldValue: methods[7],
newValue: true
}
});
test('modifies array', function() {
var expected = methods.slice();
expected[7] = true;
assert.deepEqual(this.array, expected);
});
});
suite('reset([])', function() {
var testArray = [true, false];
setup(function() {
this.observable.reset(testArray);
});
checkSpies({ reset: 1 });
checkArgs('reset', {
type: 'reset',
detail: {
items: testArray
}
});
test('changes array', function() {
assert.equal(this.observable.array, testArray);
});
});
suite('pop() on empty', function() {
setup(function() {
this.observable.reset([]);
resetSpies(this);
this.observable.pop();
});
// no events expected
checkSpies({});
});
});
});

View file

@ -0,0 +1,358 @@
'use strict';
suite('Observable', function() {
var template = {
testBoolean: true,
testNumber: 10,
testString: 'test',
'test object': {}
// currently disabled - we don't handle functions
// testFunction: function() {}
};
function executeTest() {
test('shape: Observable({}).observe is a function', function() {
var observable = this.Observable({});
assert.equal(typeof observable.observe, 'function');
});
test('shape: Observable({}).unobserve is a function', function() {
var observable = this.Observable({});
assert.equal(typeof observable.unobserve, 'function');
});
suite('Creates observable properties', function() {
var props = Object.keys(template);
setup(function() {
this.observable = this.Observable(template);
});
// some tests for each property
props.forEach(function(prop) {
test('prop: "' + prop + '" copied value', function() {
assert.equal(this.observable[prop], template[prop]);
});
// test some stuff for observing
suite('prop: "' + prop + '" observe', function() {
setup(function() {
this.spy = this.sinon.spy();
this.observable.observe(prop, this.spy);
});
test('does not generate any callbacks when attaching', function() {
assert.isFalse(this.spy.called);
});
test('does not notify when set to same value', function() {
this.observable[prop] = template[prop];
assert.isFalse(this.spy.called);
});
test('notifies when set to new value', function() {
this.newValue = {};
this.observable[prop] = this.newValue;
assert.isTrue(this.spy.calledWith(this.newValue, template[prop]));
});
});
});
suite('unobserve', function() {
setup(function() {
// bind some listeners
this.spies = [this.sinon.spy(), this.sinon.spy()];
props.forEach(function(prop) {
this.observable.observe(prop, this.spies[0]);
this.observable.observe(prop, this.spies[1]);
}, this);
});
test('(property, handler)', function() {
// remove handler for property
this.observable.unobserve('testNumber', this.spies[0]);
// change property - only other handler should be called
this.observable.testNumber = {};
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 1);
// change a different property, both handlers should be called
this.observable.testString = {};
assert.equal(this.spies[0].callCount, 1);
assert.equal(this.spies[1].callCount, 2);
});
test('(handler)', function() {
// remove handler for all properties
this.observable.unobserve(this.spies[0]);
// change property - only other handler should be called
this.observable.testNumber = {};
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 1);
// change a different property, only other handler should be called
this.observable.testString = {};
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 2);
});
test('(property)', function() {
// remove handler for all properties
this.observable.unobserve('testNumber');
// change property - neither handler should be called
this.observable.testNumber = {};
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 0);
// change a different property, both handlers should be called
this.observable.testString = {};
assert.equal(this.spies[0].callCount, 1);
assert.equal(this.spies[1].callCount, 1);
});
});
});
}
suite('Object form', function() {
suiteSetup(function(done) {
testRequire(['modules/mvvm/observable'], (function(Observable) {
this.Observable = Observable;
done();
}).bind(this));
});
executeTest();
});
suite('Prototype form', function() {
suiteSetup(function(done) {
testRequire(['modules/base/module', 'modules/mvvm/observable'],
(function(Module, Observable) {
var NewObservable = Module.create(function() {
this.super(Observable).call(this);
}).extend(Observable);
Object.keys(template).forEach(function (key) {
Observable.defineObservableProperty(NewObservable.prototype, key, {
value: template[key]
});
});
this.Observable = NewObservable;
done();
}).bind(this));
});
executeTest();
});
suite('Read-only property', function() {
var props = ['prop1', 'prop2'];
var defaultPropValues = ['value1', 'value2'];
suiteSetup(function(done) {
testRequire(['modules/base/module', 'modules/mvvm/observable'],
(function(Module, Observable) {
var NewObservable = Module.create(function() {
this.super(Observable).call(this);
}).extend(Observable);
Observable.defineObservableProperty(
NewObservable.prototype, props[0], {
readonly: true,
value: defaultPropValues[0]
});
Observable.defineObservableProperty(
NewObservable.prototype, props[1], {
readonly: true,
value: defaultPropValues[1]
});
this.Observable = NewObservable;
done();
}).bind(this));
});
test('unable to set the property', function() {
var observable = this.Observable();
assert.throw(function() {
observable[props[0]] = 300;
}, TypeError, 'setting a property that has only a getter');
});
test('internal property exists', function() {
var observable = this.Observable();
assert.equal(observable['_' + props[0]], defaultPropValues[0]);
assert.equal(observable['_' + props[1]], defaultPropValues[1]);
});
test('calls to the listeners when the internal property changes',
function(done) {
var newPropValue = 'newFakeValue';
var observable = this.Observable();
observable.observe(props[1], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[1]);
assert.equal(newValue, newPropValue);
done();
});
observable['_' + props[1]] = newPropValue;
});
});
suite('Dependency property', function() {
var props = ['prop1', 'prop2'];
var defaultPropValues = [2, 3];
var dependencyProps = ['dprop1', 'dprop2', 'dprop3'];
suiteSetup(function(done) {
testRequire(['modules/base/module', 'modules/mvvm/observable'],
(function(Module, Observable) {
var NewObservable = Module.create(function() {
this.super(Observable).call(this);
}).extend(Observable);
Observable.defineObservableProperty(
NewObservable.prototype, props[0], {
value: defaultPropValues[0]
});
Observable.defineObservableProperty(
NewObservable.prototype, props[1], {
value: defaultPropValues[1]
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[0], {
dependency: props,
get: function() {
return this[props[0]] + this[props[1]];
}
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[1], {
dependency: [dependencyProps[0]],
get: function() {
return this[dependencyProps[0]];
}
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[2], {
dependency: [dependencyProps[0]],
get: function() {
return this[dependencyProps[0]];
}
});
this.Observable = NewObservable;
done();
}).bind(this));
});
test('The property responds to the dependent property changes correctly',
function(done) {
var newPropValue = 100;
var observable = this.Observable();
observable.observe(dependencyProps[0], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
done();
});
observable[props[1]] = newPropValue;
});
test('The property responds to the dependency property changes correctly',
function(done) {
var newPropValue = 100;
var observable = this.Observable();
var changeCount = 0;
observable.observe(dependencyProps[1], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
changeCount++;
if (changeCount == 2) {
done();
}
});
observable.observe(dependencyProps[2], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
changeCount++;
if (changeCount == 2) {
done();
}
});
observable[props[1]] = newPropValue;
});
});
suite('Extendibility', function() {
var props = ['prop1', 'prop2'];
var defaultPropValues = [2, 3];
var dependencyProps = ['dprop1', 'dprop2', 'dprop3'];
suiteSetup(function(done) {
testRequire(['modules/base/module', 'modules/mvvm/observable'],
(function(Module, Observable) {
var NewObservable = Module.create(function() {
this.super(Observable).call(this);
}).extend(Observable);
Observable.defineObservableProperty(
NewObservable.prototype, props[0], {
value: defaultPropValues[0]
});
Observable.defineObservableProperty(
NewObservable.prototype, props[1], {
value: defaultPropValues[1]
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[0], {
dependency: props,
get: function() {
return this[props[0]] + this[props[1]];
}
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[1], {
dependency: [dependencyProps[0]],
get: function() {
return this[dependencyProps[0]];
}
});
Observable.defineObservableProperty(
NewObservable.prototype, dependencyProps[2], {
dependency: [dependencyProps[0]],
get: function() {
return this[dependencyProps[0]];
}
});
var NewObservable2 = Module.create(function() {
this.super(NewObservable).call(this);
}).extend(NewObservable);
this.Observable = NewObservable2;
done();
}).bind(this));
});
test('The property responds to the dependent property changes correctly',
function(done) {
var newPropValue = 100;
var observable = this.Observable();
observable.observe(dependencyProps[0], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
done();
});
observable[props[1]] = newPropValue;
});
test('The property responds to the dependency property changes correctly',
function(done) {
var newPropValue = 100;
var observable = this.Observable();
var changeCount = 0;
observable.observe(dependencyProps[1], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
changeCount++;
if (changeCount == 2) {
done();
}
});
observable.observe(dependencyProps[2], function(newValue, oldValue) {
assert.equal(oldValue, defaultPropValues[0] + defaultPropValues[1]);
assert.equal(newValue, defaultPropValues[0] + newPropValue);
changeCount++;
if (changeCount == 2) {
done();
}
});
observable[props[1]] = newPropValue;
});
});
});

View file

@ -0,0 +1,116 @@
'use strict';
suite('PanelCache', function() {
suiteSetup(function(done) {
navigator.addIdleObserver = sinon.spy();
var modules = [
'modules/panel_cache',
'unit/mock_settings_panel',
'unit/mock_test_panel'
];
// Use map to mock the dependencies.
// In this case for modules/panel_cache, we use unit/mock_settings_panel to
// replace modules/settings_panel.
var map = {
'modules/panel_cache': {
'modules/settings_panel': 'unit/mock_settings_panel'
}
};
testRequire(modules, map, (function(PanelCache, MockSettingsPanel,
MockTestPanel) {
this.PanelCache = PanelCache;
// Mock of the SettingsPanel function
this.MockSettingsPanel = MockSettingsPanel;
// Mock of the TestPanel function
this.MockTestPanel = MockTestPanel;
// XXX: As we use 'require' function of requirejs in PanelCache and it
// conflicts to the original require function, we replace it here.
this.originalRequire = window.require;
window.require = testRequire;
done();
}).bind(this));
});
suiteTeardown(function() {
window.require = this.originalRequire;
this.originalRequire = null;
});
suite('get()', function() {
setup(function() {
// create mock panel element
this.panelID = 'id';
this.panelElement = document.createElement('div');
this.panelElement.id = this.panelID;
document.body.appendChild(this.panelElement);
// create mock panel instance
this.mockPanelInstance = {};
});
teardown(function() {
document.body.removeChild(this.panelElement);
this.panelID = null;
this.panelElement = null;
this.mockPanelInstance = null;
this.PanelCache.reset();
this.MockSettingsPanel.mTeardown();
this.MockTestPanel.mTeardown();
});
test('should return a SettingsPanel when without specifying a panel module',
function(done) {
this.MockSettingsPanel.mInnerFunction =
sinon.stub().returns(this.mockPanelInstance);
this.PanelCache.get(this.panelID, (function(panelInstance) {
// ensure the returned panel instance is a SettingsPanel
assert.equal(panelInstance, this.mockPanelInstance);
done();
}).bind(this));
});
test('should return correct panel instance when with a specified panel' +
'module', function(done) {
this.MockTestPanel.mInnerFunction =
sinon.stub().returns(this.mockPanelInstance);
/**
* Make the dom element like:
* <div>
* <panel data-path="unit/mock_test_panel"></panel>
* </div>
*/
var pathElement = document.createElement('panel');
pathElement.dataset.path = 'unit/mock_test_panel';
this.panelElement.appendChild(pathElement);
this.PanelCache.get(this.panelID, (function(panelInstance) {
// ensure the returned panel instance is a TestPanel
assert.equal(panelInstance, this.mockPanelInstance);
done();
}).bind(this));
});
test('should return the same panel instance when inquiring with the ' +
'same panel id', function(done) {
var settingsPanelStub = sinon.stub().returns(this.mockPanelInstance);
this.MockSettingsPanel.mInnerFunction = settingsPanelStub;
this.PanelCache.get(this.panelID, (function(panelInstance) {
assert.equal(panelInstance, this.mockPanelInstance);
this.PanelCache.get(this.panelID, function(newPanelInstance) {
// ensure there is only one instance created
sinon.assert.calledOnce(settingsPanelStub);
// ensure the returned instance is the original instance
assert.equal(panelInstance, newPanelInstance);
done();
});
}).bind(this));
});
});
});

View file

@ -0,0 +1,152 @@
'use strict';
suite('Panel', function() {
suiteSetup(function(done) {
testRequire(['modules/panel'], (function(panelFunc) {
this.Panel = panelFunc;
done();
}).bind(this));
});
suite('Basic functions', function() {
setup(function() {
this.panel = this.Panel();
});
teardown(function() {
this.panel = null;
});
test('init()', function(done) {
// initialized shoule be false by default.
assert.isFalse(this.panel.initialized);
Promise.resolve(this.panel.init())
.then(function() {
// initialized shoule be true after initialized.
assert.isTrue(this.panel.initialized);
}.bind(this)).then(done, done);
});
test('uninit()', function(done) {
Promise.resolve(this.panel.init())
.then(function() {
assert.isTrue(this.panel.initialized);
this.panel.uninit();
// initialized shoule be false after uninitialized.
assert.isFalse(this.panel.initialized);
}.bind(this)).then(done, done);
});
test('beforeShow()', function(done) {
var initSpy = sinon.spy(this.panel, 'init');
var panelElement = document.createElement('div');
var options = {};
Promise.resolve(this.panel.beforeShow(panelElement, options))
.then(function() {
// init should be called when beforeShow is called at the first time.
sinon.assert.calledWith(initSpy, panelElement, options);
}).then(done, done);
});
test('show()', function(done) {
var initSpy = sinon.spy(this.panel, 'init');
var panelElement = document.createElement('div');
var options = {};
Promise.resolve(this.panel.show(panelElement, options))
.then(function() {
// init should be called when show is called at the first time.
sinon.assert.calledWith(initSpy, panelElement, options);
}).then(done, done);
});
});
suite('Internal functions', function() {
setup(function() {
this.mockOptions = {
onInit: function() {},
onUninit: function() {},
onShow: function() {},
onHide: function() {},
onBeforeShow: function() {},
onBeforeHide: function() {}
};
});
teardown(function() {
this.mockOptions = null;
});
function convertToInternalFuncName(name) {
return 'on' + name.charAt(0).toUpperCase() + name.slice(1);
}
['init', 'show', 'beforeShow'].forEach(function(funcName) {
var internalFuncName = convertToInternalFuncName(funcName);
test(internalFuncName + ' should be called when ' +
funcName + ' is called',
function(done) {
var spy = sinon.spy(this.mockOptions, internalFuncName);
var panel = this.Panel(this.mockOptions);
var panelElement = document.createElement('div');
var options = {};
Promise.resolve(panel[funcName](panelElement, options))
.then(function() {
sinon.assert.calledWith(spy, panelElement, options);
}).then(done, done);
});
});
['hide', 'beforeHide'].forEach(function(funcName) {
var internalFuncName = convertToInternalFuncName(funcName);
test(internalFuncName + ' should be called when ' +
funcName + ' is called',
function(done) {
var spy = sinon.spy(this.mockOptions, internalFuncName);
var panel = this.Panel(this.mockOptions);
Promise.resolve(panel[funcName]())
.then(function() {
sinon.assert.calledOnce(spy);
}).then(done, done);
});
});
test('onUninit should be called when uninit is called',
function(done) {
var spy = sinon.spy(this.mockOptions, 'onUninit');
var panel = this.Panel(this.mockOptions);
Promise.resolve(panel.init())
.then(function() {
panel.uninit();
sinon.assert.calledOnce(spy);
}).then(done, done);
});
test('onInit should be called only once', function(done) {
var spy = sinon.spy(this.mockOptions, 'onInit');
var panel = this.Panel(this.mockOptions);
Promise.resolve(panel.init())
.then(panel.init())
.then(function() {
// onInit should be called only once.
sinon.assert.calledOnce(spy);
}).then(done, done);
});
test('onUninit should not be called if the panel is not initialized',
function() {
var spy = sinon.spy(this.mockOptions, 'onUninit');
var panel = this.Panel(this.mockOptions);
panel.uninit();
sinon.assert.notCalled(spy);
});
});
});

View file

@ -0,0 +1,40 @@
'use strict';
suite('PanelUtils', function() {
var modules = [
'modules/panel_utils'
];
var map = {
'*': {
'modules/settings_cache': 'unit/mock_settings_cache'
}
};
suiteSetup(function(done) {
testRequire(modules, map, (function(PanelUtils) {
this.PanelUtils = PanelUtils;
done();
}).bind(this));
});
suite('activate', function() {
setup(function() {
this.targetElement = document.createElement('div');
this.targetLink = document.createElement('a');
this.targetElement.appendChild(this.targetLink);
this.sinon.spy(this.targetLink, 'addEventListener');
});
test('The navigated url should be the current url', function() {
var originalLink = 'http://www.originallink/';
var newLink = 'http://www.newlink/';
this.targetLink.href = originalLink;
this.PanelUtils.activate(this.targetElement);
this.targetLink.href = newLink;
assert.ok(this.targetLink.addEventListener.calledWith('click'));
});
});
});

View file

@ -0,0 +1,174 @@
'use strict';
suite('SettingsCache', function() {
suiteSetup(function(done) {
this.keyValuePairs = {
'key1': 'value1',
'key2': 'value2',
'key3': 'value3'
};
testRequire(['shared_mocks/mock_navigator_moz_settings'],
(function(MockNavigatorSettings) {
this.MockNavigatorSettings = MockNavigatorSettings;
this.realSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
// require settings cache after the mock of mozSettings loaded.
testRequire(['modules/settings_cache'], (function(SettingsCache) {
this.SettingsCache = SettingsCache;
done();
}).bind(this));
}).bind(this));
});
suiteTeardown(function() {
this.keyValuePairs = null;
navigator.mozSettings = this.realSettings;
this.realSettings = null;
this.MockNavigatorSettings = null;
});
setup(function() {
// init mozSettings
var settings = this.MockNavigatorSettings.mSettings;
for (var key in this.keyValuePairs) {
settings[key] = this.keyValuePairs[key];
}
// reset SettingsCache to its initial state
this.SettingsCache.reset();
});
teardown(function() {
this.MockNavigatorSettings.mTeardown();
});
suite('getSettings()', function() {
test('should return the same value as mozSettings',
function(done) {
this.SettingsCache.getSettings((function(settings) {
for (var key in this.keyValuePairs) {
assert.ok(settings[key] === this.keyValuePairs[key]);
}
done();
}).bind(this));
});
test('should queue the settings requests before the cache being ready',
function(done) {
this.MockNavigatorSettings.mSyncRepliesOnly = true;
var callCount = 10;
var count = 0;
var checkDone = function() {
count++;
if (callCount === count) {
done();
}
};
var gotSettings = (function(settings) {
for (var key in this.keyValuePairs) {
assert.ok(settings[key] === this.keyValuePairs[key]);
}
checkDone();
}).bind(this);
// query the settings
for (var i = 0; i < callCount; i++) {
this.SettingsCache.getSettings(gotSettings);
}
// trigger the mozSettings onsuccess 1s later
setTimeout(function() {
this.MockNavigatorSettings.mReplyToRequests();
}, 1000);
});
test('should reflect mozSettings changes', function(done) {
var key = 'key2';
var newValue = 'value2_1';
// make the cache as the current mozSettings.
this.SettingsCache.getSettings((function() {
var obj = {};
obj[key] = newValue;
// modify the value
var req = this.MockNavigatorSettings.createLock().set(obj);
req.onsuccess = (function() {
this.SettingsCache.getSettings(function(settings) {
assert.ok(settings[key] === newValue);
done();
});
}).bind(this);
}).bind(this));
});
test('should reflect newly created mozSettings fields', function(done) {
var newKey = 'key4';
var value = 'value4';
// make the cache as the current mozSettings.
this.SettingsCache.getSettings((function() {
var obj = {};
obj[newKey] = value;
// set the value
var req = this.MockNavigatorSettings.createLock().set(obj);
req.onsuccess = (function() {
this.SettingsCache.getSettings(function(settings) {
assert.ok(settings[newKey] === value);
done();
});
}).bind(this);
}).bind(this));
});
});
suite('event listeners', function() {
suiteSetup(function() {
// helper function to mutate setting
this.mutateSetting = (function(key, value, callback) {
var obj = {};
obj[key] = value;
var req = this.MockNavigatorSettings.createLock().set(obj);
req.onsuccess = callback;
}).bind(this);
});
suiteTeardown(function() {
this.mutateSetting = null;
});
test('addEventListener()', function(done) {
var key = 'key2';
var newValue = 'value2_1';
var onSettingsChange = function(event) {
assert.ok(event.settingName === key);
assert.ok(event.settingValue === newValue);
done();
};
this.SettingsCache.addEventListener('settingsChange', onSettingsChange);
this.mutateSetting(key, newValue);
});
test('removeEventListener()', function(done) {
var onSettingsChangeSpy = sinon.spy();
this.SettingsCache.addEventListener('settingsChange',
onSettingsChangeSpy);
this.mutateSetting('key2', 'value2_1', (function() {
// ensure the event listener has been added
sinon.assert.calledOnce(onSettingsChangeSpy);
this.SettingsCache.removeEventListener('settingsChange',
onSettingsChangeSpy);
this.mutateSetting('key2', 'value2_2', function() {
sinon.assert.calledOnce(onSettingsChangeSpy);
done();
});
}).bind(this));
});
});
});

View file

@ -0,0 +1,225 @@
'use strict';
suite('SettingsPanel', function() {
suiteSetup(function(done) {
testRequire([
'modules/settings_panel',
'modules/panel_utils',
'modules/settings_cache',
], (function(settingsPanelFunc, PanelUtils, SettingsCache) {
this.realL10n = document.l10n;
this.PanelUtils = PanelUtils;
this.SettingsCache = SettingsCache;
this.SettingsPanel = settingsPanelFunc;
done();
}).bind(this));
});
suite('Basic functions', function() {
setup(function() {
this.panel = this.SettingsPanel();
});
teardown(function() {
this.panel = null;
});
test('init()', function() {
var panelElement = document.createElement('div');
var activateSpy = sinon.spy(this.PanelUtils, 'activate');
// initialized should be false by default.
assert.isFalse(this.panel.initialized);
this.panel.init(panelElement);
// initialized should be true after initialized.
assert.isTrue(this.panel.initialized);
// PanelUtils.activate should be called with the panel element.
sinon.assert.calledWith(activateSpy, panelElement);
activateSpy.restore();
});
test('uninit()', function(done) {
var panelElement = document.createElement('div');
var settingsCacheRemoveEventListenerSpy =
this.sinon.spy(this.SettingsCache, 'removeEventListener');
var panelRemoveEventListenerSpy =
this.sinon.spy(panelElement, 'removeEventListener');
Promise.resolve(this.panel.init(panelElement))
.then(function() {
assert.isTrue(this.panel.initialized);
this.panel.uninit();
// initialized is false after uninitialized.
assert.isFalse(this.panel.initialized);
// Added listeners should be removed when uninit.
sinon.assert.calledWith(settingsCacheRemoveEventListenerSpy,
'settingsChange');
sinon.assert.calledWith(panelRemoveEventListenerSpy, 'change',
this.PanelUtils.onInputChange);
sinon.assert.calledWith(panelRemoveEventListenerSpy, 'click',
this.PanelUtils.onLinkClick);
settingsCacheRemoveEventListenerSpy.restore();
panelRemoveEventListenerSpy.restore();
}.bind(this)).then(done, done);
});
test('beforeShow()', function(done) {
var initSpy = sinon.spy(this.panel, 'init');
var panelElement = document.createElement('div');
var options = {};
var presetSpy = sinon.spy(this.PanelUtils, 'preset');
var settingsCacheAddEventListenerSpy =
sinon.spy(this.SettingsCache, 'addEventListener');
var panelAddEventListenerSpy =
sinon.spy(panelElement, 'addEventListener');
Promise.resolve(this.panel.beforeShow(panelElement, options))
.then(function() {
// init should be called when beforeShow is called at the first time.
sinon.assert.calledWith(initSpy, panelElement, options);
// PanelUtils.preset should be called with the panel element.
sinon.assert.calledWith(presetSpy, panelElement);
// Related listeners should be added.
sinon.assert.calledWith(settingsCacheAddEventListenerSpy,
'settingsChange');
sinon.assert.calledWith(panelAddEventListenerSpy, 'change',
this.PanelUtils.onInputChange);
sinon.assert.calledWith(panelAddEventListenerSpy, 'click',
this.PanelUtils.onLinkClick);
presetSpy.restore();
settingsCacheAddEventListenerSpy.restore();
panelAddEventListenerSpy.restore();
}.bind(this)).then(done, done);
});
test('show()', function(done) {
var initSpy = sinon.spy(this.panel, 'init');
var panelElement = document.createElement('div');
var options = {};
Promise.resolve(this.panel.show(panelElement, options))
.then(function() {
// init should be called when show is called at the first time.
sinon.assert.calledWith(initSpy, panelElement, options);
}).then(done, done);
});
test('hide()', function(done) {
var panelElement = document.createElement('div');
var settingsCacheRemoveEventListenerSpy =
this.sinon.spy(this.SettingsCache, 'removeEventListener');
var panelRemoveEventListenerSpy =
this.sinon.spy(panelElement, 'removeEventListener');
Promise.resolve(this.panel.init(panelElement))
.then(this.panel.hide())
.then(function() {
// Added listeners should be removed when hiding.
sinon.assert.calledWith(settingsCacheRemoveEventListenerSpy,
'settingsChange');
sinon.assert.calledWith(panelRemoveEventListenerSpy, 'change',
this.PanelUtils.onInputChange);
sinon.assert.calledWith(panelRemoveEventListenerSpy, 'click',
this.PanelUtils.onLinkClick);
settingsCacheRemoveEventListenerSpy.restore();
panelRemoveEventListenerSpy.restore();
}.bind(this)).then(done, done);
});
});
suite('Internal functions', function() {
setup(function() {
this.mockOptions = {
onInit: function() {},
onUninit: function() {},
onShow: function() {},
onHide: function() {},
onBeforeShow: function() {},
onBeforeHide: function() {}
};
});
teardown(function() {
this.mockOptions = null;
});
function convertToInternalFuncName(name) {
return 'on' + name.charAt(0).toUpperCase() + name.slice(1);
}
['init', 'show', 'beforeShow'].forEach(function(funcName) {
var internalFuncName = convertToInternalFuncName(funcName);
test(internalFuncName + ' should be called when ' +
funcName + ' is called',
function(done) {
var spy = sinon.spy(this.mockOptions, internalFuncName);
var panel = this.SettingsPanel(this.mockOptions);
var panelElement = document.createElement('div');
var options = {};
Promise
.resolve(panel[funcName](panelElement, options))
.then(function() {
sinon.assert.calledWith(spy, panelElement, options);
}).then(done, done);
});
});
['hide', 'beforeHide'].forEach(function(funcName) {
var internalFuncName = convertToInternalFuncName(funcName);
test(internalFuncName + ' shoule be called when ' +
funcName + ' is called',
function(done) {
var spy = sinon.spy(this.mockOptions, internalFuncName);
var panel = this.SettingsPanel(this.mockOptions);
Promise
.resolve(panel[funcName]())
.then(function() {
sinon.assert.calledOnce(spy);
}).then(done, done);
});
});
test('onUninit should be called when uninit is called',
function(done) {
var spy = sinon.spy(this.mockOptions, 'onUninit');
var panel = this.SettingsPanel(this.mockOptions);
Promise.resolve(panel.init())
.then(function() {
panel.uninit();
sinon.assert.calledOnce(spy);
}).then(done, done);
});
test('onInit should be called only once', function(done) {
var spy = sinon.spy(this.mockOptions, 'onInit');
var panel = this.SettingsPanel(this.mockOptions);
var panelElement = document.createElement('div');
Promise.resolve(panel.init(panelElement))
.then(panel.init(panelElement))
.then(function() {
sinon.assert.calledOnce(spy);
}).then(done, done);
});
test('onUninit should not be called if it is not initialized', function() {
var spy = sinon.spy(this.mockOptions, 'onUninit');
var panel = this.SettingsPanel(this.mockOptions);
panel.uninit();
sinon.assert.notCalled(spy);
});
});
});

View file

@ -0,0 +1,279 @@
'use strict';
suite('SettingsService', function() {
suiteSetup(function(done) {
navigator.addIdleObserver = sinon.spy();
var modules = [
'modules/settings_service',
'modules/panel_cache',
'unit/mock_settings_panel',
'settings'
];
// Use map to mock the dependencies.
// In this case for modules/panel_cache, we use unit/mock_settings_panel to
// replace modules/settings_panel.
var map = {
'modules/panel_cache': {
'modules/settings_panel': 'unit/mock_settings_panel'
},
'modules/settings_service': {
'modules/page_transitions': 'unit/mock_page_transitions'
},
'settings': 'unit/mock_settings'
};
testRequire(modules, map,
(function(SettingsService, PanelCache,
MockSettingsPanel, MockSettings) {
this.SettingsService = SettingsService;
this.PanelCache = PanelCache;
// Mock of the SettingsPanel function
this.MockSettingsPanel = MockSettingsPanel;
this.MockSettings = MockSettings;
done();
}).bind(this));
});
setup(function() {
this.SettingsService.reset();
this.PanelCache.reset();
this.options = [];
this.panelElements = [];
this.mockSettingsPanelInstances = [];
var panelInstance = function() {
return {
init: function() {},
uninit: function() {},
show: function() {},
hide: function() {},
beforeShow: function() {},
beforeHide: function() {}
};
};
for (var i = 0; i < 4; i++) {
this.options.push({ value: i });
this.mockSettingsPanelInstances.push(panelInstance());
// create panel elements
var panelElement = document.createElement('div');
panelElement.id = 'id' + i;
document.body.appendChild(panelElement);
this.panelElements.push(panelElement);
}
// create additional frame panel
var framePanelElement = document.createElement('div');
framePanelElement.id = 'frame';
document.body.appendChild(framePanelElement);
this.panelElements.push(framePanelElement);
this.callCount = 0;
this.MockSettingsPanel.mInnerFunction = (function() {
return this.mockSettingsPanelInstances[this.callCount++];
}).bind(this);
});
teardown(function() {
this.panelElements.forEach(function(panelElement) {
document.body.removeChild(panelElement);
});
});
suite('navigate()', function() {
test('should call to panel functions correctly', function(done) {
var mockInstances = [];
this.SettingsService.init();
mockInstances[0] = sinon.mock(this.mockSettingsPanelInstances[0]);
// Expect only calls to beforeShow and show of panel0.
mockInstances[0].expects('beforeShow').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('show').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('beforeHide').never();
mockInstances[0].expects('hide').never();
this.SettingsService.navigate('id0', this.options[0], (function() {
mockInstances[0].verify();
mockInstances[0] = sinon.mock(this.mockSettingsPanelInstances[0]);
// Expect only calls to beforeHide and hide of panel0.
mockInstances[0].expects('beforeShow').never();
mockInstances[0].expects('show').never();
mockInstances[0].expects('beforeHide').once();
mockInstances[0].expects('hide').once();
mockInstances[1] = sinon.mock(this.mockSettingsPanelInstances[1]);
// Expect only calls to beforeShow and show of panel1.
mockInstances[1].expects('beforeShow').once()
.withExactArgs(this.panelElements[1], this.options[1]);
mockInstances[1].expects('show').once()
.withExactArgs(this.panelElements[1], this.options[1]);
mockInstances[1].expects('beforeHide').never();
mockInstances[1].expects('hide').never();
this.SettingsService.navigate('id1', this.options[1], function() {
mockInstances[0].verify();
mockInstances[1].verify();
done();
});
}).bind(this));
});
test('should not deactivate the root panel', function(done) {
var mockInstances = [];
this.SettingsService.init({
rootPanelId: 'id0',
context: {
initialPanelId: 'id0',
activityHandler: null
}
});
mockInstances[0] = sinon.mock(this.mockSettingsPanelInstances[0]);
// Expect only calls to beforeShow and show of panel0.
mockInstances[0].expects('beforeShow').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('show').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('beforeHide').never();
mockInstances[0].expects('hide').never();
this.SettingsService.navigate('id0', this.options[0], (function() {
mockInstances[0].verify();
mockInstances[0] = sinon.mock(this.mockSettingsPanelInstances[0]);
// Expect only calls to beforeHide and hide of panel0.
mockInstances[0].expects('beforeShow').never();
mockInstances[0].expects('show').never();
mockInstances[0].expects('beforeHide').never();
mockInstances[0].expects('hide').never();
mockInstances[1] = sinon.mock(this.mockSettingsPanelInstances[1]);
// Expect only calls to beforeShow and show of panel1.
mockInstances[1].expects('beforeShow').once()
.withExactArgs(this.panelElements[1], this.options[1]);
mockInstances[1].expects('show').once()
.withExactArgs(this.panelElements[1], this.options[1]);
mockInstances[1].expects('beforeHide').never();
mockInstances[1].expects('hide').never();
this.SettingsService.navigate('id1', this.options[1], function() {
mockInstances[0].verify();
mockInstances[1].verify();
done();
});
}).bind(this));
});
test('should process the pending navigations correctly', function(done) {
var mockInstances = [];
for (var i = 0; i < 4; i++) {
mockInstances[i] = sinon.mock(this.mockSettingsPanelInstances[i]);
}
// Expect only calls to beforeShow and show of panel0.
mockInstances[0].expects('beforeShow').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('show').once()
.withExactArgs(this.panelElements[0], this.options[0]);
mockInstances[0].expects('beforeHide').once();
mockInstances[0].expects('hide').once();
// Expect only calls to beforeShow and show of panel3.
mockInstances[1].expects('beforeShow').once()
.withExactArgs(this.panelElements[3], this.options[3]);
mockInstances[1].expects('show').once()
.withExactArgs(this.panelElements[3], this.options[3]);
mockInstances[1].expects('beforeHide').never();
mockInstances[1].expects('hide').never();
// With the following calling sequence, the navigation to 'id1' and 'id2'
// will be ignored. We will only get two panel instances for panel0 and
// panel3 correspondingly.
this.SettingsService.navigate('id0', this.options[0]);
this.SettingsService.navigate('id1', this.options[1]);
this.SettingsService.navigate('id2', this.options[2]);
this.SettingsService.navigate('id3', this.options[3], (function() {
mockInstances.forEach(function(mockInstance) {
mockInstance.verify();
});
// The third and fourth panel instances should not be created.
assert.ok(this.callCount == 2,
'only two panel instances should be created');
done();
}).bind(this));
});
test('should override Settings._currentPanel', function() {
var fakePanelId = 'id0';
this.SettingsService.navigate(fakePanelId, {}, (function() {
assert.equal(this.MockSettings._currentPanel, '#' + fakePanelId);
}).bind(this));
});
test('can navigate to frame panel if panelId is app:app_name', function() {
var fakePanelId = 'app:bluetooth';
this.SettingsService.navigate(fakePanelId, {}, (function() {
assert.equal(this.MockSettings._currentPanel, '#frame');
}).bind(this));
});
test('can\'t navigate to non-trust apps', function() {
var fakePanelId = 'app:unknownApp';
this.sinon.spy(console, 'error');
this.SettingsService.navigate(fakePanelId, {});
console.error.calledWith('We only embed trust apps.');
});
});
suite('visibility change', function() {
var originalDocumentHidden;
setup(function(done) {
originalDocumentHidden = document.hidden;
this.SettingsService.init();
this.SettingsService.navigate('id0', this.options[0], function() {
this.mockInstance = sinon.mock(this.mockSettingsPanelInstances[0]);
done();
}.bind(this));
});
teardown(function() {
Object.defineProperty(document, 'hidden', {
value: originalDocumentHidden,
configurable: true
});
});
test('visible', function() {
Object.defineProperty(document, 'hidden', {
value: false,
configurable: true
});
this.mockInstance.expects('beforeShow').once()
.withExactArgs(this.panelElements[0], this.options[0]);
this.mockInstance.expects('show').once()
.withExactArgs(this.panelElements[0], this.options[0]);
window.dispatchEvent(new CustomEvent('visibilitychange'));
this.mockInstance.verify();
});
test('invisible', function() {
Object.defineProperty(document, 'hidden', {
value: true,
configurable: true
});
this.mockInstance.expects('beforeHide').once();
this.mockInstance.expects('hide').once();
window.dispatchEvent(new CustomEvent('visibilitychange'));
this.mockInstance.verify();
});
});
});

View file

@ -0,0 +1,59 @@
/* global MockSettings */
requireApp('settings/test/unit/mock_settings.js');
suite('SettingsUtils', function() {
'use strict';
var realSettings;
var settingsUtils;
var settingsService;
var map = {
'*': {
'modules/settings_service': 'unit/mock_settings_service',
'shared/lazy_loader': 'shared_mocks/mock_lazy_loader'
}
};
suiteSetup(function() {
realSettings = window.Settings;
window.Settings = MockSettings;
});
suiteTeardown(function() {
window.Settings = realSettings;
});
setup(function(done) {
testRequire([
'modules/settings_utils',
'unit/mock_settings_service'
], map, function(SettingsUtils, MockSettingsService) {
settingsUtils = SettingsUtils;
settingsService = MockSettingsService;
done();
});
});
suite('loadTemplate', function() {
var spyCallback;
setup(function() {
spyCallback = this.sinon.spy();
});
test('with element', function() {
var element = {
innerHTML: 'html'
};
this.sinon.stub(document, 'getElementById').returns(element);
settingsUtils.loadTemplate('real', spyCallback);
assert.isTrue(spyCallback.calledWith(element.innerHTML));
});
test('without element', function() {
this.sinon.stub(document, 'getElementById').returns(null);
settingsUtils.loadTemplate('fake', spyCallback);
assert.isTrue(spyCallback.calledWith(null));
});
});
});

View file

@ -0,0 +1,68 @@
suite('SimSecurity', function() {
'use strict';
var simSecurity;
setup(function(done) {
testRequire([
'modules/sim_security'
], {}, function(SimSecurity) {
simSecurity = SimSecurity;
simSecurity._getIccByCardIndex = function(cardIndex) {
var func = function() {
return Promise.resolve();
};
return {
unlockCardLock: func,
setCardLock: func,
updateContact: func,
getCardLock: func,
getCardLockRetryCount: func
};
};
done();
});
});
['pin', 'pin2', 'fdn'].forEach(function(lockType) {
[true, false].forEach(function(enabled) {
var fakeCardIndex = 0;
var status = enabled ? 'enabled' : 'disabled';
var eventName = lockType + '-' + status;
test(eventName + ' works', function(done) {
simSecurity.addEventListener(eventName, function(evt) {
var cardIndex = evt.detail;
assert.equal(cardIndex, fakeCardIndex);
done();
});
var apiName = enabled ? 'setCardLock' : 'unlockCardLock';
simSecurity[apiName](fakeCardIndex, {
lockType: lockType,
pin: '1234',
enabled: enabled
});
});
});
});
['pin', 'pin2', 'puk2'].forEach(function(lockType) {
var fakeCardIndex = 0;
var eventName = lockType + '-changed';
test(eventName + ' works', function(done) {
simSecurity.addEventListener(eventName, function(evt) {
var cardIndex = evt.detail;
assert.equal(cardIndex, fakeCardIndex);
done();
});
simSecurity.setCardLock(fakeCardIndex, {
lockType: lockType,
pin: '1234',
newPin: '0000'
});
});
});
});

View file

@ -0,0 +1,175 @@
'use strict';
suite('StateModel', function() {
var StateModel;
var modules = [
'modules/state_model'
];
suiteSetup(function(done) {
testRequire(modules, (stateModel) => {
StateModel = stateModel;
done();
});
});
suite('initial state', function() {
test('the state should change from the initial state to the first state',
function(done) {
var initialState = 'initialState';
var firstState = 'firstState';
var model = StateModel({
_state: initialState,
onInit: function() {
return this._state;
},
onGetState: function() {
return new Promise((resolve) => {
setTimeout(() => {
this._state = firstState;
resolve(this._state);
});
});
},
onSetState: function() {}
});
model.observe('currentState', function() {
// Ensure the first state.
if (model.currentState === firstState) {
done();
}
});
// Ensure the initial state.
assert.equal(model.targetState, initialState);
assert.equal(model.currentState, initialState);
});
});
suite('set target state', function() {
test('the final state should be the last state if success', function(done) {
var initialState = 'initialState';
var finalTargetState = 'finalState';
// Note that OPERATION_TIME should be larger than the total time required
// for performing the random user actions.
var OPERATION_TIME = 300;
var USER_ACTION_TIME = 10;
var model = StateModel({
_state: initialState,
onInit: function() {
return this._state;
},
onGetState: function() {
return Promise.resolve(this._state);
},
onSetState: function(newState) {
// a fake time consuming operation
return new Promise((resolve) => {
setTimeout(() => {
this._state = newState;
resolve(this._state);
}, OPERATION_TIME);
});
}
});
model.observe('transitioning', function(transitioning) {
if (transitioning) {
return;
}
assert.equal(model.targetState, finalTargetState);
assert.equal(model.currentState, finalTargetState);
done();
});
// The following code tries to simulate random actions from the user and
// stop at `finalTargetState`.
var count = 0;
var intervalId = setInterval(function() {
count++;
if (count === 11) {
clearInterval(intervalId);
model.targetState = finalTargetState;
} else {
// Some random actions.
model.targetState = (Math.random() * 10) % 10;
}
}, USER_ACTION_TIME);
});
test('the final state should be the reported state if failed',
function(done) {
var initialState = 'initialState';
var targetState = 'targetState';
var realState = 'realState';
var model = StateModel({
_state: initialState,
onInit: function() {
return this._state;
},
onGetState: function() {
return Promise.resolve(this._state);
},
onSetState: function(newState) {
// a fake time consuming operation
return new Promise((resolve, reject) => {
setTimeout(() => {
this._state = realState;
reject(this._state);
});
});
}
});
model.observe('transitioning', function(transitioning) {
if (transitioning) {
return;
}
assert.equal(model.targetState, realState);
assert.equal(model.currentState, realState);
done();
});
model.targetState = targetState;
});
});
suite('state change', function() {
test('the final state should change to the real state', function(done) {
var initialState = 'initialState';
var realState = 'realState';
var _onStateChange = null;
var model = StateModel({
_state: initialState,
onInit: function(onStateChange) {
_onStateChange = onStateChange;
return this._state;
},
onGetState: function() {
return Promise.resolve(this._state);
},
onSetState: function() {}
});
var targetStateChecked = false;
var currentStateChecked = false;
model.observe('targetState', function() {
targetStateChecked = model.targetState == realState;
if (targetStateChecked && currentStateChecked) {
done();
}
});
model.observe('currentState', function() {
currentStateChecked = model.currentState == realState;
if (targetStateChecked && currentStateChecked) {
done();
}
});
// trigger the state change
_onStateChange('realState');
});
});
});

View file

@ -0,0 +1,196 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
suite('settings/SyncManagerBridge >', () => {
var subject;
var realMozApps;
var port;
var app;
var onmessageData;
suiteSetup(done => {
navigator.addIdleObserver = sinon.spy();
realMozApps = navigator.mozApps;
port = {
postMessage: function() {},
set onmessage(callback) {
setTimeout(() => {
callback({ data: onmessageData });
});
}
};
app = {
connect: function() {
return Promise.resolve([port]);
}
};
var onsuccess = {
set onsuccess(callback) {
setTimeout(() => {
callback({ target: { result: app } });
});
}
};
navigator.mozApps = {
getSelf: function() {
return onsuccess;
}
};
testRequire(['modules/sync_manager_bridge'], SyncManagerBridge => {
subject = SyncManagerBridge;
done();
});
});
suiteTeardown(() => {
navigator.mozApps = realMozApps;
});
suite('Initial state', () => {
test('Integrity', () => {
assert.ok(subject !== null);
assert.isNull(subject._port);
assert.equal(subject._requests.size, 0);
});
});
['enable',
'disable',
'sync'].forEach(method => {
suite(method, () => {
var getSelfSpy;
var connectSpy;
var postMessageSpy;
setup(() => {
getSelfSpy = this.sinon.spy(navigator.mozApps, 'getSelf');
connectSpy = this.sinon.spy(app, 'connect');
postMessageSpy = this.sinon.spy(port, 'postMessage');
});
teardown(() => {
getSelfSpy.restore();
connectSpy.restore();
postMessageSpy.restore();
});
suiteTeardown(() => {
subject._port = null;
});
test(method + ' - no previous connection', done => {
subject[method]();
setTimeout(() => {
this.sinon.assert.calledOnce(getSelfSpy);
this.sinon.assert.calledOnce(connectSpy);
this.sinon.assert.calledOnce(postMessageSpy);
assert.equal(postMessageSpy.getCall(0).args[0].name, method);
done();
});
});
test(method + ' - previous connection', done => {
subject[method]();
setTimeout(() => {
this.sinon.assert.notCalled(getSelfSpy);
this.sinon.assert.notCalled(connectSpy);
this.sinon.assert.calledOnce(postMessageSpy);
assert.equal(postMessageSpy.getCall(0).args[0].name, method);
done();
});
});
});
});
suite('getInfo', () => {
var postMessageSpy;
var dateNowStub;
var onsyncchangeSpy;
var now;
setup(() => {
postMessageSpy = this.sinon.spy(port, 'postMessage');
dateNowStub = this.sinon.stub(Date, 'now', () => {
return now;
});
subject.onsyncchange = () => {};
onsyncchangeSpy = this.sinon.spy(subject, '_onsyncchange');
});
teardown(() => {
postMessageSpy.restore();
dateNowStub.restore();
onsyncchangeSpy.restore();
});
test('getInfo', done => {
now = 'now';
var expectedMessage = {
data: {
id: now,
result: 'whatever'
}
};
subject.getInfo().then(message => {
assert.equal(message.id, expectedMessage.data.id);
assert.equal(message.result, expectedMessage.data.result);
this.sinon.assert.notCalled(onsyncchangeSpy);
done();
});
setTimeout(() => {
this.sinon.assert.calledOnce(postMessageSpy);
assert.equal(postMessageSpy.getCall(0).args[0].name, 'getInfo');
assert.equal(postMessageSpy.getCall(0).args[0].id, now);
assert.ok(subject._requests.has(now));
subject.onmessage(expectedMessage);
});
});
});
suite('onsyncchange', () => {
var postMessageSpy;
var onsyncchangeSpy;
var now;
setup(() => {
postMessageSpy = this.sinon.spy(port, 'postMessage');
subject.onsyncchange = () => {};
onsyncchangeSpy = this.sinon.spy(subject, '_onsyncchange');
});
teardown(() => {
postMessageSpy.restore();
onsyncchangeSpy.restore();
});
test('getInfo', done => {
var expectedMessage = {
data: {
name: 'onsyncchange',
result: 'whatever'
}
};
subject.onmessage(expectedMessage);
setTimeout(() => {
this.sinon.assert.notCalled(postMessageSpy);
assert.ok(!(subject._requests.has(now)));
this.sinon.assert.calledOnce(onsyncchangeSpy);
assert.equal(onsyncchangeSpy.getCall(0).args[0].result,
expectedMessage.data.result);
done();
});
});
});
});

View file

@ -0,0 +1,245 @@
/* global MockNavigatorSettings, MockNavigatorMozWifiManager */
requireApp('settings/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp(
'settings/shared/test/unit/mocks/mock_navigator_moz_wifi_manager.js');
suite('WifiContext', function() {
'use strict';
var realSettings;
var wifiHelper;
var wifiContext;
var wifiManager;
var modules = [
'modules/wifi_context',
'shared/wifi_helper'
];
var maps = {
'*': {
'modules/settings_cache': 'unit/mock_settings_cache',
'shared/wifi_helper': 'shared_mocks/mock_wifi_helper'
}
};
suiteSetup(function() {
realSettings = window.navigator.mozSettings;
window.navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
window.navigator.mozSettings = realSettings;
});
setup(function(done) {
testRequire(modules, maps,
function(WifiContext, MockWifiHelper) {
wifiContext = WifiContext;
wifiHelper = MockWifiHelper;
wifiManager = wifiHelper.getWifiManager();
MockNavigatorMozWifiManager.mSetup();
done();
});
});
suite('WifiStatusTextChange', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
wifiContext.addEventListener('wifiStatusTextChange', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiStatusTextChange', fakeCb);
});
test('when wifiManger.onenabled, trigger cb', function() {
wifiManager.onenabled();
assert.isTrue(fakeCb.called);
});
test('when wifiManger.ondisabled, trigger cb', function() {
wifiManager.ondisabled();
assert.isTrue(fakeCb.called);
});
test('when wifiManger.onstatuschange, trigger cb', function() {
wifiManager.onstatuschange();
assert.isTrue(fakeCb.called);
});
});
suite('WifiEnabled', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
wifiContext.addEventListener('wifiEnabled', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiEnabled', fakeCb);
});
test('when wifiManager.onenabled, trigger cb', function() {
wifiManager.onenabled();
assert.isTrue(fakeCb.called);
});
});
suite('WifiStatusChange', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
wifiContext.addEventListener('wifiStatusChange', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiStatusChange', fakeCb);
});
test('when wifiManager.onstatuschange, trigger cb', function() {
wifiManager.onstatuschange();
assert.isTrue(fakeCb.called);
});
});
suite('WifiConnectionInfoUpdate', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
wifiContext.addEventListener('wifiConnectionInfoUpdate', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiConnectionInfoUpdate', fakeCb);
});
test('when wifiManager.onconnectioninfoupdate, trigger cb', function() {
wifiManager.onconnectioninfoupdate();
assert.isTrue(fakeCb.called);
});
});
suite('WifiNetworkForgotten', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
wifiContext.addEventListener('wifiNetworkForgotten', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiNetworkForgotten', fakeCb);
});
test('when wifiContext.forgetNetwork callback, trigger cb', function() {
wifiContext.forgetNetwork({}, () => {
assert.isTrue(fakeCb.called);
});
});
});
suite('wifiStatusText', function() {
suiteTeardown(function() {
delete(wifiManager.connection.status);
});
test('wifiManager is enabled, get fullstatus', function() {
wifiManager.enabled = true;
wifiManager.connection.status = 'disconnected';
wifiManager.onstatuschange();
assert.equal(wifiContext.wifiStatusText.id, 'full-status-disconnected');
});
test('wifiManager is disabled, get disabled string', function() {
wifiManager.enabled = false;
wifiManager.onstatuschange();
assert.equal(wifiContext.wifiStatusText.id, 'disabled');
});
});
suite('forgetNetwork', function() {
setup(function() {
this.sinon.spy(wifiManager, 'forget');
});
test('will bypass to wifiManager', function() {
wifiContext.forgetNetwork({});
assert.isTrue(wifiManager.forget.called);
assert.isNull(wifiContext.currentNetwork);
});
});
suite('associateNetwork', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
this.sinon.spy(wifiManager, 'associate');
this.sinon.spy(wifiManager, 'forget');
wifiContext.addEventListener('wifiWrongPassword', fakeCb);
});
test('will bypass to wifiManager', function() {
var fakeNetwork = {
known: false,
password: '1234',
ssid: 'fake-network'
};
wifiContext.associateNetwork(fakeNetwork);
assert.isTrue(wifiManager.associate.calledWith(fakeNetwork));
assert.equal(wifiContext.currentNetwork, fakeNetwork);
wifiManager.enabled = true;
wifiManager.connection.status = 'connected';
wifiManager.onstatuschange({
network: {
ssid: fakeNetwork.ssid
},
status: wifiManager.connection.status
});
assert.isFalse(fakeCb.calledWith());
assert.isFalse(wifiManager.forget.calledWith(fakeNetwork));
});
});
suite('Connecting failed', function() {
var fakeCb;
setup(function() {
fakeCb = sinon.spy();
this.sinon.spy(wifiManager, 'associate');
this.sinon.spy(wifiManager, 'forget');
wifiContext.addEventListener('wifiWrongPassword', fakeCb);
});
teardown(function() {
wifiContext.removeEventListener('wifiWrongPassword', fakeCb);
});
test('when connectingfailed, trigger cb', function() {
var fakeNetwork = {
known: false,
password: '1234',
ssid: 'fake-network'
};
wifiContext.associateNetwork(fakeNetwork);
wifiManager.enabled = true;
wifiManager.connection.status = 'connectingfailed';
wifiManager.onstatuschange({
network: {
ssid: fakeNetwork.ssid
},
status: wifiManager.connection.status
});
assert.isTrue(fakeCb.calledWith());
assert.isTrue(wifiManager.forget.calledWith(fakeNetwork));
});
});
});

View file

@ -0,0 +1,439 @@
/* global MockL10n, MockNavigatorSettings, MockNavigatorMozWifiManager,
MockNavigatorSettings */
require('/shared/test/unit/mocks/mock_l20n.js');
requireApp('settings/shared/test/unit/mocks/mock_navigator_moz_settings.js');
requireApp('settings/shared/test/unit/load_body_html_helper.js');
requireApp(
'settings/shared/test/unit/mocks/mock_navigator_moz_wifi_manager.js');
suite('WifiUtils', function() {
'use strict';
var realL10n;
var realSettings;
var wifiUtils;
var settingsUtils;
var map = {
'*': {
'modules/settings_utils': 'unit/mock_settings_utils',
'shared/wifi_helper': 'shared_mocks/mock_wifi_helper'
}
};
suiteSetup(function() {
realL10n = document.l10n;
document.l10n = MockL10n;
realSettings = window.navigator.mozSettings;
window.navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
document.l10n = realL10n;
window.navigator.mozSettings = realSettings;
});
setup(function(done) {
testRequire([
'modules/wifi_utils',
'unit/mock_settings_utils',
], map, function(WifiUtils, MockSettingsUtils) {
wifiUtils = WifiUtils;
settingsUtils = MockSettingsUtils;
MockNavigatorSettings.mSetup();
MockNavigatorMozWifiManager.mSetup();
done();
});
});
test('newExplanationItem', function() {
var li = wifiUtils.newExplanationItem('test');
assert.equal(li.className, 'explanation');
assert.equal(li.getAttribute('data-l10n-id'), 'test');
});
suite('updateListItemStatus', function() {
var fakeNetworkKey = 'network1';
var fakeListItems;
var fakeListItemDOM;
var fakeActiveItemDOM;
setup(function() {
this.sinon.stub(wifiUtils, 'getNetworkKey').returns(fakeNetworkKey);
fakeListItems = {};
fakeListItemDOM = document.createElement('div');
fakeListItemDOM.appendChild(document.createElement('small'));
fakeListItemDOM.appendChild(document.createElement('aside'));
fakeActiveItemDOM = document.createElement('div');
fakeActiveItemDOM.appendChild(document.createElement('small'));
fakeActiveItemDOM.appendChild(document.createElement('aside'));
fakeListItems[fakeNetworkKey] = fakeListItemDOM;
});
suite('with no needed parameters', function() {
setup(function() {
this.sinon.stub(window.console, 'log');
wifiUtils.updateListItemStatus();
});
test('we will get error message', function() {
assert.isTrue(window.console.log.called);
});
});
suite('if we are connecting to an AP with `connecting` status', function() {
setup(function() {
wifiUtils.updateListItemStatus({
network: {},
networkStatus: 'connecting',
listItems: fakeListItems,
activeItemDOM: fakeActiveItemDOM
});
});
test('activeItemDOM is cleaned up to normal status', function() {
assert.isFalse(fakeActiveItemDOM.classList.contains('active'));
assert.equal(fakeActiveItemDOM.querySelector('small').dataset.l10nId,
'short-status-disconnected');
assert.isFalse(fakeActiveItemDOM.querySelector(
'aside').classList.contains('connecting'));
assert.isFalse(fakeActiveItemDOM.querySelector(
'aside').classList.contains('connected'));
});
test('listItemDOM is in right status', function() {
assert.isTrue(fakeListItemDOM.classList.contains('active'));
assert.equal(fakeListItemDOM.querySelector('small').dataset.l10nId,
'short-status-connecting');
assert.isTrue(fakeListItemDOM.querySelector(
'aside').classList.contains('connecting'));
assert.isFalse(fakeListItemDOM.querySelector(
'aside').classList.contains('connected'));
});
});
suite('if we are connecting to an AP with `connected` status', function() {
setup(function() {
wifiUtils.updateListItemStatus({
network: {},
networkStatus: 'connected',
listItems: fakeListItems,
activeItemDOM: fakeActiveItemDOM
});
});
test('listItemDOM is in right status', function() {
assert.isTrue(fakeListItemDOM.classList.contains('active'));
assert.equal(fakeListItemDOM.querySelector('small').dataset.l10nId,
'short-status-connected');
assert.isTrue(fakeListItemDOM.querySelector(
'aside').classList.contains('connected'));
assert.isFalse(fakeListItemDOM.querySelector(
'aside').classList.contains('connecting'));
});
});
});
suite('changeDisplay', function() {
var dialog;
var eap;
var identity;
var password;
var authPhase2;
var certificate;
var description;
setup(function() {
loadBodyHTML('./_wifi_utils.html');
dialog = document.getElementById('wifi-auth');
eap = dialog.querySelector('li.eap select');
identity = dialog.querySelector('input[name=identity]');
password = dialog.querySelector('input[name=password]');
authPhase2 = dialog.querySelector('li.auth-phase2 select');
certificate = dialog.querySelector('li.server-certificate select');
description = dialog.querySelector('li.server-certificate-description');
});
teardown(function() {
document.body.innerHTML = '';
});
test('panel is wifi-auth, security is WEP', function() {
wifiUtils.changeDisplay(dialog, 'WEP');
assert.equal(identity.parentNode.style.display, 'none');
assert.equal(password.parentNode.style.display, 'block');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'none');
assert.equal(certificate.parentNode.parentNode.style.display, 'none');
assert.equal(description.style.display, 'none');
});
test('panel is wifi-auth, security is WPA-PSK', function() {
wifiUtils.changeDisplay(dialog, 'WPA-PSK');
assert.equal(identity.parentNode.style.display, 'none');
assert.equal(password.parentNode.style.display, 'block');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'none');
assert.equal(certificate.parentNode.parentNode.style.display, 'none');
assert.equal(description.style.display, 'none');
});
test('panel is wifi-auth, security is WPA-EAP, eap is SIM', function() {
eap.value = 'SIM';
wifiUtils.changeDisplay(dialog, 'WPA-EAP');
assert.equal(identity.parentNode.style.display, 'none');
assert.equal(password.parentNode.style.display, 'none');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'none');
assert.equal(certificate.parentNode.parentNode.style.display, 'none');
assert.equal(description.style.display, 'none');
});
test('panel is wifi-auth, security is WPA-EAP, eap is PEAP', function() {
eap.value = 'PEAP';
wifiUtils.changeDisplay(dialog, 'WPA-EAP');
assert.equal(identity.parentNode.style.display, 'block');
assert.equal(password.parentNode.style.display, 'block');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'block');
assert.equal(certificate.parentNode.parentNode.style.display, 'block');
assert.equal(description.style.display, 'block');
});
test('panel is wifi-auth, security is WPA-EAP, eap is TLS', function() {
eap.value = 'TLS';
wifiUtils.changeDisplay(dialog, 'WPA-EAP');
assert.equal(identity.parentNode.style.display, 'block');
assert.equal(password.parentNode.style.display, 'block');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'block');
assert.equal(certificate.parentNode.parentNode.style.display, 'block');
assert.equal(description.style.display, 'block');
});
test('panel is wifi-auth, security is WPA-EAP, eap is TTLS', function() {
eap.value = 'TTLS';
wifiUtils.changeDisplay(dialog, 'WPA-EAP');
assert.equal(identity.parentNode.style.display, 'block');
assert.equal(password.parentNode.style.display, 'block');
assert.equal(authPhase2.parentNode.parentNode.style.display, 'block');
assert.equal(certificate.parentNode.parentNode.style.display, 'block');
assert.equal(description.style.display, 'block');
});
test('panel is wifi-auth, security is none', function() {
wifiUtils.changeDisplay(dialog, 'none');
assert.equal(identity.parentNode.style.display, 'none');
assert.equal(password.parentNode.style.display, 'none');
});
});
test('loadImportedCertificateOptions', function() {
var selectDOM = document.createElement('select');
selectDOM.appendChild(createOption('--'));
selectDOM.appendChild(createOption('1'));
selectDOM.appendChild(createOption('2'));
MockNavigatorMozWifiManager._certificateList = [
'cert1', 'cert2', 'cert3'
];
// the total options would be one default item and certificates
var expectedLengthOfOptions =
1 + MockNavigatorMozWifiManager._certificateList.length;
this.sinon.spy(selectDOM, 'remove');
this.sinon.spy(selectDOM, 'add');
wifiUtils.loadImportedCertificateOptions(selectDOM);
// we have three options, but we would keep the first one
assert.isTrue(selectDOM.remove.calledTwice);
// and we would add three options back based on the number of our fake
// certificates
assert.isTrue(selectDOM.add.calledThrice);
// and the total options should be one default item and certificates
assert.equal(selectDOM.options.length, expectedLengthOfOptions);
});
suite('newListItem', function() {
test('WPA-PSK network - not configured, in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: ['WPA-PSK'],
relSignalStrength: 40,
known: false,
};
var l10nSpy = this.sinon.spy(document.l10n, 'setAttributes');
var listItem = wifiUtils.newListItem({
network: testNetwork
});
var icon = listItem.querySelector('aside');
assert.ok(icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.ok(l10nSpy.calledWith(small, 'securedBy',
{capabilities: 'WPA-PSK'}));
});
test('WPA-PSK network - configured, in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: ['WPA-PSK'],
relSignalStrength: 40,
known: true,
};
var l10nSpy = this.sinon.spy(document.l10n, 'setAttributes');
var listItem = wifiUtils.newListItem({
network: testNetwork
});
var icon = listItem.querySelector('aside');
assert.ok(icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.ok(l10nSpy.calledWith(small, 'securedBy',
{capabilities: 'WPA-PSK'}));
});
test('WPA-PSK network - configured, not in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: ['WPA-PSK'],
relSignalStrength: 0,
known: true,
};
var listItem = wifiUtils.newListItem({
network: testNetwork,
showNotInRange: true
});
var icon = listItem.querySelector('aside');
assert.ok(icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.equal(small.getAttribute('data-l10n-id'), 'notInRange');
});
test('Open network - not configured, in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: [],
relSignalStrength: 40,
known: false,
};
var listItem = wifiUtils.newListItem({
network: testNetwork
});
var icon = listItem.querySelector('aside');
assert.ok(!icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.equal(small.getAttribute('data-l10n-id'), 'securityOpen');
});
test('OPEN network - configured, in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: [],
relSignalStrength: 40,
known: true,
};
var listItem = wifiUtils.newListItem({
network: testNetwork
});
var icon = listItem.querySelector('aside');
assert.ok(!icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.equal(small.getAttribute('data-l10n-id'), 'securityOpen');
});
test('OPEN network - configured, not in range', function() {
var testNetwork = {
ssid: 'Dummy network',
security: [],
relSignalStrength: 0,
known: true,
};
var listItem = wifiUtils.newListItem({
network: testNetwork,
showNotInRange: true
});
var icon = listItem.querySelector('aside');
assert.ok(!icon.classList.contains('secured'));
var a = listItem.querySelector('a');
var ssid = a.querySelector('span');
assert.equal(ssid.textContent, testNetwork.ssid);
var small = a.querySelector('small');
assert.equal(small.getAttribute('data-l10n-id'), 'notInRange');
});
});
suite('updateNetworkSignal', function() {
var testNetwork;
var availableNetworks;
var listItem;
var networkIcon;
setup(function() {
loadBodyHTML('./_wifi_utils.html');
testNetwork = {
ssid: 'Dummy network',
security: ['WPA-PSK'],
relSignalStrength: 40,
known: false,
};
listItem = wifiUtils.newListItem({
network: testNetwork
});
networkIcon = listItem.querySelector('aside');
availableNetworks = document.querySelector('ul.wifi-availableNetworks');
availableNetworks.appendChild(listItem);
});
teardown(function() {
document.body.innerHTML = '';
});
test('Signal Strength changes', function() {
assert.isTrue(networkIcon.classList.contains('level-2'));
wifiUtils.updateNetworkSignal(testNetwork, 100);
assert.isTrue(networkIcon.classList.contains('level-4'));
});
});
function createOption(value) {
var dom = document.createElement('option');
dom.text = value;
dom.value = value;
return dom;
}
});

View file

@ -0,0 +1,127 @@
'use strict';
suite('about > factory_reset', function() {
var factoryReset;
var realNavigatorPower;
var modules = [
'panels/about/factory_reset'
];
var maps = {};
var MockPower = {
factoryReset: function() {}
};
var elements = {
resetButton: document.createElement('button'),
resetDialog: document.createElement('form'),
resetConfirm: document.createElement('button'),
resetCancel: document.createElement('button')
};
setup(function(done) {
testRequire(modules, maps,
function(FactoryReset) {
realNavigatorPower = navigator.mozPower;
navigator.mozPower = MockPower;
factoryReset = FactoryReset();
elements.resetDialog.hidden = true;
factoryReset._elements = elements;
done();
});
});
suiteTeardown(function() {
navigator.mozPower = realNavigatorPower;
realNavigatorPower = null;
});
suite('initiation', function() {
setup(function() {
this.sinon.stub(factoryReset, '_resetClick');
});
test('resetButton is enabled when mozPower exist', function() {
factoryReset.init(elements);
assert.equal(factoryReset._elements.resetButton.disabled, false);
});
test('resetButton is disabled when mozPower does not exist', function() {
navigator.mozPower = null;
factoryReset.init(elements);
assert.equal(factoryReset._elements.resetButton.disabled, true);
});
test('resetButton is enabled when mozPower exist', function() {
factoryReset.init(elements);
factoryReset._elements.resetButton.click();
assert.ok(factoryReset._resetClick.called);
});
});
suite('resetClick', function() {
setup(function() {
this.sinon.stub(factoryReset, '_factoryReset');
factoryReset.init(elements);
});
test('resetDialog is shown when calling resetClick', function() {
assert.equal(factoryReset._elements.resetDialog.hidden, true);
factoryReset._resetClick();
assert.equal(factoryReset._elements.resetDialog.hidden, false);
});
test('resetDialog is hidden when cancel button is clicked', function() {
factoryReset._resetClick();
assert.equal(factoryReset._elements.resetDialog.hidden, false);
factoryReset._elements.resetCancel.click();
assert.equal(factoryReset._elements.resetDialog.hidden, true);
});
test('factoryReset is called when confirm button is clicked', function() {
factoryReset._resetClick();
assert.equal(factoryReset._elements.resetDialog.hidden, false);
factoryReset._elements.resetConfirm.click();
assert.ok(factoryReset._factoryReset.called);
assert.equal(factoryReset._elements.resetDialog.hidden, true);
});
test('if mozPower is not defined, dialog is still hidden ' +
'when confirm button is clicked', function() {
navigator.mozPower = null;
factoryReset._resetClick();
assert.equal(factoryReset._elements.resetDialog.hidden, false);
factoryReset._elements.resetConfirm.click();
assert.ok(factoryReset._factoryReset.called);
assert.equal(factoryReset._elements.resetDialog.hidden, true);
});
});
suite('factoryReset', function() {
setup(function() {
this.sinon.spy(navigator.mozPower, 'factoryReset');
this.sinon.stub(console, 'error');
});
test('mozPower factoryReset is called', function() {
factoryReset._factoryReset();
assert.ok(navigator.mozPower.factoryReset.called);
});
test('if mozPower is not defined, return error',
function() {
navigator.mozPower = null;
factoryReset._factoryReset();
assert.ok(console.error.calledWith('Cannot get mozPower'));
});
test('if mozPower factoryReset is not defined, ' +
'mozPower factoryReset should not be called', function() {
navigator.mozPower.factoryReset = null;
factoryReset._factoryReset();
assert.ok(
console.error.calledWith('Cannot invoke mozPower.factoryReset()'));
});
});
});

View file

@ -0,0 +1,227 @@
/* global MockNavigatorMozMobileConnections, MockNavigatorMozIccManager */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
suite('about > hardware_info', function() {
var hardwareInfo;
var realL10n;
var realNavigatorSettings;
var realMobileConnections;
var realIccManager;
var modules = [
'shared_mocks/mock_l20n',
'shared_mocks/mock_navigator_moz_settings',
'panels/about/hardware_info'
];
var maps = {};
var elements = {
updateStatus: document.createElement('li'),
deviceInfoPhoneNum: document.createElement('li'),
deviceInfoMsisdns: document.createElement('li')
};
setup(function(done) {
testRequire(modules, maps,
function(MockL10n, MockNavigatorSettings, HardwareInfo) {
realL10n = document.l10n;
document.l10n = MockL10n;
realIccManager = navigator.mozIccManager;
navigator.mozIccManager = MockNavigatorMozIccManager;
realMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realNavigatorSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
hardwareInfo = HardwareInfo();
hardwareInfo._elements = elements;
done();
});
});
suiteTeardown(function() {
document.l10n = realL10n;
realL10n = null;
navigator.mozSettings = realNavigatorSettings;
realNavigatorSettings = null;
navigator.mozMobileConnections = realMobileConnections;
realMobileConnections = null;
navigator.mozIccManager = realIccManager;
realIccManager = null;
});
suite('initiation', function() {
setup(function() {
this.sinon.stub(hardwareInfo, '_loadHardwareInfo');
hardwareInfo.init(elements);
});
test('_loadHardwareInfo and _loadLastUpdated are called while initiate',
function() {
assert.ok(hardwareInfo._loadHardwareInfo.called);
});
});
suite('loadHardwareInfo >', function() {
var iccIds = ['12345', '22345'];
setup(function() {
MockNavigatorMozIccManager.addIcc(iccIds[0]);
MockNavigatorMozMobileConnections[0].iccId = iccIds[0];
this.sinon.spy(hardwareInfo, '_renderPhoneNumberElement');
});
suite('single sim', function() {
test('the list item should be hidden when without iccinfo',
function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo = null;
hardwareInfo.init(elements);
assert.isTrue(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
test('should show unknown phone number when no msisdn and mdn',
function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo = {};
hardwareInfo._loadHardwareInfo();
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{}, 0, false));
assert.isFalse(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
test('should show correct value when with mdn', function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo =
{ mdn: 'mdn' };
hardwareInfo._loadHardwareInfo();
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{ mdn: 'mdn' }, 0, false));
assert.isFalse(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
test('should show correct value when with msisdn', function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo =
{ msisdn: 'msisdn' };
hardwareInfo._loadHardwareInfo();
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{ msisdn: 'msisdn' }, 0, false));
assert.isFalse(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
});
suite('multiple sim', function() {
setup(function() {
MockNavigatorMozIccManager.addIcc(iccIds[1]);
MockNavigatorMozMobileConnections.mAddMobileConnection();
MockNavigatorMozMobileConnections[1].iccId = iccIds[1];
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo =
{ mdn: 'mdn1' };
MockNavigatorMozIccManager.getIccById(iccIds[1]).iccInfo =
{ mdn: 'mdn2' };
});
test('the list item should be hidden when without iccinfo',
function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo = null;
MockNavigatorMozIccManager.getIccById(iccIds[1]).iccInfo = null;
hardwareInfo._loadHardwareInfo();
assert.isTrue(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
test('should show the list item when there are iccinfos', function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo = null;
MockNavigatorMozIccManager.getIccById(iccIds[1]).iccInfo = {};
hardwareInfo._loadHardwareInfo();
assert.isFalse(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
});
test('should show unknown phone number when no msisdn and mdn',
function() {
MockNavigatorMozIccManager.getIccById(iccIds[0]).iccInfo = {};
MockNavigatorMozIccManager.getIccById(iccIds[1]).iccInfo = {};
hardwareInfo._loadHardwareInfo();
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{}, 0, true));
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{}, 1, true));
});
test('should show correct sim indicator', function() {
hardwareInfo._loadHardwareInfo();
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{ mdn: 'mdn1' }, 0, true));
assert.ok(hardwareInfo._renderPhoneNumberElement.calledWith(
{ mdn: 'mdn2' }, 1, true));
});
});
});
suite('_renderPhoneNumberElement >', function() {
suite('single sim', function() {
test('should show unknown phone number when no msisdn and mdn',
function() {
var span = hardwareInfo._renderPhoneNumberElement({}, 0, false);
assert.ok(span.getAttribute('data-l10n-id', 'unknown-phoneNumber'));
});
test('should show correct value when with mdn', function() {
var span = hardwareInfo._renderPhoneNumberElement(
{ mdn: 'mdn' }, 0, false);
assert.equal(span.textContent, 'mdn');
});
test('should show correct value when with msisdn', function() {
var span = hardwareInfo._renderPhoneNumberElement(
{ msisdn: 'msisdn' }, 0, false);
assert.isFalse(hardwareInfo._elements.deviceInfoPhoneNum.hidden);
assert.equal(span.textContent, 'msisdn');
});
});
suite('multiple sim', function() {
test('should show unknown phone number when no msisdn and mdn',
function() {
this.sinon.spy(document.l10n, 'setAttributes');
var spans0 = hardwareInfo._renderPhoneNumberElement({}, 0, true);
assert.equal(document.l10n.setAttributes.args[0][0], spans0);
assert.equal(document.l10n.setAttributes.args[0][1],
'unknown-phoneNumber-sim');
assert.equal(document.l10n.setAttributes.args[0][2].index,
1);
var spans1 = hardwareInfo._renderPhoneNumberElement({}, 1, true);
assert.equal(document.l10n.setAttributes.args[1][0], spans1);
assert.equal(document.l10n.setAttributes.args[1][1],
'unknown-phoneNumber-sim');
assert.equal(document.l10n.setAttributes.args[1][2].index,
2);
});
test('should show correct sim indicator', function() {
hardwareInfo._loadHardwareInfo();
var spans0 = hardwareInfo._renderPhoneNumberElement(
{ mdn: 'mdn1' }, 0, true);
var spans1 = hardwareInfo._renderPhoneNumberElement(
{ mdn: 'mdn2' }, 1, true);
var sim1Args = JSON.parse(spans0.getAttribute('data-l10n-args'));
var sim2Args = JSON.parse(spans1.getAttribute('data-l10n-args'));
assert.equal(spans0.getAttribute('data-l10n-id'),
'deviceInfo-MSISDN-with-index');
assert.equal(spans1.getAttribute('data-l10n-id'),
'deviceInfo-MSISDN-with-index');
assert.equal(sim1Args.index, 1);
assert.equal(sim1Args.msisdn, 'mdn1');
assert.equal(sim2Args.index, 2);
assert.equal(sim2Args.msisdn, 'mdn2');
});
});
});
});

Some files were not shown because too many files have changed in this diff Show more