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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
126
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_cod_mapper_test.js
Executable file
126
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_cod_mapper_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1682
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_context_test.js
Executable file
1682
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_context_test.js
Executable file
File diff suppressed because it is too large
Load diff
195
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_device_test.js
Executable file
195
gaia/apps/bluetooth/test/unit/modules/bluetooth/bluetooth_device_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
618
gaia/apps/bluetooth/test/unit/modules/mvvm/list_view_test.js
Executable file
618
gaia/apps/bluetooth/test/unit/modules/mvvm/list_view_test.js
Executable 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
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
275
gaia/apps/bluetooth/test/unit/modules/mvvm/observable_array_test.js
Executable file
275
gaia/apps/bluetooth/test/unit/modules/mvvm/observable_array_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
110
gaia/apps/bluetooth/test/unit/modules/mvvm/observable_test.js
Executable file
110
gaia/apps/bluetooth/test/unit/modules/mvvm/observable_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
623
gaia/apps/bluetooth/test/unit/modules/pair_manager_test.js
Executable file
623
gaia/apps/bluetooth/test/unit/modules/pair_manager_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
701
gaia/apps/bluetooth/test/unit/modules/transfer_manager_test.js
Executable file
701
gaia/apps/bluetooth/test/unit/modules/transfer_manager_test.js
Executable 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue