Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
506 lines
18 KiB
JavaScript
Executable file
506 lines
18 KiB
JavaScript
Executable file
/* global MocksHelper */
|
|
/* global MockSettingsListener */
|
|
/* global MockGeolocation */
|
|
/* global Commands */
|
|
/* global FindMyDevice */
|
|
/* global MockNavigatorSettings */
|
|
/* global PasscodeHelper */
|
|
|
|
'use strict';
|
|
|
|
require('/shared/test/unit/mocks/mocks_helper.js');
|
|
require('/shared/test/unit/mocks/mock_settings_url.js');
|
|
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
|
require('/shared/test/unit/mocks/mock_settings_listener.js');
|
|
require('/shared/test/unit/mocks/mock_settings_helper.js');
|
|
require('/shared/test/unit/mocks/mock_audio.js');
|
|
require('/shared/test/unit/mocks/mock_device_storage.js');
|
|
require('/shared/test/unit/mocks/mock_geolocation.js');
|
|
require('/shared/js/passcode_helper.js');
|
|
|
|
var mocksForFindMyDevice = new MocksHelper([
|
|
'SettingsListener', 'SettingsURL', 'SettingsHelper', 'Audio',
|
|
'DeviceStorage', 'Geolocation'
|
|
]).init();
|
|
|
|
suite('FindMyDevice >', function() {
|
|
var realMozPower;
|
|
var realKillSwitch;
|
|
var realMozApps;
|
|
var fakeClock;
|
|
var realMozSettings;
|
|
|
|
mocksForFindMyDevice.attachTestHelpers();
|
|
|
|
var subject;
|
|
setup(function(done) {
|
|
realMozPower = navigator.mozPower;
|
|
navigator.mozPower = {
|
|
factoryReset: function(reason) {},
|
|
};
|
|
|
|
this.sinon.spy(navigator.mozPower, 'factoryReset');
|
|
|
|
realKillSwitch = navigator.mozKillSwitch;
|
|
navigator.mozKillSwitch = {
|
|
enable: function() {}
|
|
};
|
|
|
|
realMozApps = navigator.mozApps;
|
|
navigator.mozApps = {
|
|
getSelf: function() {
|
|
var app = {
|
|
manifestURL: 'app://findmydevice.gaiamobile.org/manifest.webapp',
|
|
origin: 'app://findmydevice.gaiamobile.org/'
|
|
};
|
|
|
|
var request = {result: app};
|
|
setTimeout(function() {
|
|
request.onsuccess.call(request);
|
|
});
|
|
|
|
return request;
|
|
}
|
|
};
|
|
|
|
// replace shared/js/dump.js
|
|
window.DUMP = function() {};
|
|
|
|
fakeClock = this.sinon.useFakeTimers();
|
|
|
|
realMozSettings = navigator.mozSettings;
|
|
navigator.mozSettings = MockNavigatorSettings;
|
|
|
|
window.FindMyDevice = {
|
|
beginHighPriority: this.sinon.stub(),
|
|
endHighPriority: this.sinon.stub()
|
|
};
|
|
|
|
require('/js/commands.js', function() {
|
|
subject = Commands;
|
|
done();
|
|
});
|
|
});
|
|
|
|
suite('Ring command', function() {
|
|
var duration = 2;
|
|
|
|
setup(function() {
|
|
this.sinon.spy(window, 'setTimeout');
|
|
this.sinon.spy(window, 'clearTimeout');
|
|
});
|
|
|
|
test('Basic invocation', function() {
|
|
var ringtone = 'user selected ringtone';
|
|
|
|
MockSettingsListener.mCallbacks['dialer.ringtone'](ringtone);
|
|
|
|
Commands.invokeCommand('ring', [duration, function(retval) {
|
|
var lock = MockSettingsListener.getSettingsLock().locks.pop();
|
|
|
|
var channel = Commands._ringer.mozAudioChannelType;
|
|
assert.equal(channel, 'ringer', 'use notification channel');
|
|
assert.equal(lock['audio.volume.notification'], 15,
|
|
'volume set to maximum');
|
|
assert.equal(Commands._ringer.paused, false, 'must be playing');
|
|
assert.equal(Commands._ringer.src, ringtone, 'must use ringtone');
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.beginHighPriority, 'command');
|
|
}]);
|
|
|
|
fakeClock.tick(duration * 1000);
|
|
assert.equal(Commands._ringer.paused, true, 'must have stopped');
|
|
sinon.assert.calledOnce(FindMyDevice.endHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.endHighPriority, 'command');
|
|
});
|
|
|
|
test('Stop ringing if duration is zero', function() {
|
|
Commands.invokeCommand('ring', [duration, function(retval) {
|
|
assert.equal(Commands._ringer.paused, false, 'must be playing');
|
|
sinon.assert.called(window.setTimeout);
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.notCalled(FindMyDevice.endHighPriority);
|
|
}]);
|
|
|
|
fakeClock.tick(duration/2 * 1000);
|
|
|
|
var timeoutId = Commands._ringTimeoutId;
|
|
assert.isFalse(Commands._ringer.paused, 'must not be done ringing');
|
|
Commands.invokeCommand('ring', [0, function(retval) {}]);
|
|
|
|
assert.equal(Commands._ringer.paused, true, 'must have stopped');
|
|
assert.isNull(Commands._ringTimeoutId, 'timeout must have been canceled');
|
|
sinon.assert.calledWith(window.clearTimeout, timeoutId);
|
|
sinon.assert.calledTwice(FindMyDevice.beginHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.beginHighPriority, 'command');
|
|
sinon.assert.calledTwice(FindMyDevice.endHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.endHighPriority, 'command');
|
|
});
|
|
|
|
test('Should do nothing if already ringing', function() {
|
|
Commands.invokeCommand('ring', [duration, function(retval) {
|
|
assert.isFalse(Commands._ringer.paused, 'must be playing');
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.notCalled(FindMyDevice.endHighPriority);
|
|
}]);
|
|
|
|
fakeClock.tick(duration/2 * 1000);
|
|
|
|
assert.isFalse(Commands._ringer.paused, 'must not be done ringing');
|
|
Commands.invokeCommand('ring', [duration, function(retval) {}]);
|
|
|
|
fakeClock.tick(duration/2 * 1000);
|
|
|
|
assert.isTrue(Commands._ringer.paused, 'must have stopped');
|
|
assert.isNull(Commands._ringTimeoutId, 'no timeouts should be set');
|
|
sinon.assert.calledTwice(FindMyDevice.beginHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.beginHighPriority, 'command');
|
|
sinon.assert.calledTwice(FindMyDevice.endHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.endHighPriority, 'command');
|
|
});
|
|
|
|
test('Should do nothing if not ringing and duration is zero', function() {
|
|
Commands.invokeCommand('ring', [0, function(retval) {}]);
|
|
assert.isTrue(Commands._ringer.paused, 'must not have started');
|
|
assert.isNull(Commands._ringTimeoutId);
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.beginHighPriority, 'command');
|
|
sinon.assert.calledOnce(FindMyDevice.endHighPriority);
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.endHighPriority, 'command');
|
|
});
|
|
});
|
|
|
|
test('Erase: ensure factoryReset is called with "wipe"', function(done) {
|
|
subject.invokeCommand('erase', [function(retval, error) {
|
|
assert.equal(navigator.mozPower.factoryReset.calledWith('wipe'), true);
|
|
done();
|
|
}]);
|
|
});
|
|
|
|
suite('Track command', function() {
|
|
test('Track receives positions correctly', function() {
|
|
// track for a period of time
|
|
var duration = (subject.TRACK_UPDATE_INTERVAL_MS) / 1000;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {
|
|
assert.equal(retval, true);
|
|
// ensure, while tracking, the locations match what the mock makes
|
|
assert.deepEqual(position, MockGeolocation.fakePosition ,
|
|
'check the position is what the mock provided');
|
|
}]);
|
|
fakeClock.tick(2* subject.TRACK_UPDATE_INTERVAL_MS);
|
|
});
|
|
|
|
test('Track command', function() {
|
|
// track for 3 intervals
|
|
var duration = (3 * subject.TRACK_UPDATE_INTERVAL_MS) / 1000;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {}]);
|
|
|
|
// after 2 intervals...
|
|
fakeClock.tick(2 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
// we're mid-track, check we have a watch and timeout IDs
|
|
assert.notEqual(subject._watchPositionId, null);
|
|
assert.notEqual(subject._trackTimeoutId, null);
|
|
|
|
// after another interval...
|
|
fakeClock.tick(1 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
// .. tracking should have stopped.
|
|
// ensure we no longer have track and watch IDs.
|
|
assert.equal(subject._trackTimeoutId, null);
|
|
assert.equal(subject._watchPositionId, null);
|
|
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.beginHighPriority, 'command');
|
|
sinon.assert.alwaysCalledWith(FindMyDevice.endHighPriority, 'command');
|
|
|
|
// check wakelock count
|
|
var begin = FindMyDevice.beginHighPriority.callCount;
|
|
var end = FindMyDevice.beginHighPriority.callCount;
|
|
assert.equal(begin, end, 'begin and end count should match');
|
|
});
|
|
|
|
// We assume for this test that the interval used by the geolocation mock
|
|
// is significantly smaller than TRACK_UPDATE_INTERVAL.
|
|
test('Track command should throttle observed positions', function() {
|
|
// track for a long duration (e.g. 10 intervals)
|
|
var duration = 10 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
var positions = 0;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {
|
|
positions++;
|
|
}]);
|
|
|
|
// set the clock to just before the interval, ensure no locations are seen
|
|
var amountBefore = 2;
|
|
fakeClock.tick(subject.TRACK_UPDATE_INTERVAL_MS - amountBefore);
|
|
assert.equal(positions, 0, 'no update intervals should have passed');
|
|
// set the clock to almost two intervals, ensure only one location is seen
|
|
fakeClock.tick(subject.TRACK_UPDATE_INTERVAL_MS);
|
|
assert.equal(positions, 1, '1 update interval should have passed');
|
|
});
|
|
|
|
test('Track command should update its duration if invoked while running',
|
|
function() {
|
|
// track for a long duration (e.g. 10 intervals)
|
|
var duration = 10 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
var positions = 0;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {
|
|
positions++;
|
|
}]);
|
|
|
|
// ensure the position count updates as expected (one position per
|
|
// interval)
|
|
fakeClock.tick(subject.TRACK_UPDATE_INTERVAL_MS);
|
|
assert.equal(positions, 1, '1 update interval should have passed');
|
|
|
|
// reset the duration to a shorter value:
|
|
duration = 2 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {
|
|
positions++;
|
|
}]);
|
|
|
|
// allow the timer to progress to after the duration completes
|
|
fakeClock.tick(5 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
|
|
// ensure tracking stopped at the end of the new duration:
|
|
// position count should be at 3
|
|
assert.equal(positions, 3, '2 more (3) intervals should have passed');
|
|
|
|
// and the track and watch IDs should be null
|
|
assert.equal(subject._trackTimeoutId, null);
|
|
assert.equal(subject._watchPositionId, null);
|
|
|
|
// check wakelock count
|
|
var begin = FindMyDevice.beginHighPriority.callCount;
|
|
var end = FindMyDevice.beginHighPriority.callCount;
|
|
assert.equal(begin, end, 'begin and end count should match');
|
|
});
|
|
|
|
test('Track command should stop if duration is zero',
|
|
function() {
|
|
// track for a long duration (e.g. 10 intervals)
|
|
var duration = 10 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
var positions = 0;
|
|
subject.invokeCommand('track', [duration, function(retval, position) {
|
|
positions++;
|
|
}]);
|
|
|
|
fakeClock.tick(subject.TRACK_UPDATE_INTERVAL_MS);
|
|
|
|
// after a single interval, stop the tracking
|
|
subject.invokeCommand('track', [0, function(retval) {
|
|
assert.equal(retval, true);
|
|
}]);
|
|
|
|
// allow more intervals to pass
|
|
fakeClock.tick(2 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
|
|
// and ensure the position count remains at 1
|
|
assert.equal(positions, 1);
|
|
|
|
// check the track and watch IDs should be null
|
|
assert.equal(subject._trackTimeoutId, null);
|
|
assert.equal(subject._watchPositionId, null);
|
|
});
|
|
|
|
test('track should not leak wakelocks with nonzero duration', function() {
|
|
// start tracking
|
|
var duration = 9 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
subject.invokeCommand('track', [duration, function() {}]);
|
|
|
|
// once tracking is complete, check the wakelock call counts match
|
|
fakeClock.tick(10 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
var begin = FindMyDevice.beginHighPriority.callCount;
|
|
var end = FindMyDevice.beginHighPriority.callCount;
|
|
assert.equal(begin, end, 'begin and end count should match');
|
|
});
|
|
|
|
test('track should not leak wakelocks with zero duration', function() {
|
|
subject.invokeCommand('track', [0, function() {}]);
|
|
var begin = FindMyDevice.beginHighPriority.callCount;
|
|
var end = FindMyDevice.beginHighPriority.callCount;
|
|
assert.equal(begin, end, 'begin and end count should match');
|
|
});
|
|
|
|
test('track should not leak wakelocks if stopped mid-track', function() {
|
|
// track for a long duration (e.g. 10 intervals)
|
|
var duration = 10 * subject.TRACK_UPDATE_INTERVAL_MS / 1000;
|
|
subject.invokeCommand('track', [duration, function() {}]);
|
|
|
|
// after a single interval, stop the tracking
|
|
fakeClock.tick(subject.TRACK_UPDATE_INTERVAL_MS);
|
|
subject.invokeCommand('track', [0, function(retval) {}]);
|
|
|
|
// go past the original duration
|
|
fakeClock.tick(10 * subject.TRACK_UPDATE_INTERVAL_MS);
|
|
|
|
// check the wakelock call counts
|
|
var begin = FindMyDevice.beginHighPriority.callCount;
|
|
var end = FindMyDevice.beginHighPriority.callCount;
|
|
assert.equal(begin, end, 'begin and end count should match');
|
|
});
|
|
});
|
|
|
|
test('Bug 1027325 - correctly check that passcode lock is set', function() {
|
|
MockSettingsListener.mTriggerCallback('lockscreen.enabled', true);
|
|
MockSettingsListener.mTriggerCallback('lockscreen.passcode-lock.enabled',
|
|
'1234');
|
|
assert.equal(true, subject.deviceHasPasscode());
|
|
});
|
|
|
|
test('Bug 1027325 - correctly check that passcode lock is unset', function() {
|
|
MockSettingsListener.mTriggerCallback('lockscreen.enabled', false);
|
|
MockSettingsListener.mTriggerCallback('lockscreen.passcode-lock.enabled',
|
|
false);
|
|
assert.equal(false, subject.deviceHasPasscode());
|
|
});
|
|
|
|
test('Bug 1027325 - correctly check that lockscreen is set, but passcode ' +
|
|
'lock is unset', function() {
|
|
MockSettingsListener.mTriggerCallback('lockscreen.enabled', true);
|
|
MockSettingsListener.mTriggerCallback('lockscreen.passcode-lock.enabled',
|
|
false);
|
|
assert.equal(false, subject.deviceHasPasscode());
|
|
});
|
|
|
|
suite('KillSwitch command', function() {
|
|
suite('With API existing', function() {
|
|
setup(function() {
|
|
this.sinon.spy(navigator.mozKillSwitch, 'enable');
|
|
});
|
|
|
|
test('Basic invocation', function(done) {
|
|
subject.invokeCommand('killswitch', [ function(retval, error) {
|
|
sinon.assert.calledOnce(navigator.mozKillSwitch.enable);
|
|
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.beginHighPriority, 'command');
|
|
} ]);
|
|
|
|
sinon.assert.calledOnce(FindMyDevice.endHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.endHighPriority, 'command');
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
suite('Without API existing', function() {
|
|
setup(function() {
|
|
navigator.mozKillSwitch = undefined;
|
|
});
|
|
|
|
test('No invocation', function(done) {
|
|
subject.invokeCommand('killswitch', [ function(retval, error) {
|
|
sinon.assert.calledOnce(FindMyDevice.beginHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.beginHighPriority, 'command');
|
|
} ]);
|
|
|
|
sinon.assert.calledOnce(FindMyDevice.endHighPriority);
|
|
sinon.assert.calledWith(FindMyDevice.endHighPriority, 'command');
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
test('List of accepted commands', function() {
|
|
MockSettingsListener.mTriggerCallback('geolocation.enabled', true);
|
|
|
|
var allCommands = ['track', 'erase', 'ring', 'lock', 'killswitch'];
|
|
var enabledCommands = subject.getEnabledCommands();
|
|
assert.deepEqual(enabledCommands.sort(), allCommands.sort());
|
|
|
|
// track should be disabled when geolocation is disabled
|
|
MockSettingsListener.mTriggerCallback('geolocation.enabled', false);
|
|
allCommands = ['erase', 'ring', 'lock', 'killswitch'];
|
|
enabledCommands = subject.getEnabledCommands();
|
|
assert.deepEqual(enabledCommands.sort(), allCommands.sort());
|
|
});
|
|
|
|
teardown(function() {
|
|
navigator.mozPower = realMozPower;
|
|
navigator.mozKillSwitch = realKillSwitch;
|
|
navigator.mozApps = realMozApps;
|
|
navigator.mozSettings = realMozSettings;
|
|
|
|
delete window.DUMP;
|
|
|
|
fakeClock.restore();
|
|
|
|
delete window.FindMyDevice;
|
|
});
|
|
});
|
|
|
|
|
|
|
|
suite('FindMyDevice (with real clock) >', function() {
|
|
var realMozSettings;
|
|
|
|
mocksForFindMyDevice.attachTestHelpers();
|
|
|
|
var subject;
|
|
setup(function(done) {
|
|
|
|
// replace shared/js/dump.js
|
|
window.DUMP = function() {};
|
|
|
|
realMozSettings = navigator.mozSettings;
|
|
MockNavigatorSettings.mSetup();
|
|
navigator.mozSettings = MockNavigatorSettings;
|
|
|
|
window.FindMyDevice = {
|
|
beginHighPriority: this.sinon.stub(),
|
|
endHighPriority: this.sinon.stub()
|
|
};
|
|
|
|
require('/js/commands.js', function() {
|
|
subject = Commands;
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('Lock command', function(done) {
|
|
var code = '1234', message = 'locked!';
|
|
var DIGEST_VALUE = 'lockscreen.passcode-lock.digest.value';
|
|
var settingsLock = MockSettingsListener.getSettingsLock();
|
|
var oldDigest = settingsLock.get(DIGEST_VALUE);
|
|
|
|
subject.invokeCommand('lock', [message, code, function(retval) {
|
|
assert.equal(retval, true);
|
|
sinon.assert.calledWith(FindMyDevice.beginHighPriority, 'command');
|
|
sinon.assert.calledWith(FindMyDevice.endHighPriority, 'command');
|
|
var lock = MockSettingsListener.getSettingsLock().locks.pop();
|
|
assert.deepEqual({
|
|
'lockscreen.enabled': true,
|
|
'lockscreen.notifications-preview.enabled': false,
|
|
'lockscreen.passcode-lock.enabled': true,
|
|
'lockscreen.lock-message': message,
|
|
'lockscreen.lock-immediately': true
|
|
}, lock, 'check that the correct settings were set');
|
|
assert.notEqual(oldDigest, lock[DIGEST_VALUE],
|
|
'check that Passcode digest changed');
|
|
|
|
done();
|
|
}]);
|
|
});
|
|
|
|
test('Bug 1201530 - check existing passcode override', function() {
|
|
var code = '2342', message = 'already locked!';
|
|
var stubPasscodeSet = sinon.stub(PasscodeHelper, 'set', function() {
|
|
return Promise.resolve(true);
|
|
});
|
|
// Simulate already-locked device.
|
|
MockSettingsListener.mTriggerCallback('lockscreen.enabled', true);
|
|
MockSettingsListener.mTriggerCallback('lockscreen.passcode-lock.enabled',
|
|
true);
|
|
subject.invokeCommand('lock', [message, code, function() {}]);
|
|
assert.isTrue(stubPasscodeSet.calledWith(code),
|
|
'existing passcode is not overridden');
|
|
stubPasscodeSet.restore();
|
|
});
|
|
|
|
teardown(function() {
|
|
// clean up sinon.js stubs
|
|
navigator.mozSettings = realMozSettings;
|
|
MockNavigatorSettings.mTeardown();
|
|
delete window.DUMP;
|
|
delete window.FindMyDevice;
|
|
});
|
|
});
|