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,14 @@
<!DOCTYPE html>
<html>
<body role="application">
<form role="dialog" data-type="confirm" id="pairing-request-timeout" hidden>
<section>
<h1 data-l10n-id="pairing-request-timeout-title">Bluetooth Pairing Request</h1>
<p data-l10n-id="pairing-request-timeout-description">Bluetooth pairing request expired. Try to pair again.</p>
</section>
<menu>
<button data-l10n-id="ok" class="full recommend" id="incoming-pairing-timeout-confirm">OK</button>
</menu>
</form>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<body role="application">
<form role="dialog" data-type="confirm" id="pair-view">
<section>
<h2 data-l10n-id="pair-view-title"></h2>
<h1 id="label-name"></h1>
<p id="pair-description"></p>
<span id="confirmation-method" >
<span id="passkey"></span>
</span>
<span id="pin-input-method" hidden>
<input type="text" id="pin-input" size="16" maxlength="16" />
</span>
<span id="passkey-input-method" hidden>
<input type="number" id="passkey-input" x-inputmode="digits" size="6" maxlength="6" />
</span>
</section>
<menu>
<button id="button-close" data-l10n-id="cancel">Cancel</button>
<button id="button-pair" class="recommend" data-l10n-id="pair">Pair</button>
</menu>
</form>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<body role="application" class="skin-organic theme-settings uninit">
<!-- Enable Bluetooth View -->
<form role="dialog" data-type="confirm" id="enable-bluetooth-view" hidden>
<section>
<h1 data-l10n-id="confirmation">Confirmation</h1>
<p>
<img id="icon" src="style/images/settings_bluetooth.png" alt="Bluetooth Icon"/>
<strong data-l10n-id="bluetooth">Bluetooth</strong>
<small data-l10n-id="bluetooth-status-description">Bluetooth is disabled</small>
</p>
<p data-l10n-id="turn-bluetooth-on">Do you want to turn Bluetooth on?</p>
</section>
<menu>
<button type="reset" id="enable-bluetooth-button-cancel" data-l10n-id="cancel">Cancel</button>
<button type="submit" class="recommend" id="enable-bluetooth-button-turn-on" data-l10n-id="turn-on">Turn On</button>
</menu>
</form>
<!-- Bluetooth File Transfer Failed View -->
<form role="dialog" data-type="confirm" id="alert-view" hidden>
<section>
<h1 data-l10n-id="error-transfer-title">Bluetooth file transfer failed</h1>
<p data-l10n-id="error-transfer-settings">
Bluetooth file transfer failed. Check that the Bluetooth settings are correct.
</p>
</section>
<menu>
<button class="full danger" id="alert-button-ok" data-l10n-id="ok">OK</button>
</menu>
</form>
<!-- Devices List View -->
<section role="region" id="devices-list-view" class="devices-list-view current" data-rendered="true" hidden>
<gaia-header ignore-dir id="devices-list-header" class="devices-list-header" action="close">
<h1 data-l10n-id="bluetooth"></h1>
</gaia-header>
<div>
<!-- Devices List View :: Paired Devices List -->
<gaia-subheader id="bluetooth-paired-title" class="bluetooth-paired-title" skin="organic">
<span data-l10n-id="bluetooth-paired-devices">Paired Devices</span>
</gaia-subheader>
<ul id="bluetooth-paired-devices" class="bluetooth-paired-devices devices">
</ul>
<!-- Devices List View :: Found Devices List -->
<gaia-subheader id="bluetooth-found-title" class="bluetooth-found-title" skin="organic">
<span data-l10n-id="bluetooth-devices-in-area">Devices found</span>
</gaia-subheader>
<ul id="bluetooth-devices" class="bluetooth-devices devices">
</ul>
<!-- Devices List View :: Search Button -->
<div id="bluetooth-searching" data-l10n-id="search-for-device" class="bluetooth-searching explanation">
Searching for devices…
</div>
<ul id="bluetooth-search" class="bluetooth-search">
<li>
<label>
<button id="search-device" class="search-device" data-l10n-id="search-device" disabled>Search for Devices</button>
</label>
</li>
</ul>
</div>
</section>
</body>
</html>

View file

@ -0,0 +1,76 @@
/* global MocksHelper, MockL10n, MockNavigatorSettings,
MockMozBluetooth, gDeviceList */
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_bluetooth.js');
function switchReadOnlyProperty(originObject, propName, targetObj) {
Object.defineProperty(originObject, propName, {
configurable: true,
get: function() { return targetObj; }
});
}
var mocksForDeviceListHelper = new MocksHelper([
'NavigatorSettings'
]).init();
suite('Bluetooth app > deviceList ', function() {
var realL10n;
var realMozSettings;
var realMozBluetooth;
var deviceList;
var bluetoothSearch;
var searchAgainBtn;
var searchingItem;
var exitButton;
mocksForDeviceListHelper.attachTestHelpers();
suiteSetup(function(done) {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockL10n;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
realMozBluetooth = navigator.mozBluetooth;
switchReadOnlyProperty(navigator, 'mozBluetooth', MockMozBluetooth);
loadBodyHTML('./_transfer.html');
requireApp('bluetooth/js/deviceList.js', done);
});
suiteTeardown(function() {
navigator.mozSettings = realMozSettings;
switchReadOnlyProperty(navigator, 'mozBluetooth', realMozBluetooth);
window.navigator.mozL10n = realL10n;
document.body.innerHTML = '';
});
setup(function() {
deviceList = document.getElementById('devices-list-view');
bluetoothSearch = document.getElementById('bluetooth-search');
searchAgainBtn = document.getElementById('search-device');
searchingItem = document.getElementById('bluetooth-searching');
exitButton = document.getElementById('cancel-activity');
});
suite('deviceList > ', function() {
test('list is updated with arg "true" ', function() {
gDeviceList.update(true);
assert.isFalse(bluetoothSearch.hidden);
assert.isFalse(searchingItem.hidden);
});
test('list is updated with arg "false" ', function() {
gDeviceList.update(false);
assert.isTrue(bluetoothSearch.hidden);
assert.isTrue(searchingItem.hidden);
});
});
});

View file

@ -0,0 +1,12 @@
define(function() {
'use strict';
var MockCannotTransferDialog = {
init: function mctd_init() {},
showConfirm: function mctd_showConfirm() {},
get isVisible() {},
close: function mctd_close() {}
};
return MockCannotTransferDialog;
});

View file

@ -0,0 +1,10 @@
/* exported MockgDeviceList */
'use strict';
var MockgDeviceList = {
update: function mdl_updateDeviceList() {},
initWithAdapter: function mdl_initial() {},
uninit: function mdl_uninit() {},
startDiscovery: function mdl_startDiscovery() {},
onDeviceFound: function mdl_onDeviceFound() {}
};

View file

@ -0,0 +1,12 @@
define(function() {
'use strict';
var MockPairExpiredDialog = {
init: function mped_init() {},
showConfirm: function mped_showConfirm() {},
get isVisible() {},
close: function mped_close() {}
};
return MockPairExpiredDialog;
});

View file

@ -0,0 +1,8 @@
/* exported MockPairManager */
'use strict';
var MockPairManager = {
setConfirmation: function mpm_setConfirmation(address, confirmed) {},
setPinCode: function mpm_setPinCode(address, pincode) {},
setPasskey: function mpm_setPasskey(address, passkey) {}
};

View file

@ -0,0 +1,10 @@
define(function() {
'use strict';
var MockPairview = {
init: function mpv_init(mode, method, device, passkey) {},
closeInput: function mpv_closeInput() {}
};
return MockPairview;
});

View file

@ -0,0 +1,12 @@
define(function() {
'use strict';
var MockTurnBluetoothOnDialog = {
init: function mtbod_init() {},
showConfirm: function mtbod_showConfirm() {},
get isVisible() {},
close: function mtbod_close() {}
};
return MockTurnBluetoothOnDialog;
});

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,618 @@
/* 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, 'observe');
sinon.spy(result, 'unobserve');
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 observe = this.observableArray.observe;
assert.ok(observe.calledWith('insert'));
assert.ok(observe.calledWith('remove'));
assert.ok(observe.calledWith('replace'));
assert.ok(observe.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 observe = withNew.returnValues[0].observe;
assert.ok(observe.calledWith('insert'));
assert.ok(observe.calledWith('remove'));
assert.ok(observe.calledWith('replace'));
assert.ok(observe.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 observe = this.observableArray.observe;
assert.ok(observe.calledWith('insert'));
assert.ok(observe.calledWith('remove'));
assert.ok(observe.calledWith('replace'));
assert.ok(observe.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.unobserve.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 unobserve with a method
assert.equal(this.observableArray.unobserve.callCount, 1);
var arg = this.observableArray.unobserve.args[0][0];
assert.isFunction(arg);
});
test('creating another ListView destroys old', function() {
this.observableArray.observe.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.unobserve.called);
// re-observes
assert.ok(this.observableArray.observe.called);
// and make sure its done in the right order
assert.ok(
this.observableArray.unobserve.calledBefore(
this.observableArray.observe
)
);
});
});
});

View file

@ -0,0 +1,275 @@
'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.observe(event, this.spies[event]);
}, this);
});
suite('pop()', function() {
setup(function() {
this.observable.pop();
});
checkSpies({ remove: 1 });
checkArgs('remove', {
type: 'remove',
data: {
index: methods.length - 1,
count: 1
}
});
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',
data: {
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',
data: {
index: 1,
count: 2
}
});
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',
data: {
index: 1,
count: 2
}
});
checkArgs('insert', {
type: 'insert',
data: {
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',
data: {
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',
data: {
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',
data: {
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({});
});
suite('unobserve', function() {
setup(function() {
// bind some listeners
this.spies = [this.sinon.spy(), this.sinon.spy()];
events.forEach(function(event) {
this.observable.observe(event, this.spies[0]);
this.observable.observe(event, this.spies[1]);
}, this);
});
test('(property, handler)', function() {
// remove handler for property
this.observable.unobserve('insert', this.spies[0]);
// insert, only other handler should be called
this.observable.push({});
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 1);
// remove, both handlers should be called
this.observable.pop();
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]);
// insert, only other handler should be called
this.observable.push({});
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 1);
// remove, only other handler should be called
this.observable.pop();
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('insert');
// insert, neither handler should be called
this.observable.push({});
assert.equal(this.spies[0].callCount, 0);
assert.equal(this.spies[1].callCount, 0);
// remove, both handlers should be called
this.observable.pop({});
assert.equal(this.spies[0].callCount, 1);
assert.equal(this.spies[1].callCount, 1);
});
});
});
});

View file

@ -0,0 +1,110 @@
'use strict';
suite('Observable', function() {
suiteSetup(function(done) {
testRequire(['modules/mvvm/observable'], (function(Observable) {
this.Observable = Observable;
done();
}).bind(this));
});
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.observe, 'function');
});
suite('Creates observable properties', function() {
var template = {
testBoolean: true,
testNumber: 10,
testString: 'test',
'test object': {}
// currently disabled - we don't handle functions
// testFunction: 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);
});
});
});
});

View file

@ -0,0 +1,623 @@
/* global MockL10n, MockNavigatormozSetMessageHandler, MockNavigatorSettings,
MockNavigatormozApps, MockMozBluetooth, MockNotificationHelper */
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_bluetooth.js');
require('/shared/test/unit/mocks/mock_notification_helper.js');
function switchReadOnlyProperty(originObject, propName, targetObj) {
Object.defineProperty(originObject, propName, {
configurable: true,
get: function() { return targetObj; }
});
}
suite('Bluetooth app > PairManager ', function() {
var realL10n;
var realNotificationHelper;
var realSetMessageHandler;
var realMozSettings;
var realMozApps;
var realMozBluetooth;
var AdapterManager;
var BtContext;
var PairManager;
var PairExpiredDialog;
var Pairview;
suiteSetup(function(done) {
realL10n = window.navigator.mozL10n;
navigator.mozL10n = MockL10n;
realNotificationHelper = window.NotificationHelper;
window.NotificationHelper = MockNotificationHelper;
realSetMessageHandler = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozBluetooth = navigator.mozBluetooth;
switchReadOnlyProperty(navigator, 'mozBluetooth', MockMozBluetooth);
MockNavigatormozSetMessageHandler.mSetup();
var modules = [
'unit/mock_pair_expired_dialog',
'unit/mock_pair_view',
'modules/bluetooth/bluetooth_adapter_manager',
'modules/bluetooth/bluetooth_context',
'modules/pair_manager'
];
var maps = {
'*': {
'views/pair_expired_dialog': 'unit/mock_pair_expired_dialog',
'modules/bluetooth/bluetooth_adapter_manager': 'MockAdapterManager',
'modules/bluetooth/bluetooth_context': 'MockBluetoothContext'
}
};
this.MockAdapterManager = {
defaultAdapter: {},
observe: function() {}
};
define('MockAdapterManager', function() {
return this.MockAdapterManager;
}.bind(this));
this.MockBluetoothContext = {
callbacks: {
'enabled': []
},
observe: function(eventName, callback) {
this.callbacks[eventName].push(callback);
}
};
define('MockBluetoothContext', function() {
return this.MockBluetoothContext;
}.bind(this));
var requireCtx = testRequire([], maps, function() {});
requireCtx(modules, function(pairExpiredDialog, pairview, adapterManager,
btContext, pairManager) {
PairExpiredDialog = pairExpiredDialog;
Pairview = pairview;
AdapterManager = adapterManager;
BtContext = btContext;
PairManager = pairManager;
done();
}.bind(this));
});
suiteTeardown(function() {
MockNavigatormozApps.mTeardown();
switchReadOnlyProperty(navigator, 'mozBluetooth', realMozBluetooth);
navigator.mozApps = realMozApps;
MockNavigatormozSetMessageHandler.mTeardown();
navigator.mozSettings = realMozSettings;
MockNavigatorSettings.mTeardown();
navigator.mozSetMessageHandler = realSetMessageHandler;
navigator.mozL10n = realL10n;
window.NotificationHelper = realNotificationHelper;
});
suite('init > ', function() {
setup(function() {
this.sinon.stub(AdapterManager, 'observe');
this.sinon.spy(BtContext, 'observe');
this.sinon.stub(PairManager, '_onDefaultAdapterChanged');
this.sinon.stub(PairManager, '_watchOndisplaypasskeyreq');
this.sinon.stub(PairManager, '_watchOnenterpincodereq');
this.sinon.stub(PairManager, '_watchOnpairingconfirmationreq');
this.sinon.stub(PairManager, '_watchOnpairingconsentreq');
this.sinon.stub(PairManager, '_watchOnpairingaborted');
this.sinon.stub(PairManager, '_onRequestPairingFromSystemMessage');
this.sinon.stub(PairManager, 'showPendingPairing');
this.sinon.stub(PairManager, 'onBluetoothDisabled');
PairManager.init();
});
teardown(function() {
MockNavigatorSettings.mTeardown();
});
suite('observe "defaultAdapter" from AdapterManager > ', function() {
test('AdapterManager "defaultAdapter" property should be observed, ' +
'and access defaultAdapter from AdapterManager manually ',
function() {
assert.isTrue(AdapterManager.observe.calledWith('defaultAdapter'));
assert.isTrue(PairManager._onDefaultAdapterChanged.calledWith(
AdapterManager.defaultAdapter));
});
});
suite('should watch pairing request events from dom event > ', function() {
test('"_watchOndisplaypasskeyreq", "_watchOnenterpincodereq", ' +
'"_watchOnpairingconfirmationreq", "_watchOnpairingconsentreq", ' +
'"_watchOnpairingaborted" should be called ', function() {
assert.isTrue(PairManager._watchOndisplaypasskeyreq.called);
assert.isTrue(PairManager._watchOnenterpincodereq.called);
assert.isTrue(PairManager._watchOnpairingconfirmationreq.called);
assert.isTrue(PairManager._watchOnpairingconsentreq.called);
assert.isTrue(PairManager._watchOnpairingaborted.called);
});
});
suite('fire "bluetooth-pairing-request" event > ', function() {
var message, eventName;
setup(function() {
message = {};
eventName = 'bluetooth-pairing-request';
MockNavigatormozSetMessageHandler.mTrigger(eventName, message);
});
test('handle event with _onRequestPairingFromSystemMessage()',
function() {
assert.isTrue(
PairManager._onRequestPairingFromSystemMessage.calledWith(message),
'the "_onRequestPairingFromSystemMessage" should be called with ' +
'"message" object after received "bluetooth-pairing-request" event ');
});
});
suite('screen locked status change > ', function() {
var lockscreenKey = 'lockscreen.locked';
setup(function() {
MockNavigatorSettings.mTriggerObservers(lockscreenKey,
{settingValue: false});
});
test('observes settings key "lockscreen.locked"', function() {
assert.equal(MockNavigatorSettings.mObservers[lockscreenKey].length, 1);
});
test('showPendingPairing() should be called with arg event while ' +
'lockscreen.locked settings key value changed ', function() {
assert.isTrue(PairManager.showPendingPairing.calledWith(false));
});
});
suite('observe "enabled" from BtContext > ', function() {
var property;
setup(function() {
property = 'enabled';
});
test('BtContext "enabled" property should be observed, ' +
'onBluetoothDisabled() should be called ' +
'while BtContext "enabled" property changed to false ', function() {
assert.isTrue(BtContext.observe.calledWith(property));
BtContext.callbacks[property][0](false);
assert.isTrue(PairManager.onBluetoothDisabled.called);
});
test('onBluetoothDisabled() should not be called ' +
'while BtContext "enabled" property changed to true ', function() {
BtContext.callbacks[property][0](true);
assert.isFalse(PairManager.onBluetoothDisabled.called);
});
});
});
suite('_onDefaultAdapterChanged > ', function() {
var mockNewAdapter;
setup(function() {
mockNewAdapter = {};
PairManager._defaultAdapter = null;
});
test('_defaultAdapter should be defined ', function() {
PairManager._onDefaultAdapterChanged(mockNewAdapter);
assert.equal(PairManager._defaultAdapter, mockNewAdapter);
});
});
suite('_watchOndisplaypasskeyreq > ', function() {
var mockDefaultAdapter, mockEvent, mockEventName, expectedPairingInfo;
setup(function() {
mockDefaultAdapter = {
pairingReqs: {
addEventListener: function() {}
}
};
PairManager._defaultAdapter = mockDefaultAdapter;
this.sinon.stub(PairManager._defaultAdapter.pairingReqs,
'addEventListener');
this.sinon.stub(PairManager, '_handlePairingRequest');
mockEvent = {};
mockEventName = 'displaypasskey';
expectedPairingInfo = {
method: mockEventName,
evt: mockEvent
};
});
test('pairingReqs.ondisplaypasskeyreq should be registered callback',
function() {
PairManager._watchOndisplaypasskeyreq();
// addEventListener
assert.equal(
PairManager._defaultAdapter.pairingReqs.addEventListener.args[0][0],
'displaypasskeyreq');
PairManager._defaultAdapter.pairingReqs.addEventListener.args[0][1](
mockEvent, mockEventName);
// _handlePairingRequest
assert.deepEqual(PairManager._handlePairingRequest.args[0][0],
expectedPairingInfo);
});
});
suite('_watchOnpairingaborted > ', function() {
var mockDefaultAdapter, mockEvent;
setup(function() {
mockDefaultAdapter = {
addEventListener: function() {}
};
PairManager._defaultAdapter = mockDefaultAdapter;
this.sinon.stub(PairManager._defaultAdapter, 'addEventListener');
this.sinon.stub(PairManager, '_onPairingAborted');
mockEvent = {};
});
test('_defaultAdapter.onpairingaborted should be registered callback ' +
'function ', function() {
PairManager._watchOnpairingaborted();
assert.equal(PairManager._defaultAdapter.addEventListener.args[0][0],
'pairingaborted');
PairManager._defaultAdapter.addEventListener.args[0][1](mockEvent);
assert.isTrue(PairManager._onPairingAborted.calledWith(mockEvent));
});
});
suite('_onPairingAborted > ', function() {
var mockEvent;
setup(function() {
this.sinon.stub(Pairview, 'closeInput');
PairManager.pendingPairing = {};
PairManager.childWindow = {
Pairview: Pairview,
close: this.sinon.spy()
};
switchReadOnlyProperty(PairExpiredDialog, 'isVisible', true);
this.sinon.stub(PairExpiredDialog, 'close');
this.sinon.stub(window, 'close');
});
test('Pairview.closeInput() should be called, childWindow should be close' +
'window.close() should be called, pendingPairing should be null, ' +
'PairExpiredDialog.close() should be called ', function() {
PairManager._onPairingAborted(mockEvent);
assert.isTrue(PairManager.childWindow.Pairview.closeInput.called);
assert.isTrue(PairManager.childWindow.close.called);
assert.isNull(PairManager.pendingPairing);
assert.isTrue(PairExpiredDialog.close.called);
assert.isTrue(window.close.called);
});
});
suite('_handlePairingRequest > ', function() {
var pairingInfo = {
method: 'confirmation',
evt: {}
};
suite('screen locked mode > ', function() {
setup(function(done) {
this.sinon.stub(PairManager, 'fireNotification');
this.sinon.stub(PairManager, 'cleanPendingPairing');
this.sinon.stub(PairManager, 'showPairview');
MockNavigatorSettings.mSettings['lockscreen.locked'] = true;
PairManager._handlePairingRequest(pairingInfo);
setTimeout(done);
});
test('handle showPairview() from _handlePairingRequest() ', function() {
assert.isTrue(PairManager.fireNotification.calledWith(pairingInfo),
'fireNotification() should be called after do _handlePairingRequest()' +
' in screen lock mode');
assert.isFalse(PairManager.cleanPendingPairing.called,
'cleanPendingPairing() should not be called after do ' +
'_handlePairingRequest() in screen lock mode');
assert.isFalse(PairManager.showPairview.called,
'showPairview() should not be called after do _handlePairingRequest()' +
' in screen lock mode');
});
});
suite('screen unlocked mode > ', function() {
setup(function(done) {
this.sinon.stub(PairManager, 'fireNotification');
this.sinon.stub(PairManager, 'cleanPendingPairing');
this.sinon.stub(PairManager, 'showPairview');
MockNavigatorSettings.mSettings['lockscreen.locked'] = false;
PairManager._handlePairingRequest(pairingInfo);
setTimeout(done);
});
test('handle showPairview() from _handlePairingRequest() ', function() {
assert.isFalse(PairManager.fireNotification.called,
'fireNotification() should not be called after ' +
'do _handlePairingRequest() in screen lock mode');
assert.isTrue(PairManager.cleanPendingPairing.called,
'cleanPendingPairing() should not be called after do ' +
'_handlePairingRequest() in screen lock mode');
assert.isTrue(PairManager.showPairview.calledWith(pairingInfo),
'showPairview() should be called after do onRequestPairing() ' +
'in screen unlock mode');
});
});
});
suite('fireNotification > ', function() {
var notificationStub, mockNotification, pairingInfo, titleResult,
optionsResult;
setup(function() {
this.sinon.stub(PairManager, 'showPairview');
this.sinon.stub(PairManager, 'pairingRequestExpiredNotificationHandler');
mockNotification = {};
notificationStub =
this.sinon.stub(window.NotificationHelper, 'send').returns(
Promise.resolve(mockNotification));
PairManager.pendingPairing = null;
pairingInfo = {
evt: {
deviceName: 'device-01'
}
};
titleResult = 'bluetooth-pairing-request-now-title';
optionsResult = {
bodyL10n: { raw: pairingInfo.evt.deviceName },
icon: 'app://bluetooth.gaiamobile.org/style/images/icon_bluetooth.png',
tag: 'pairing-request',
closeOnClick: false
};
});
test('pendingPairing, new and fired one notification, set notification ' +
'onclick handler ', function(done) {
PairManager.fireNotification(pairingInfo).then(() => {
assert.isDefined(PairManager.pendingPairing.showPairviewCallback);
PairManager.pendingPairing.showPairviewCallback();
assert.isTrue(PairManager.showPairview.calledWith(pairingInfo));
assert.isTrue(notificationStub.calledOnce);
assert.equal(notificationStub.firstCall.args[0], titleResult);
assert.deepEqual(notificationStub.firstCall.args[1], optionsResult);
assert.isDefined(mockNotification.onclick);
mockNotification.onclick();
assert.isTrue(
PairManager.pairingRequestExpiredNotificationHandler.called);
done();
});
});
});
suite('pairingRequestExpiredNotificationHandler > ', function() {
var mockNotification;
suite('screen locked mode > ', function() {
setup(function(done) {
mockNotification = {
close: function() {/* do something */}
};
this.sinon.spy(mockNotification, 'close');
this.sinon.stub(PairExpiredDialog, 'showConfirm');
MockNavigatorSettings.mSettings['lockscreen.locked'] = true;
PairManager.pairingRequestExpiredNotificationHandler(mockNotification);
setTimeout(done);
});
test('notification should not be close, pair expired dialog should not ' +
'be show with confirmation', function() {
assert.isFalse(mockNotification.close.called);
assert.isFalse(PairExpiredDialog.showConfirm.called);
});
});
suite('screen unlocked mode > ', function() {
setup(function(done) {
mockNotification = {
close: function() {/* do something */}
};
this.sinon.spy(mockNotification, 'close');
switchReadOnlyProperty(PairExpiredDialog, 'isVisible', false);
this.sinon.stub(PairExpiredDialog, 'showConfirm');
MockNavigatorSettings.mSettings['lockscreen.locked'] = false;
PairManager.pairingRequestExpiredNotificationHandler(mockNotification);
setTimeout(done);
});
teardown(function() {
mockNotification = null;
});
test('notification should be close, confirm dialog should not be ' +
'called if pair request not expired yet', function() {
PairManager._isExpired = false;
assert.isTrue(mockNotification.close.called);
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.isFalse(PairExpiredDialog.showConfirm.called);
assert.isFalse(MockNavigatormozApps.mAppWasLaunched);
});
test('notification should be close, confirm dialog should be called ' +
'if it is not visible', function() {
PairManager._isExpired = true;
assert.isTrue(mockNotification.close.called);
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.isTrue(PairExpiredDialog.showConfirm.called);
assert.isFalse(MockNavigatormozApps.mAppWasLaunched);
});
test('notification should be close, confirm dialog should not be called' +
' if it is visible', function() {
switchReadOnlyProperty(PairExpiredDialog, 'isVisible', true);
assert.isTrue(mockNotification.close.called);
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.isFalse(PairExpiredDialog.showConfirm.called);
assert.ok(MockNavigatormozApps.mAppWasLaunched);
});
});
});
suite('showPendingPairing > ', function() {
var cleanPendingPairingStub;
suite('screen unlocked mode > with pending pairing', function() {
setup(function() {
cleanPendingPairingStub =
sinon.stub(PairManager, 'cleanPendingPairing');
PairManager.pendingPairing.showPairviewCallback = this.sinon.spy();
});
test('showPairviewCallback() should be called, cleanPendingPairing() ' +
'should be called', function() {
PairManager.showPendingPairing(false);
assert.isTrue(PairManager.pendingPairing.showPairviewCallback.called);
assert.isTrue(cleanPendingPairingStub.called);
cleanPendingPairingStub.restore();
});
});
suite('screen unlocked mode > without pending pairing', function() {
setup(function() {
cleanPendingPairingStub =
sinon.stub(PairManager, 'cleanPendingPairing');
PairManager.pendingPairing = null;
});
test('showPairviewCallback() should not be called, ' +
'cleanPendingPairing() should not be called', function() {
PairManager.showPendingPairing(false);
assert.isFalse(cleanPendingPairingStub.called);
cleanPendingPairingStub.restore();
});
});
suite('screen locked mode > with pending pairing', function() {
setup(function() {
cleanPendingPairingStub =
sinon.stub(PairManager, 'cleanPendingPairing');
PairManager.pendingPairing = {
showPairviewCallback: this.sinon.spy()
};
});
test('showPairviewCallback() should not be called, ' +
'cleanPendingPairing() should not be called', function() {
PairManager.showPendingPairing(true);
assert.isFalse(PairManager.pendingPairing.showPairviewCallback.called);
assert.isFalse(cleanPendingPairingStub.called);
cleanPendingPairingStub.restore();
});
});
});
suite('cleanPendingPairing > ', function() {
setup(function() {
this.sinon.stub(PairManager, 'cleanNotifications');
PairManager.pendingPairing = {};
});
test('pendingPairing should be null, cleanNotifications() should be ' +
'called', function() {
PairManager.cleanPendingPairing();
assert.equal(PairManager.pendingPairing, null);
assert.isTrue(PairManager.cleanNotifications.called);
});
});
suite('cleanNotifications > ', function() {
var realNotificationGet, mockNotificationGet, mockNotis;
setup(function() {
mockNotis = [{
tag: 'pairing-request',
close: this.sinon.spy()
}, {
tag: 'email-msg',
close: this.sinon.spy()
}];
mockNotificationGet = function() {
return {
then: function(callback) {
callback(mockNotis);
}
};
};
realNotificationGet = window.Notification.get;
window.Notification.get = mockNotificationGet;
});
teardown(function() {
window.Notification.get = realNotificationGet;
mockNotificationGet = null;
mockNotis = null;
});
test('notification with tag "pairing-request" should be closed',
function() {
PairManager.cleanNotifications();
assert.isTrue(mockNotis[0].close.called);
assert.isFalse(mockNotis[1].close.called);
});
});
suite('showPairview > ', function() {
var openStub, pairviewInitSpy, pairingInfo;
suiteSetup(function() {
pairviewInitSpy = sinon.spy(Pairview, 'init');
openStub = sinon.stub(window, 'open', function() {
return {Pairview: Pairview};
});
pairingInfo = {
method: 'confirmation',
evt: {}
};
PairManager.showPairview(pairingInfo);
});
suiteTeardown(function() {
pairviewInitSpy.restore();
openStub.restore();
});
test('should init Pairview after page onloaded ', function() {
PairManager.childWindow.onload();
assert.isTrue(pairviewInitSpy.calledWith(pairingInfo.method,
pairingInfo.evt));
});
});
suite('onBluetoothDisabled > ', function() {
setup(function() {
this.sinon.stub(Pairview, 'closeInput');
PairManager.childWindow = {
Pairview: Pairview,
close: this.sinon.spy()
};
this.sinon.stub(window, 'close');
});
test('Pairview.closeInput() should be called, childWindow should be close' +
'window.close() should be called', function() {
PairManager.onBluetoothDisabled();
assert.isTrue(PairManager.childWindow.Pairview.closeInput.called);
assert.isTrue(PairManager.childWindow.close.called);
assert.isTrue(window.close.called);
});
});
});

View file

@ -0,0 +1,701 @@
/* global MockNavigatormozSetMessageHandler, MockNavigatormozApps */
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
function switchReadOnlyProperty(originObject, propName, targetObj) {
Object.defineProperty(originObject, propName, {
configurable: true,
get: function() { return targetObj; }
});
}
suite('Bluetooth app > PairManager ', function() {
var realSetMessageHandler;
var realMozApps;
var realGetDeviceStorage;
var AdapterManager;
var BtContext;
var CannotTransferDialog;
var DevicePickerPanel;
var TurnBluetoothOnDialog;
var TransferManager;
suiteSetup(function(done) {
realSetMessageHandler = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
MockNavigatormozSetMessageHandler.mSetup();
var modules = [
'modules/bluetooth/bluetooth_adapter_manager',
'modules/bluetooth/bluetooth_context',
'views/cannot_transfer_dialog',
'views/device_picker_panel',
'views/turn_bluetooth_on_dialog',
'modules/transfer_manager'
];
var maps = {
'*': {
'modules/bluetooth/bluetooth_adapter_manager': 'MockAdapterManager',
'modules/bluetooth/bluetooth_context': 'MockBluetoothContext',
'views/cannot_transfer_dialog': 'unit/mock_cannot_transfer_dialog',
'views/device_picker_panel': 'MockDevicePickerPanel',
'views/turn_bluetooth_on_dialog': 'unit/mock_turn_bluetooth_on_dialog'
}
};
this.MockAdapterManager = {
defaultAdapter: {},
observe: function() {}
};
define('MockAdapterManager', function() {
return this.MockAdapterManager;
}.bind(this));
this.MockBluetoothContext = {
callbacks: {
'enabled': []
},
observe: function(eventName, callback) {
this.callbacks[eventName].push(callback);
},
setEnabled: function() {},
sendFile: function() {}
};
define('MockBluetoothContext', function() {
return this.MockBluetoothContext;
}.bind(this));
this.MockDevicePickerPanel = {
visible: false,
addEventListener: function() {}
};
define('MockDevicePickerPanel', function() {
return this.MockDevicePickerPanel;
}.bind(this));
var requireCtx = testRequire([], maps, function() {});
requireCtx(modules, function(adapterManager, btContext,
cannotTransferDialog, devicePickerPanel,
turnBluetoothOnDialog, transferManager) {
AdapterManager = adapterManager;
BtContext = btContext;
CannotTransferDialog = cannotTransferDialog;
DevicePickerPanel = devicePickerPanel;
TurnBluetoothOnDialog = turnBluetoothOnDialog;
TransferManager = transferManager;
done();
}.bind(this));
});
suiteTeardown(function() {
MockNavigatormozApps.mTeardown();
navigator.mozApps = realMozApps;
MockNavigatormozSetMessageHandler.mTeardown();
navigator.mozSetMessageHandler = realSetMessageHandler;
navigator.getDeviceStorage = realGetDeviceStorage;
});
suite('init > ', function() {
setup(function() {
this.sinon.stub(TransferManager, '_watchActivityRequest');
});
test('_watchActivityRequest() should be called ', function() {
TransferManager.init();
assert.isTrue(TransferManager._watchActivityRequest.called);
});
});
suite('_watchActivityRequest > ', function() {
setup(function() {
this.sinon.stub(navigator, 'mozSetMessageHandler');
this.sinon.stub(TransferManager, '_activityHandler');
});
test('navigator.mozSetMessageHandler() should be set with activity handler',
function() {
TransferManager._watchActivityRequest();
assert.equal(navigator.mozSetMessageHandler.args[0][0], 'activity');
navigator.mozSetMessageHandler.args[0][1]();
assert.isTrue(TransferManager._activityHandler.called);
});
});
suite('_activityHandler > ', function() {
var mockActivityRequest;
suite('accepted format to service sending files > ', function() {
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {
blobs: [{}, {}]
}
}
};
TransferManager._activity = null;
DevicePickerPanel.visible = false;
this.sinon.stub(TransferManager, '_observeDefaultAdapter');
this.sinon.stub(TransferManager, '_observeBluetoothEnabled');
this.sinon.stub(TransferManager, '_watchEventFromDevicePicker');
this.sinon.stub(TransferManager, '_cannotTransfer');
});
test('Should service the activity request, ' +
'adapter, Bluetooth state, device picker should be observed event ',
function() {
TransferManager._activityHandler(mockActivityRequest);
assert.deepEqual(TransferManager._activity, mockActivityRequest);
assert.isTrue(TransferManager._observeDefaultAdapter.called);
assert.isTrue(TransferManager._observeBluetoothEnabled.called);
assert.isTrue(TransferManager._watchEventFromDevicePicker.called);
assert.isTrue(DevicePickerPanel.visible);
assert.isFalse(TransferManager._cannotTransfer.called);
});
});
suite('unaccepted format to cancel sending files > ', function() {
setup(function() {
TransferManager._activity = null;
DevicePickerPanel.visible = false;
this.sinon.stub(TransferManager, '_observeDefaultAdapter');
this.sinon.stub(TransferManager, '_observeBluetoothEnabled');
this.sinon.stub(TransferManager, '_watchEventFromDevicePicker');
this.sinon.stub(TransferManager, '_cannotTransfer');
});
suite('no blobs in activity.source.data > ', function() {
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {}
}
};
});
test('Should not service the activity request, ' +
'_cannotTransfer() should be called ',
function() {
TransferManager._activityHandler(mockActivityRequest);
assert.isNull(TransferManager._activity);
assert.isFalse(TransferManager._observeDefaultAdapter.called);
assert.isFalse(TransferManager._observeBluetoothEnabled.called);
assert.isFalse(TransferManager._watchEventFromDevicePicker.called);
assert.isFalse(DevicePickerPanel.visible);
assert.isTrue(TransferManager._cannotTransfer.called);
});
});
suite('empty array in activity.source.data.blobs > ', function() {
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {
blobs: []
}
}
};
});
test('Should not service the activity request, ' +
'_cannotTransfer() should be called ',
function() {
TransferManager._activityHandler(mockActivityRequest);
assert.isNull(TransferManager._activity);
assert.isFalse(TransferManager._observeDefaultAdapter.called);
assert.isFalse(TransferManager._observeBluetoothEnabled.called);
assert.isFalse(TransferManager._watchEventFromDevicePicker.called);
assert.isFalse(DevicePickerPanel.visible);
assert.isTrue(TransferManager._cannotTransfer.called);
});
});
});
});
suite('_cannotTransfer > ', function() {
suite('CannotTransferDialog is not visible > ', function() {
var mockShowConfirmPromise;
setup(function() {
switchReadOnlyProperty(CannotTransferDialog, 'isVisible', false);
mockShowConfirmPromise = Promise.resolve();
this.sinon.stub(CannotTransferDialog, 'showConfirm').returns(
mockShowConfirmPromise);
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
test('CannotTransferDialog should be shown, ' +
'_endTransferWithReason() should be called with "cancelled"' +
'after a user confirm the dialog ', function(done) {
TransferManager._cannotTransfer();
assert.isTrue(CannotTransferDialog.showConfirm.called);
CannotTransferDialog.showConfirm().then(() => {
assert.isTrue(
TransferManager._endTransferWithReason.calledWith('cancelled'));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('CannotTransferDialog is visible > ', function() {
setup(function() {
switchReadOnlyProperty(CannotTransferDialog, 'isVisible', true);
this.sinon.stub(CannotTransferDialog, 'showConfirm');
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
test('_endTransferWithReason() should not be called ', function() {
TransferManager._cannotTransfer();
assert.isFalse(CannotTransferDialog.showConfirm.called);
});
});
});
suite('_endTransferWithReason > ', function() {
var mockActivityRequest, mockReason;
suite('reason is "transferred" > ', function() {
setup(function() {
mockActivityRequest = {
postResult: function() {}
};
TransferManager._activity = mockActivityRequest;
this.sinon.stub(mockActivityRequest, 'postResult');
mockReason = 'transferred';
});
test('postResult() should be call with the reason ', function() {
TransferManager._endTransferWithReason(mockReason);
assert.isTrue(mockActivityRequest.postResult.calledWith(mockReason));
assert.isNull(TransferManager._activity);
});
});
suite('reason is "cancelled" > ', function() {
setup(function() {
mockActivityRequest = {
postError: function() {}
};
TransferManager._activity = mockActivityRequest;
this.sinon.stub(mockActivityRequest, 'postError');
mockReason = 'cancelled';
});
test('postError() should be call with the reason ', function() {
TransferManager._endTransferWithReason(mockReason);
assert.isTrue(mockActivityRequest.postError.calledWith(mockReason));
assert.isNull(TransferManager._activity);
});
});
});
suite('_observeDefaultAdapter > ', function() {
var mockAdapter;
setup(function() {
mockAdapter = {};
AdapterManager.defaultAdapter = mockAdapter;
this.sinon.stub(AdapterManager, 'observe');
this.sinon.stub(TransferManager, '_onDefaultAdapterChanged');
});
test('AdapterManager should be observed with "defaultAdapter", ' +
'_onDefaultAdapterChanged() should be called ', function() {
TransferManager._observeDefaultAdapter();
assert.equal(AdapterManager.observe.args[0][0], 'defaultAdapter');
assert.isDefined(AdapterManager.observe.args[0][1]);
assert.isTrue(TransferManager._onDefaultAdapterChanged.calledWith(
mockAdapter));
});
});
suite('_observeBluetoothEnabled > ', function() {
var mockEnabledState;
setup(function() {
mockEnabledState = true;
BtContext.enabled = mockEnabledState;
this.sinon.stub(BtContext, 'observe');
this.sinon.stub(TransferManager, '_onBluetoothEnabledChanged');
});
test('BtContext should be observed with "enabled"', function() {
TransferManager._observeBluetoothEnabled();
assert.equal(BtContext.observe.args[0][0], 'enabled');
assert.isDefined(BtContext.observe.args[0][1]);
});
});
suite('_onDefaultAdapterChanged > ', function() {
suite('adapter is changed from existing instance to null > ', function() {
var mockNewAdapter, mockOldAdapter;
setup(function() {
mockNewAdapter = null;
mockOldAdapter = {};
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
test('_endTransferWithReason() should be called with reason "cancelled"',
function() {
TransferManager._onDefaultAdapterChanged(
mockNewAdapter, mockOldAdapter);
assert.isTrue(TransferManager._endTransferWithReason.calledWith(
'cancelled'));
});
});
suite('adapter is changed from null to existing instance > ', function() {
var mockNewAdapter, mockOldAdapter;
setup(function() {
mockNewAdapter = {};
mockOldAdapter = null;
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
test('_endTransferWithReason() should not be called', function() {
TransferManager._onDefaultAdapterChanged(
mockNewAdapter, mockOldAdapter);
assert.isFalse(TransferManager._endTransferWithReason.called);
});
});
});
suite('_onBluetoothEnabledChanged > ', function() {
var mockEnabledState;
setup(function() {
this.sinon.stub(TurnBluetoothOnDialog, 'close');
});
test('not function when passed value is undefined ', function() {
TransferManager._onBluetoothEnabledChanged(undefined);
assert.isFalse(TurnBluetoothOnDialog.close.called);
});
suite('Bluetooth "enabled" state changed to be true > ', function() {
setup(function() {
mockEnabledState = true;
this.sinon.stub(TurnBluetoothOnDialog, 'showConfirm');
});
test('TurnBluetoothOnDialog should be closed ', function() {
TransferManager._onBluetoothEnabledChanged(mockEnabledState);
assert.isTrue(TurnBluetoothOnDialog.close.called);
assert.isFalse(TurnBluetoothOnDialog.showConfirm.called);
});
});
suite('Bluetooth "enabled" state changed to be false > ', function() {
var mockUserAction, mockShowConfirmPromise;
setup(function() {
this.sinon.stub(BtContext, 'setEnabled');
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
suite('A user confirmed to turn Bluetooth on > ', function() {
setup(function() {
mockEnabledState = false;
mockUserAction = 'confirm';
mockShowConfirmPromise = Promise.resolve(mockUserAction);
this.sinon.stub(TurnBluetoothOnDialog, 'showConfirm').returns(
mockShowConfirmPromise);
});
test('TurnBluetoothOnDialog should be shown with confirm dialog, ' +
'BtContext should be enabled with true ', function(done) {
TransferManager._onBluetoothEnabledChanged(mockEnabledState);
assert.isFalse(TurnBluetoothOnDialog.close.called);
assert.isTrue(TurnBluetoothOnDialog.showConfirm.called);
mockShowConfirmPromise.then((userAction) => {
assert.equal(userAction, mockUserAction);
assert.isTrue(BtContext.setEnabled.calledWith(true));
assert.isFalse(TransferManager._endTransferWithReason.called);
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('A user cancelled to turn Bluetooth on > ', function() {
setup(function() {
mockEnabledState = false;
mockUserAction = 'cancel';
mockShowConfirmPromise = Promise.resolve(mockUserAction);
this.sinon.stub(TurnBluetoothOnDialog, 'showConfirm').returns(
mockShowConfirmPromise);
});
test('TurnBluetoothOnDialog should be shown with confirm dialog, ' +
'_endTransferWithReason should be called with reason "cancelled" ',
function(done) {
TransferManager._onBluetoothEnabledChanged(mockEnabledState);
assert.isFalse(TurnBluetoothOnDialog.close.called);
assert.isTrue(TurnBluetoothOnDialog.showConfirm.called);
mockShowConfirmPromise.then((userAction) => {
assert.equal(userAction, mockUserAction);
assert.isFalse(BtContext.setEnabled.called);
assert.isTrue(
TransferManager._endTransferWithReason.calledWith('cancelled'));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
});
});
suite('_watchEventFromDevicePicker > ', function() {
setup(function() {
this.sinon.stub(DevicePickerPanel, 'addEventListener');
});
test('Should add "devicePicked", "cancelSelection" events, ' +
'and regited _onDevicePicked(), _onCancelSelection() handlers ' +
'from DevicePickerPanel ', function() {
TransferManager._watchEventFromDevicePicker();
assert.equal(DevicePickerPanel.addEventListener.args[0][0],
'devicePicked');
assert.isDefined(DevicePickerPanel.addEventListener.args[0][1]);
assert.equal(DevicePickerPanel.addEventListener.args[1][0],
'cancelSelection');
assert.isDefined(DevicePickerPanel.addEventListener.args[1][1]);
});
});
suite('_onDevicePicked > ', function() {
var mockActivityRequest, mockEvent, mockSchedule;
setup(function() {
mockSchedule = {};
this.sinon.stub(TransferManager,
'_produceSendingFilesSchedule').returns(mockSchedule);
this.sinon.stub(TransferManager, '_postMessageToSystemApp');
this.sinon.stub(BtContext, 'sendFile');
this.sinon.stub(TransferManager, '_endTransferWithReason');
mockEvent = {
detail: {
address: 'AA:BB:CC:00:11:22'
}
};
});
suite('blob has name > ', function() {
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {
blobs: [{
name: 'file_01'
}, {
name: 'file_02'
}]
}
}
};
TransferManager._activity = mockActivityRequest;
});
test('_produceSendingFilesSchedule() should be called with length, ' +
'_postMessageToSystemApp() should be called with schedule, ' +
'BtContext.sendFile() should be called with: ' +
'address of target device and blob in soure.data, ' +
'_endTransferWithReason() should be called with transferred ',
function(done) {
TransferManager._onDevicePicked(mockEvent).then(() => {
assert.isTrue(
TransferManager._produceSendingFilesSchedule.calledWith(
mockActivityRequest.source.data.blobs.length));
assert.isTrue(
TransferManager._postMessageToSystemApp.calledWith(mockSchedule));
assert.equal(BtContext.sendFile.args[0][0], mockEvent.detail.address);
assert.deepEqual(BtContext.sendFile.args[0][1],
mockActivityRequest.source.data.blobs[0]);
assert.equal(BtContext.sendFile.args[1][0], mockEvent.detail.address);
assert.deepEqual(BtContext.sendFile.args[1][1],
mockActivityRequest.source.data.blobs[1]);
assert.isTrue(TransferManager._endTransferWithReason.calledWith(
'transferred'));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('blob has no name, has filepaths > ', function() {
var mockGetPromise, mockGotFile;
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {
blobs: [{}, {}],
filepaths: [
'/sdcard/DCIM/100MZLLA/IMG_0001.jpg',
'/sdcard/DCIM/100MZLLA/IMG_0002.jpg'
]
}
}
};
TransferManager._activity = mockActivityRequest;
mockGotFile = {};
mockGetPromise = Promise.resolve(mockGotFile);
var MockGetDeviceStorage = function() {
return {
get: function() {return mockGetPromise;}
};
};
realGetDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockGetDeviceStorage;
});
teardown(function() {
navigator.getDeviceStorage = realGetDeviceStorage;
});
test('_produceSendingFilesSchedule() should be called with length, ' +
'_postMessageToSystemApp() should be called with schedule, ' +
'BtContext.sendFile() should be called with: ' +
'address of target device and file from device storage, ' +
'_endTransferWithReason() should be called with transferred ',
function(done) {
TransferManager._onDevicePicked(mockEvent).then(() => {
assert.isTrue(
TransferManager._produceSendingFilesSchedule.calledWith(
mockActivityRequest.source.data.blobs.length));
assert.isTrue(
TransferManager._postMessageToSystemApp.calledWith(mockSchedule));
assert.equal(BtContext.sendFile.args[0][0], mockEvent.detail.address);
assert.equal(BtContext.sendFile.args[0][1], mockGotFile);
assert.equal(BtContext.sendFile.args[1][0], mockEvent.detail.address);
assert.equal(BtContext.sendFile.args[1][1], mockGotFile);
assert.isTrue(TransferManager._endTransferWithReason.calledWith(
'transferred'));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('blob has no name, has no filepaths > ', function() {
setup(function() {
mockActivityRequest = {
source: {
name: 'share',
data: {
blobs: [{}, {}]
}
}
};
TransferManager._activity = mockActivityRequest;
});
test('_produceSendingFilesSchedule() should be called with length, ' +
'_postMessageToSystemApp() should be called with schedule, ' +
'BtContext.sendFile() should be called with: ' +
'address of target device and blob in soure.data, ' +
'_endTransferWithReason() should be called with transferred ',
function(done) {
TransferManager._onDevicePicked(mockEvent).then(() => {
assert.isTrue(
TransferManager._produceSendingFilesSchedule.calledWith(
mockActivityRequest.source.data.blobs.length));
assert.isTrue(
TransferManager._postMessageToSystemApp.calledWith(mockSchedule));
assert.equal(BtContext.sendFile.args[0][0], mockEvent.detail.address);
assert.deepEqual(BtContext.sendFile.args[0][1],
mockActivityRequest.source.data.blobs[0]);
assert.equal(BtContext.sendFile.args[1][0], mockEvent.detail.address);
assert.deepEqual(BtContext.sendFile.args[1][1],
mockActivityRequest.source.data.blobs[1]);
assert.isTrue(TransferManager._endTransferWithReason.calledWith(
'transferred'));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
});
suite('_onCancelSelection > ', function() {
setup(function() {
this.sinon.stub(TransferManager, '_endTransferWithReason');
});
test('_endTransferWithReason() should be called with reason "cancelled"',
function() {
TransferManager._onCancelSelection();
assert.isTrue(
TransferManager._endTransferWithReason.calledWith('cancelled'));
});
});
suite('_produceSendingFilesSchedule > ', function() {
var mockSendingFilesSchedule, mockNumberOfTasks;
setup(function() {
mockNumberOfTasks = 3;
mockSendingFilesSchedule = {
numberOfFiles: mockNumberOfTasks,
numSuccessful: 0,
numUnsuccessful: 0
};
});
test('_postMessageToSystemApp() should be called with object of ' +
'sending files schedule ', function() {
assert.deepEqual(
TransferManager._produceSendingFilesSchedule(mockNumberOfTasks),
mockSendingFilesSchedule);
});
});
suite('_postMessageToSystemApp > ', function() {
var mockMessage, mockResultOfMozAppGetSelf, mockConnectPromise, mockPorts;
setup(function() {
mockMessage = {};
mockPorts = [
{
postMessage: function() {}
}
];
this.sinon.stub(mockPorts[0], 'postMessage');
mockConnectPromise = Promise.resolve(mockPorts);
mockResultOfMozAppGetSelf = {
connect: function() {return mockConnectPromise;}
};
this.sinon.spy(mockResultOfMozAppGetSelf, 'connect');
});
test('Should post inputed message via port "bluetoothTransfercomms" ' +
'if get app.connect, ports via mozApps successfully ', function(done) {
TransferManager._postMessageToSystemApp(mockMessage);
MockNavigatormozApps.mTriggerLastRequestSuccess(
mockResultOfMozAppGetSelf);
assert.isTrue(
mockResultOfMozAppGetSelf.connect.calledWith('bluetoothTransfercomms'));
mockConnectPromise.then(() => {
assert.isTrue(
mockPorts[0].postMessage.calledWith(mockMessage));
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
});

View file

@ -0,0 +1,43 @@
/* global requirejs */
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
requireApp('bluetooth/js/vendor/alameda.js', (function() {
var contextId = 0;
var baseConfig = {
baseUrl: '/js',
urlArgs: 'bust=' + Date.now(),
paths: {
'modules': 'modules',
'views': 'views',
'shared': '../shared/js',
'unit': '../test/unit',
'shared_mocks': '../shared/test/unit/mocks'
},
shim: {},
modules: [
{
name: 'main'
}
]
};
this.testRequire = function(modules, map, callback) {
var ctx;
if (arguments.length === 2) {
callback = map;
map = null;
}
ctx = requirejs.config({
context: contextId++
});
ctx.config(baseConfig);
ctx.config({
map: map || {}
});
ctx(modules, callback);
return ctx;
};
}).bind(window));

View file

@ -0,0 +1,92 @@
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
suite('Bluetooth app > CannotTransferDialog ', function() {
var CannotTransferDialog;
suiteSetup(function(done) {
loadBodyHTML('./_transfer.html');
var module = [
'views/cannot_transfer_dialog'
];
var map = {};
var requireCtx = testRequire([], map, function() {});
requireCtx(module, function(cannotTransferDialog) {
CannotTransferDialog = cannotTransferDialog;
done();
}.bind(this));
});
suiteTeardown(function() {
document.body.innerHTML = '';
});
suite('init > ', function() {
test('dialog, confirm button should be defined after init() ', function() {
assert.equal(CannotTransferDialog.dialog,
document.getElementById('alert-view'));
assert.equal(CannotTransferDialog.confirmBtn,
document.getElementById('alert-button-ok'));
});
});
suite('showConfirm > ', function() {
setup(function() {
CannotTransferDialog.dialog.hidden = true;
this.sinon.stub(CannotTransferDialog, 'close');
});
teardown(function() {
CannotTransferDialog.dialog.hidden = true;
});
test('dialog should be show after showConfirm() called ', function() {
CannotTransferDialog.showConfirm();
assert.isFalse(CannotTransferDialog.dialog.hidden);
});
test('close() be called and resovle promise after a user clicked ' +
'confirm button in cannot transfer dialog ', function(done) {
CannotTransferDialog.showConfirm().then(() => {
assert.ok(true);
}, () => {
// reject case
}).then(done, done);
CannotTransferDialog.confirmBtn.onclick();
assert.isTrue(CannotTransferDialog.close.called);
});
});
suite('isVisible > ', function() {
setup(function() {
CannotTransferDialog.dialog.hidden = false;
});
teardown(function() {
CannotTransferDialog.dialog.hidden = true;
});
test('getting the value of visible should be true ' +
'after isVisible() is called ', function() {
assert.isTrue(CannotTransferDialog.isVisible);
});
});
suite('close > ', function() {
setup(function() {
CannotTransferDialog.dialog.hidden = false;
});
teardown(function() {
CannotTransferDialog.dialog.hidden = true;
});
test('dialog should be hidden after close() is called ', function() {
CannotTransferDialog.close();
assert.isTrue(CannotTransferDialog.dialog.hidden);
});
});
});

View file

@ -0,0 +1,310 @@
/* global MockL10n */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
suite('DevicePickerPanel', function() {
var realL10n;
var devicePickerPanel;
var btContext;
var btTemplateFactory;
var listView;
suiteSetup(function() {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockL10n;
loadBodyHTML('./_transfer.html');
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
});
setup(function(done) {
var modules = [
'modules/bluetooth/bluetooth_context',
'views/bt_template_factory',
'modules/mvvm/list_view',
'views/device_picker_panel'
];
var map = {
'*': {
'modules/bluetooth/bluetooth_context': 'MockBluetoothContext',
'modules/mvvm/list_view': 'MockListView'
}
};
this.MockBluetoothContext = {
observe: function() {},
getPairedDevices: function() {},
getRemoteDevices: function() {},
hasPairedDevice: false,
state: 'disabled',
discovering: false,
startDiscovery: function() {return Promise.resolve();},
pair: function() {}
};
define('MockBluetoothContext', function() {
return this.MockBluetoothContext;
}.bind(this));
this.MockListView = function MockListView() {
return {};
};
define('MockListView', function() {
return this.MockListView;
}.bind(this));
testRequire(modules, map, function(BluetoothContext, BtTemplateFactory,
ListView, DevicePickerPanel) {
btContext = BluetoothContext;
btTemplateFactory = BtTemplateFactory;
listView = ListView;
devicePickerPanel = DevicePickerPanel;
done();
});
});
suite('_Init > ', function() {
setup(function() {
this.sinon.stub(devicePickerPanel._elements.header.headerActionClose,
'addEventListener');
this.sinon.stub(devicePickerPanel._elements.search.searchButton,
'addEventListener');
devicePickerPanel._pairedDevicesListView = null;
devicePickerPanel._foundDevicesListView = null;
this.sinon.stub(devicePickerPanel, '_initItemsState');
this.sinon.stub(btContext, 'startDiscovery').returns(Promise.resolve());
});
test('The close button of header should be regedit action callback, ' +
'search button should be regedit onclick callback, ' +
'paired/found list view should be defined, ' +
'_initItemsState() should be called ' +
'BtContext.startDiscovery() should be called ', function() {
devicePickerPanel._init();
// close button
assert.equal(
devicePickerPanel._elements.header.headerActionClose.
addEventListener.args[0][0], 'action');
assert.isDefined(
devicePickerPanel._elements.header.headerActionClose.
addEventListener.args[0][1]);
// search button
assert.equal(
devicePickerPanel._elements.search.searchButton.
addEventListener.args[0][0], 'click');
assert.isDefined(
devicePickerPanel._elements.search.searchButton.
addEventListener.args[0][1]);
// paired/found list view
assert.isDefined(devicePickerPanel._pairedDevicesListView);
assert.isDefined(devicePickerPanel._foundDevicesListView);
assert.isTrue(devicePickerPanel._initItemsState.called);
assert.isTrue(btContext.startDiscovery.called);
});
});
suite('_initItemsState > ', function() {
setup(function() {
this.sinon.stub(btContext, 'observe');
this.sinon.stub(devicePickerPanel, '_updatePairedDevicesHeader');
this.sinon.stub(devicePickerPanel, '_updatePairedDevicesList');
this.sinon.stub(devicePickerPanel, '_updateFoundDevicesList');
this.sinon.stub(devicePickerPanel, '_updateSearchItem');
this.sinon.stub(devicePickerPanel, '_updateSearchingItem');
});
test('btContext.hasPairedDevice should be observed with callback, ' +
'btContext.state should be observed with callback, ' +
'btContext.discovering should be observed with callback, ' +
'paired device header, paired/found device list, ' +
'search/searching item should be updated.. ', function() {
devicePickerPanel._initItemsState();
// paired device header
assert.equal(btContext.observe.args[0][0], 'hasPairedDevice');
assert.isDefined(btContext.observe.args[0][1]);
assert.isTrue(devicePickerPanel._updatePairedDevicesHeader.called);
// paired device list
assert.equal(btContext.observe.args[1][0], 'state');
assert.isDefined(btContext.observe.args[1][1]);
assert.isTrue(devicePickerPanel._updatePairedDevicesList.called);
// found device list
assert.equal(btContext.observe.args[2][0], 'state');
assert.isDefined(btContext.observe.args[2][1]);
assert.isTrue(devicePickerPanel._updateFoundDevicesList.called);
// search item
assert.equal(btContext.observe.args[3][0], 'state');
assert.isDefined(btContext.observe.args[3][1]);
assert.isTrue(devicePickerPanel._updateSearchItem.called);
// searching item
assert.equal(btContext.observe.args[4][0], 'discovering');
assert.isDefined(btContext.observe.args[4][1]);
assert.isTrue(devicePickerPanel._updateSearchingItem.called);
});
});
suite('_onHeaderActionCloseClick > ', function() {
var expectedEvent = {
type: 'cancelSelection'
};
setup(function() {
this.sinon.stub(devicePickerPanel, '_emitEvent');
});
test('_emitEvent() should be called with event, type = "cancelSelection" ' +
'while _onHeaderActionCloseClick() be called ', function() {
devicePickerPanel._onHeaderActionCloseClick();
assert.isTrue(devicePickerPanel._emitEvent.calledWith(expectedEvent));
});
});
suite('_onSearchButtonClick > ', function() {
setup(function() {
this.sinon.stub(btContext, 'startDiscovery').returns(Promise.resolve());
});
test('btContext.startDiscovery() should be called while ' +
'search button is clicked ', function() {
devicePickerPanel._onSearchButtonClick();
assert.isTrue(btContext.startDiscovery.called);
});
});
suite('_onPairedDeviceItemClick > ', function() {
var mockDeviceItem, expectedEvent;
setup(function() {
mockDeviceItem = {
address: 'AA:BB:CC:00:11:22'
};
expectedEvent = {
type: 'devicePicked',
detail: {
address: mockDeviceItem.address
}
};
this.sinon.stub(devicePickerPanel, '_emitEvent');
});
test('_emitEvent() should be called with event, type = "devicePicked" ' +
'while _onPairedDeviceItemClick() be called with device item ',
function() {
devicePickerPanel._onPairedDeviceItemClick(mockDeviceItem);
assert.isTrue(devicePickerPanel._emitEvent.calledWith(expectedEvent));
});
});
suite('_onFoundDeviceItemClick > ', function() {
var mockDeviceItem, mockPairPromise;
suite('btContext.pair() with resolve > ', function() {
var expectedEvent;
setup(function() {
mockDeviceItem = {
address: 'AA:BB:CC:00:11:22',
paired: false
};
expectedEvent = {
type: 'devicePicked',
detail: {
address: mockDeviceItem.address
}
};
mockPairPromise = Promise.resolve();
this.sinon.stub(btContext, 'pair').returns(mockPairPromise);
this.sinon.stub(devicePickerPanel, '_emitEvent');
this.sinon.stub(devicePickerPanel, '_alertPairFailedErrorMessage');
});
test('the "paired" property of device item should be set "pairing", ' +
'_emitEvent() should be called with event, type = "devicePicked," ' +
'_alertPairFailedErrorMessage() should not be called ' +
'while _onFoundDeviceItemClick() be called with device item ',
function(done) {
devicePickerPanel._onFoundDeviceItemClick(mockDeviceItem);
assert.equal(mockDeviceItem.paired, 'pairing');
mockPairPromise.then(() => {
assert.isTrue(devicePickerPanel._emitEvent.calledWith(expectedEvent));
assert.isFalse(devicePickerPanel._alertPairFailedErrorMessage.called);
}, () => {
// reject case
}).then(done, done);
});
});
suite('btContext.pair() with reject > ', function() {
setup(function() {
mockDeviceItem = {
address: 'AA:BB:CC:00:11:22',
paired: false
};
mockPairPromise = Promise.reject();
this.sinon.stub(btContext, 'pair').returns(mockPairPromise);
this.sinon.stub(devicePickerPanel, '_emitEvent');
this.sinon.stub(devicePickerPanel, '_alertPairFailedErrorMessage');
});
test('the "paired" property of device item should be set "pairing", ' +
'then the property will be set to false in pair rejected case, ' +
'_emitEvent() should not be called, ' +
'_alertPairFailedErrorMessage() should be called ' +
'while _onFoundDeviceItemClick() be called with device item ',
function(done) {
devicePickerPanel._onFoundDeviceItemClick(mockDeviceItem);
assert.equal(mockDeviceItem.paired, 'pairing');
mockPairPromise.then(() => {
// resolve case
}, () => {
assert.isFalse(mockDeviceItem.paired);
assert.isFalse(devicePickerPanel._emitEvent.called);
assert.isTrue(devicePickerPanel._alertPairFailedErrorMessage.called);
}).then(done, done);
});
});
});
suite('_alertPairFailedErrorMessage > ', function() {
var mockErrorReason, expectedMsg;
suite('no errorReason > ', function() {
setup(function() {
mockErrorReason = null;
expectedMsg = 'error-pair-title';
this.sinon.stub(window, 'alert');
});
test('Show default message if there is no error reason ', function() {
devicePickerPanel._alertPairFailedErrorMessage(mockErrorReason);
assert.isTrue(window.alert.calledWith(expectedMsg));
});
});
suite('has errorReason "Authentication Failed" > ', function() {
setup(function() {
mockErrorReason = 'Authentication Failed';
expectedMsg = 'error-pair-title' + '\n' + 'error-pair-pincode';
this.sinon.stub(window, 'alert');
});
test('Show message with pincode error if error reason is ' +
'"Authentication Failed" ', function() {
devicePickerPanel._alertPairFailedErrorMessage(mockErrorReason);
assert.isTrue(window.alert.calledWith(expectedMsg));
});
});
});
suite('visible > ', function() {
setup(function() {
devicePickerPanel._elements.view.viewDevicePicker.hidden = true;
});
test('device picker view should be set visible', function() {
devicePickerPanel.visible = true;
assert.isFalse(devicePickerPanel._elements.view.viewDevicePicker.hidden);
});
});
});

View file

@ -0,0 +1,97 @@
/* global MockL10n */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
suite('Bluetooth app > PairExpiredDialog ', function() {
var realL10n, PairExpiredDialog;
suiteSetup(function(done) {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockL10n;
loadBodyHTML('./_message.html');
var module = [
'views/pair_expired_dialog'
];
var map = {};
var requireCtx = testRequire([], map, function() {});
requireCtx(module, function(pairExpiredDialog) {
PairExpiredDialog = pairExpiredDialog;
done();
}.bind(this));
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
document.body.innerHTML = '';
});
suite('init > ', function() {
test('dialog, confirm button should be defined after init() ', function() {
assert.equal(PairExpiredDialog.dialog,
document.getElementById('pairing-request-timeout'));
assert.equal(PairExpiredDialog.confirmBtn,
document.getElementById('incoming-pairing-timeout-confirm'));
});
});
suite('showConfirm > ', function() {
var callback;
setup(function() {
callback = function() {/* do something.. */};
PairExpiredDialog.dialog.hidden = true;
this.sinon.stub(PairExpiredDialog, 'close');
});
teardown(function() {
callback = null;
PairExpiredDialog.dialog.hidden = true;
});
test('dialog should be show after showConfirm() called ', function() {
PairExpiredDialog.showConfirm(callback);
assert.isFalse(PairExpiredDialog.dialog.hidden);
});
test('close() be called with callback function after a user clicked ' +
'confirm button in pair expired dialog ', function() {
PairExpiredDialog.showConfirm(callback);
PairExpiredDialog.confirmBtn.onclick();
assert.isTrue(PairExpiredDialog.close.called);
});
});
suite('isVisible > ', function() {
setup(function() {
PairExpiredDialog.dialog.hidden = false;
});
teardown(function() {
PairExpiredDialog.dialog.hidden = true;
});
test('getting the value of visible should be true ' +
'after isVisible() is called ', function() {
assert.isTrue(PairExpiredDialog.isVisible);
});
});
suite('close > ', function() {
setup(function() {
PairExpiredDialog.dialog.hidden = false;
});
teardown(function() {
PairExpiredDialog.dialog.hidden = true;
});
test('dialog should be hidden after close() is called, and the arg of ' +
'callback should be excuted if it is given ', function() {
PairExpiredDialog.close();
assert.isTrue(PairExpiredDialog.dialog.hidden);
});
});
});

View file

@ -0,0 +1,461 @@
/* global MocksHelper, MockL10n, Pairview */
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
requireApp('bluetooth/test/unit/mock_pair_manager.js');
function switchReadOnlyProperty(originObject, propName, targetObj) {
Object.defineProperty(originObject, propName, {
configurable: true,
get: function() { return targetObj; }
});
}
var mocksForPairviewHelper = new MocksHelper([
'PairManager'
]).init();
suite('Bluetooth app > Pairview ', function() {
var realL10n;
mocksForPairviewHelper.attachTestHelpers();
suiteSetup(function(done) {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockL10n;
loadBodyHTML('./_onpair.html');
requireApp('bluetooth/js/views/pair_view.js', done);
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
document.body.innerHTML = '';
});
suite('isFullAttentionMode > ', function() {
var realWindowInnerHeight;
suiteSetup(function() {
realWindowInnerHeight = window.innerHeight;
});
suiteTeardown(function() {
switchReadOnlyProperty(window, 'innerHeight', realWindowInnerHeight);
});
suite('window.innerHeight is 200 > ', function() {
setup(function() {
switchReadOnlyProperty(window, 'innerHeight', 200);
});
test('getting the mode of full attention should be false ' +
'after isFullAttentionMode() is called ', function() {
assert.isFalse(Pairview.isFullAttentionMode);
});
});
suite('window.innerHeight is 201 > ', function() {
setup(function() {
switchReadOnlyProperty(window, 'innerHeight', 201);
});
test('getting the mode of full attention should be true ' +
'after isFullAttentionMode() is called ', function() {
assert.isTrue(Pairview.isFullAttentionMode);
});
});
});
suite('init > ', function() {
var mockPairMethod, mockOptions;
setup(function(done) {
mockPairMethod = 'displaypasskey';
mockOptions = {
deviceName: 'device-01',
handle: {
passkey: 123456
}
};
this.sinon.stub(Pairview, 'show');
this.sinon.stub(Pairview, '_pairMethod');
this.sinon.stub(Pairview, '_remoteDeviceName');
this.sinon.stub(Pairview, '_passkey');
Pairview.init(mockPairMethod, mockOptions);
setTimeout(done);
});
test('pairing info should be inited after init() ', function() {
assert.equal(Pairview._pairMethod, mockPairMethod);
assert.equal(Pairview._remoteDeviceName, mockOptions.deviceName);
assert.equal(Pairview._passkey, 123456);
});
test('pair view should be showing after mozL10n.once callback', function() {
assert.isTrue(Pairview.show.called);
});
});
suite('show > pair method in three methods > ', function() {
var realGetTruncated;
var mockDeviceName = 'device-01';
var mockPasskey = 123456;
setup(function() {
realGetTruncated = window.getTruncated;
window.getTruncated = this.sinon.stub();
window.getTruncated.returns({
addEventListener: this.sinon.stub()
});
});
suite('early return > ', function() {
var realWindowInnerHeight;
setup(function() {
realWindowInnerHeight = window.innerHeight;
switchReadOnlyProperty(window, 'innerHeight', 200);
this.sinon.stub(Pairview, 'close');
Pairview.show();
});
teardown(function() {
switchReadOnlyProperty(window, 'innerHeight', realWindowInnerHeight);
});
test('isFullAttentionMode() should be false', function() {
assert.isFalse(Pairview.isFullAttentionMode);
});
test('close() should be called while attention sceen is not in full ' +
'screen mode ', function() {
assert.isTrue(Pairview.close.called);
});
test('getTruncated() should not be called since early return',
function() {
assert.isFalse(window.getTruncated.called);
});
});
suite('confirmation, displaypasskey > ', function() {
['confirmation', 'displaypasskey'].forEach((mockPairMethod) => {
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', mockPairMethod);
this.sinon.stub(Pairview, '_remoteDeviceName', mockDeviceName);
this.sinon.stub(Pairview, '_passkey', mockPasskey);
Pairview.show();
});
teardown(function() {
Pairview.nameLabel.textContent = undefined;
Pairview.pairview.hidden = true;
Pairview.pairDescription.textContent = undefined;
Pairview.passkey.textContent = undefined;
Pairview.comfirmationItem.hidden = true;
Pairview.pinInputItem.hidden = true;
});
test('nameLabel should be defined after show() ', function() {
assert.isDefined(Pairview.nameLabel.textContent);
});
test('view should not be hidden after show() ', function() {
assert.isFalse(Pairview.pairview.hidden);
});
test('description should be defined after show() ', function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('passkey textContent should be displayed and ' +
'same with pairing info from system message ', function() {
assert.equal(Pairview.passkey.textContent, mockPasskey);
});
test('comfirmation method should not be hidden after show()',
function() {
assert.isFalse(Pairview.comfirmationItem.hidden);
});
test('pin input field should be hidden after show() ', function() {
assert.isTrue(Pairview.pinInputItem.hidden);
});
});
});
suite('enterpincode > ', function() {
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', 'enterpincode');
this.sinon.stub(Pairview, '_remoteDeviceName', mockDeviceName);
Pairview.show();
});
teardown(function() {
Pairview.nameLabel.textContent = undefined;
Pairview.pairview.hidden = true;
Pairview.pairDescription.textContent = undefined;
Pairview.comfirmationItem.hidden = true;
Pairview.pinInputItem.hidden = true;
});
test('nameLabel should be defined after show() ', function() {
assert.isDefined(Pairview.nameLabel.textContent);
});
test('view should not be hidden after show() ', function() {
assert.isFalse(Pairview.pairview.hidden);
});
test('description should be defined after show() ', function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('pin input field should not be hidden after show() ', function() {
assert.isFalse(Pairview.pinInputItem.hidden);
});
test('comfirmation method should be hidden after show() ', function() {
assert.isTrue(Pairview.comfirmationItem.hidden);
});
});
suite('consent > ', function() {
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', 'consent');
this.sinon.stub(Pairview, '_remoteDeviceName', mockDeviceName);
Pairview.show();
});
teardown(function() {
Pairview.nameLabel.textContent = undefined;
Pairview.pairview.hidden = true;
Pairview.pairDescription.textContent = undefined;
Pairview.comfirmationItem.hidden = true;
Pairview.pinInputItem.hidden = true;
});
test('nameLabel should be defined after show() ', function() {
assert.isDefined(Pairview.nameLabel.textContent);
});
test('view should not be hidden after show() ', function() {
assert.isFalse(Pairview.pairview.hidden);
});
test('description should be defined after show() ', function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('comfirmation method should not be hidden after show()', function() {
assert.isFalse(Pairview.comfirmationItem.hidden);
});
test('pin input field should be hidden after show() ', function() {
assert.isTrue(Pairview.pinInputItem.hidden);
});
});
});
suite('handleEvent > ', function() {
var fakePreventDefault = function() {/* do nothing here.. */};
var stubWindowClose;
suiteSetup(function() {
stubWindowClose = sinon.stub(window, 'close');
Pairview.pairButton.disabled = false;
Pairview.closeButton.disabled = false;
Pairview.pairDescription.textContent = undefined;
});
suiteTeardown(function() {
stubWindowClose.restore();
Pairview.pairButton.disabled = false;
Pairview.closeButton.disabled = false;
Pairview.pairDescription.textContent = undefined;
});
suite('click pair button > pair method: displaypasskey ', function() {
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', 'displaypasskey');
Pairview.handleEvent({target: {id: 'button-pair'},
type: 'click',
preventDefault: fakePreventDefault});
});
test('description should be defined after clicked pair button',
function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('pair button should be disabled after clicked event ', function() {
assert.isTrue(Pairview.pairButton.disabled);
});
test('close button should be disabled after clicked event ', function() {
assert.isTrue(Pairview.closeButton.disabled);
});
test('window should be closed after clicked event', function() {
assert.isTrue(stubWindowClose.called);
});
});
suite('click pair button > pair method: enterpincode ', function() {
var mockOptions, pincode = 'SixteenTxtLength';
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', 'enterpincode');
Pairview.pinInput.value = pincode;
mockOptions = {
handle: {
setPinCode: function() {return Promise.resolve();}
}
};
this.sinon.spy(mockOptions.handle, 'setPinCode');
this.sinon.stub(Pairview, '_options', mockOptions);
Pairview.handleEvent({target: {id: 'button-pair'},
type: 'click',
preventDefault: fakePreventDefault});
});
teardown(function() {
Pairview.pinInput.value = '';
});
test('description should be defined after clicked pair button',
function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('pair button should be disabled after clicked event ', function() {
assert.isTrue(Pairview.pairButton.disabled);
});
test('close button should be disabled after clicked event ', function() {
assert.isTrue(Pairview.closeButton.disabled);
});
test('set pin code should be set after clicked event', function() {
assert.isTrue(Pairview._options.handle.setPinCode.calledWith(pincode));
});
test('window should be closed after setPinCode resolve ', function(done) {
mockOptions.handle.setPinCode(pincode).then(() => {
assert.isTrue(stubWindowClose.called);
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('click pair button > pair method: confirmation, consent ',
function() {
var mockOptions;
['confirmation', 'consent'].forEach((mockPairMethod) => {
setup(function() {
this.sinon.stub(Pairview, '_pairMethod', mockPairMethod);
mockOptions = {
handle: {
accept: function() {return Promise.resolve();}
}
};
this.sinon.spy(mockOptions.handle, 'accept');
this.sinon.stub(Pairview, '_options', mockOptions);
Pairview.handleEvent({target: {id: 'button-pair'},
type: 'click',
preventDefault: fakePreventDefault});
});
test('description should be defined after clicked pair button',
function() {
assert.isDefined(Pairview.pairDescription.textContent);
});
test('pair button should be disabled after clicked event ', function() {
assert.isTrue(Pairview.pairButton.disabled);
});
test('close button should be disabled after clicked event', function() {
assert.isTrue(Pairview.closeButton.disabled);
});
test('handle.accept should be set after clicked event', function() {
assert.isTrue(Pairview._options.handle.accept.called);
});
test('window should be closed after accept resolve', function(done) {
mockOptions.handle.accept().then(() => {
assert.isTrue(stubWindowClose.called);
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
});
suite('click close button > ', function() {
var pairviewCloseSpy, mockOptions;
setup(function() {
pairviewCloseSpy = this.sinon.spy(Pairview, 'close');
mockOptions = {
handle: {
reject: function() {return Promise.resolve();}
}
};
this.sinon.spy(mockOptions.handle, 'reject');
this.sinon.stub(Pairview, '_options', mockOptions);
Pairview.handleEvent({target: {id: 'button-close'},
type: 'click',
preventDefault: fakePreventDefault});
});
test('close() method should be called after clicked close ', function() {
assert.isTrue(pairviewCloseSpy.called);
});
test('handle.reject should be set after clicked close ', function(done) {
mockOptions.handle.reject().then(() => {
assert.isTrue(stubWindowClose.called);
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
suite('received "resize" event > ', function() {
var pairviewCloseSpy, mockOptions;
setup(function() {
pairviewCloseSpy = this.sinon.spy(Pairview, 'close');
mockOptions = {
handle: {
reject: function() {return Promise.resolve();}
}
};
this.sinon.spy(mockOptions.handle, 'reject');
this.sinon.stub(Pairview, '_options', mockOptions);
switchReadOnlyProperty(window, 'innerHeight', 150);
Pairview.handleEvent({target: {},
type: 'resize',
preventDefault: fakePreventDefault});
});
teardown(function() {
switchReadOnlyProperty(window, 'innerHeight', null);
});
test('close() method should be called after clicked close ', function() {
assert.isTrue(pairviewCloseSpy.called);
});
test('handle.reject should be set after clicked close ', function(done) {
mockOptions.handle.reject().then(() => {
assert.isTrue(stubWindowClose.called);
}, () => {
// reject case
assert.isTrue(false);
}).then(done, done);
});
});
});
suite('closeInput > ', function() {
var pairviewPinInputSpy;
setup(function() {
pairviewPinInputSpy = this.sinon.spy(Pairview.pinInput, 'blur');
Pairview.pinInputItem.hidden = false;
Pairview.closeInput();
});
teardown(function() {
pairviewPinInputSpy.restore();
Pairview.pinInputItem.hidden = true;
});
test('pin input should be blurred after closeInput() ', function() {
assert.isTrue(pairviewPinInputSpy.called);
});
});
});

View file

@ -0,0 +1,113 @@
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
suite('Bluetooth app > TurnBluetoothOnDialog ', function() {
var TurnBluetoothOnDialog;
suiteSetup(function(done) {
loadBodyHTML('./_transfer.html');
var module = [
'views/turn_bluetooth_on_dialog'
];
var map = {};
var requireCtx = testRequire([], map, function() {});
requireCtx(module, function(turnBluetoothOnDialog) {
TurnBluetoothOnDialog = turnBluetoothOnDialog;
done();
}.bind(this));
});
suiteTeardown(function() {
document.body.innerHTML = '';
});
suite('init > ', function() {
test('dialog, cancel/confirm button should be defined after init() ',
function() {
assert.equal(TurnBluetoothOnDialog.dialog,
document.getElementById('enable-bluetooth-view'));
assert.equal(TurnBluetoothOnDialog.cancelBtn,
document.getElementById('enable-bluetooth-button-cancel'));
assert.equal(TurnBluetoothOnDialog.confirmBtn,
document.getElementById('enable-bluetooth-button-turn-on'));
});
});
suite('showConfirm > ', function() {
var confirmedAction;
setup(function() {
confirmedAction = {
cancel: 'cancel',
confirm: 'confirm'
};
TurnBluetoothOnDialog.dialog.hidden = true;
this.sinon.stub(TurnBluetoothOnDialog, 'close');
});
teardown(function() {
TurnBluetoothOnDialog.dialog.hidden = true;
});
test('dialog should be show after showConfirm() called ', function() {
TurnBluetoothOnDialog.showConfirm();
assert.isFalse(TurnBluetoothOnDialog.dialog.hidden);
});
test('close() be called and resovled promise with arg "cancel" after a ' +
'user clicked cancel button in turn Bluetooth on dialog ',
function(done) {
TurnBluetoothOnDialog.showConfirm().then((result) => {
assert.equal(result, confirmedAction.cancel);
}, () => {
// reject case
}).then(done, done);
TurnBluetoothOnDialog.cancelBtn.onclick();
assert.isTrue(TurnBluetoothOnDialog.close.called);
});
test('close() be called and resolved promise with arg "confirm" after a ' +
'user clicked confirm button in turn Bluetooth on dialog ',
function(done) {
TurnBluetoothOnDialog.showConfirm().then((result) => {
assert.equal(result, confirmedAction.confirm);
}, () => {
// reject case
}).then(done, done);
TurnBluetoothOnDialog.confirmBtn.onclick();
assert.isTrue(TurnBluetoothOnDialog.close.called);
});
});
suite('isVisible > ', function() {
setup(function() {
TurnBluetoothOnDialog.dialog.hidden = false;
});
teardown(function() {
TurnBluetoothOnDialog.dialog.hidden = true;
});
test('getting the value of visible should be true ' +
'after isVisible() is called ', function() {
assert.isTrue(TurnBluetoothOnDialog.isVisible);
});
});
suite('close > ', function() {
setup(function() {
TurnBluetoothOnDialog.dialog.hidden = false;
});
teardown(function() {
TurnBluetoothOnDialog.dialog.hidden = true;
});
test('dialog should be hidden after close() is called ', function() {
TurnBluetoothOnDialog.close();
assert.isTrue(TurnBluetoothOnDialog.dialog.hidden);
});
});
});