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
57
gaia/apps/system/test/unit/lockscreen/lockscreen_agent_test.js
Executable file
57
gaia/apps/system/test/unit/lockscreen/lockscreen_agent_test.js
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
/* globals LockScreenAgent */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/js/lockscreen_agent.js');
|
||||
|
||||
suite('system/LockScreenAgent', function() {
|
||||
var lockScreenAgent;
|
||||
setup(function() {
|
||||
lockScreenAgent = new LockScreenAgent(document.createElement('div'));
|
||||
});
|
||||
|
||||
test('It should register all events', function() {
|
||||
lockScreenAgent.start();
|
||||
var table = lockScreenAgent.configs.listens.reduce((current, ename) => {
|
||||
current[ename] = false;
|
||||
return current;
|
||||
}, {});
|
||||
this.sinon.stub(window, 'addEventListener',
|
||||
(ename) => {
|
||||
table[ename] = true;
|
||||
});
|
||||
// why we need start twice: since the table in the agent would only be
|
||||
// generated after we call the start, the start function would use
|
||||
// it to register all events at the same time.
|
||||
//
|
||||
// I don't want to pull the talbe out of the init function, because
|
||||
// we need to maintain the duplicated tables while the events change
|
||||
// in the future.
|
||||
lockScreenAgent.start();
|
||||
Object.keys(table).forEach((ename) => {
|
||||
assert.equal(table[ename], true,
|
||||
'the event "' + ename + '" is not registered');
|
||||
});
|
||||
});
|
||||
|
||||
test('It would forward IAC message', function() {
|
||||
var stubPort = {
|
||||
postMessage: function() {}
|
||||
};
|
||||
window.IACHandler = {
|
||||
getPort: () => stubPort
|
||||
};
|
||||
var stubPostMessage = this.sinon.stub(stubPort, 'postMessage');
|
||||
lockScreenAgent.start();
|
||||
lockScreenAgent.handleEvent(
|
||||
new CustomEvent('lockscreen-request-mediacommand', {
|
||||
detail: 'fakecommand'
|
||||
})
|
||||
);
|
||||
assert.isTrue(stubPostMessage.calledWithMatch(
|
||||
sinon.match((message) => {
|
||||
return message.command === 'fakecommand';
|
||||
})
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
16
gaia/apps/system/test/unit/lockscreen/lockscreen_base_state_test.js
Executable file
16
gaia/apps/system/test/unit/lockscreen/lockscreen_base_state_test.js
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
/* global LockScreenBaseState */
|
||||
|
||||
'use strict';
|
||||
requireApp('system/lockscreen/js/lockscreen_base_state.js');
|
||||
suite('sytem/LockScreenBaseState', function() {
|
||||
var subject;
|
||||
setup(function() {
|
||||
subject = (new LockScreenBaseState()).start();
|
||||
});
|
||||
test('it has the transferTo and transferOut method', function() {
|
||||
assert.isTrue('undefined' !== typeof subject.transferOut,
|
||||
'there is something wrong');
|
||||
assert.isTrue('undefined' !== typeof subject.transferTo,
|
||||
'there is something wrong');
|
||||
});
|
||||
});
|
||||
38
gaia/apps/system/test/unit/lockscreen/lockscreen_frame_agent_test.js
Executable file
38
gaia/apps/system/test/unit/lockscreen/lockscreen_frame_agent_test.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
/* globals LockScreenFrameAgent */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/lockscreen_frame_agent.js');
|
||||
|
||||
suite('system/LockScreenFrameAgent', function() {
|
||||
var lockScreenFrameAgent;
|
||||
setup(function() {
|
||||
lockScreenFrameAgent =
|
||||
new LockScreenFrameAgent(window);
|
||||
});
|
||||
|
||||
test('It should register all events', function() {
|
||||
lockScreenFrameAgent.start();
|
||||
var table = lockScreenFrameAgent.configs
|
||||
.listens.reduce((current, ename) => {
|
||||
current[ename] = false;
|
||||
return current;
|
||||
}, {});
|
||||
this.sinon.stub(window, 'addEventListener',
|
||||
(ename) => {
|
||||
table[ename] = true;
|
||||
});
|
||||
// why we need start twice: since the table in the agent would only be
|
||||
// generated after we call the start, the start function would use
|
||||
// it to register all events at the same time.
|
||||
//
|
||||
// I don't want to pull the talbe out of the init function, because
|
||||
// we need to maintain the duplicated tables while the events change
|
||||
// in the future.
|
||||
lockScreenFrameAgent.start();
|
||||
Object.keys(table).forEach((ename) => {
|
||||
assert.equal(table[ename], true,
|
||||
'the event "' + ename + '" is not registered');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
576
gaia/apps/system/test/unit/lockscreen/lockscreen_inputpad_test.js
Executable file
576
gaia/apps/system/test/unit/lockscreen/lockscreen_inputpad_test.js
Executable file
|
|
@ -0,0 +1,576 @@
|
|||
/* global LockScreenInputpad */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/shared/test/unit/mocks/mock_settings_listener.js');
|
||||
requireApp('system/lockscreen/js/lockscreen_inputpad.js');
|
||||
|
||||
var mocks = new window.MocksHelper([
|
||||
'SettingsListener'
|
||||
]).init();
|
||||
|
||||
suite('LockScreenInputpad', function() {
|
||||
var subject;
|
||||
var mockLockScreenFacade;
|
||||
var mockGetElementById;
|
||||
var mockHtmlKeypad;
|
||||
var oneKey;
|
||||
var twoKey;
|
||||
var threeKey;
|
||||
var emergencyKey;
|
||||
var cancelKey;
|
||||
var backKey;
|
||||
mocks.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
mockGetElementById = sinon.stub(window.document, 'getElementById',
|
||||
function() {
|
||||
var elem = document.createElement('div');
|
||||
elem.querySelector = function() {
|
||||
return document.createElement('div');
|
||||
};
|
||||
return elem;
|
||||
});
|
||||
mockLockScreenFacade = {};
|
||||
|
||||
// Build a mock keypad that helper functions can work on.
|
||||
// Uses verbatim HTML from keypad from lockscreen_inputpad_frame.html
|
||||
mockHtmlKeypad = document.createElement('html');
|
||||
mockHtmlKeypad.appendChild(document.createElement('div'));
|
||||
// Don't use + to concatenate strings, or eslint will jump
|
||||
// at you for use of unsafe innerHTML.
|
||||
mockHtmlKeypad.firstChild.innerHTML = `
|
||||
<a role="key" href="#" data-key="1"
|
||||
class="row0"><div>1<span></span></div></a>
|
||||
<a role="key" href="#" data-key="2"
|
||||
class="row0"><div>2<span>ABC</span></div></a>
|
||||
<a role="key" href="#" data-key="3"
|
||||
class="row0"><div>3<span>DEF</span></div></a>
|
||||
<a role="button" href="#" data-key="e"
|
||||
class="lockscreen-passcode-pad-func last-row row3"><div>
|
||||
<span data-l10n-id="emergency-call-button">Emergency Call</span>
|
||||
</div></a>
|
||||
<a role="key" href="#" data-key="0" class="last-row row3">
|
||||
<div>0</div></a>
|
||||
<a role="button" href="#" data-key="c"
|
||||
class="lockscreen-passcode-pad-func last-row row3"><div>
|
||||
<span data-l10n-id="cancel">Cancel</span></div></a>
|
||||
<a role="button" href="#" data-key="b" class="last-row row3">
|
||||
<div data-l10n-id="undo">⌫</div></a>
|
||||
`;
|
||||
oneKey = mockHtmlKeypad.firstChild.children[0];
|
||||
twoKey = mockHtmlKeypad.firstChild.children[1];
|
||||
threeKey = mockHtmlKeypad.firstChild.children[2];
|
||||
emergencyKey = mockHtmlKeypad.firstChild.children[3];
|
||||
cancelKey = mockHtmlKeypad.firstChild.children[5];
|
||||
backKey = mockHtmlKeypad.firstChild.children[6];
|
||||
|
||||
subject = new LockScreenInputpad(mockLockScreenFacade);
|
||||
var stub = sinon.stub(subject, 'toggleEmergencyButton');
|
||||
subject.start();
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
mockGetElementById.restore();
|
||||
});
|
||||
|
||||
test('Emergency call: should disable when has no telephony', function() {
|
||||
navigator.mozTelephony = null;
|
||||
subject.passcodePad = document.createElement('div');
|
||||
subject.passcodePad.innerHTML = `<a data-key="e">`;
|
||||
subject.emergencyCallBtn =
|
||||
subject.passcodePad.querySelector('a[data-key=e]');
|
||||
subject.toggleEmergencyButton();
|
||||
assert.isTrue(subject.passcodePad.querySelector('a[data-key=e]')
|
||||
.classList.contains('disabled'));
|
||||
});
|
||||
|
||||
test('Emergency call: should enable when has telephony', function() {
|
||||
navigator.mozTelephony = {
|
||||
calls: {
|
||||
length: 1
|
||||
}
|
||||
};
|
||||
subject.passcodePad = document.createElement('div');
|
||||
subject.passcodePad.appendChild(emergencyKey);
|
||||
subject.emergencyCallBtn =
|
||||
subject.passcodePad.querySelector('a[data-key=e]');
|
||||
subject.toggleEmergencyButton();
|
||||
assert.isFalse(subject.passcodePad.querySelector('a[data-key=e]')
|
||||
.classList.contains('disabled'));
|
||||
});
|
||||
|
||||
suite('Inputpad key helper functions >', function() {
|
||||
|
||||
test('find the correct anchor element for touch targets', function () {
|
||||
var method = subject._anchorForTarget;
|
||||
var a = twoKey; // key to work with is "2"
|
||||
assert.isTrue(a === method(a),
|
||||
'anchor detection fails for a level 0 target');
|
||||
assert.isTrue(a === method(a.firstElementChild),
|
||||
'anchor detection fails for a level 1 target');
|
||||
assert.isTrue(a === method(a.firstElementChild.firstElementChild),
|
||||
'anchor detection fails for a level 2 target');
|
||||
// The tested method currently only supports up to 2nd-level
|
||||
// children and we don't really care if it supports more.
|
||||
assert.isNull(method(mockHtmlKeypad),
|
||||
'anchorForTarget fails unexpectedly for top element');
|
||||
assert.isNull(method(mockHtmlKeypad.firstElementChild),
|
||||
'anchorForTarget fails unexpectedly for non-keypad element');
|
||||
assert.isNull(method(null),
|
||||
'anchorForTarget improperly handles null targets');
|
||||
});
|
||||
|
||||
test('extract the correct key from touch targets', function () {
|
||||
// Get a pointer to a sub-element of mock key "2"
|
||||
var target = twoKey.firstElementChild;
|
||||
assert.isTrue('2' === subject._keyForTarget(target),
|
||||
'keyForTarget does not extract the expected key value');
|
||||
assert.isNull(subject._keyForTarget(null),
|
||||
'keyForTarget does not properly handle null targets');
|
||||
});
|
||||
|
||||
test('find the correct element for touch events', function () {
|
||||
// This is not a good test, but since at this point our
|
||||
// unit testing can't fully simulate a rendered web page such
|
||||
// that document.elementFromPoint() is fully functional, all we
|
||||
// can do is check whether it is called with the expected
|
||||
// arguments.
|
||||
var stubFromPoint = sinon.stub(document, 'elementFromPoint',
|
||||
function() {
|
||||
return 'CORRECT_ELEMENT';
|
||||
});
|
||||
var event = {
|
||||
changedTouches: [
|
||||
{
|
||||
clientX: 23,
|
||||
clientY: 42
|
||||
}
|
||||
]
|
||||
};
|
||||
var result = subject._touchTarget(event);
|
||||
assert.isTrue(stubFromPoint.calledWith(23,42),
|
||||
'touchTarget does not call .elementFromPoint() ' +
|
||||
'with expected arguments');
|
||||
assert.isTrue(result === 'CORRECT_ELEMENT',
|
||||
'touchTarget does not find the expected element');
|
||||
stubFromPoint.restore();
|
||||
});
|
||||
|
||||
test('decorates active key with CSS classes', function() {
|
||||
var a = twoKey; // anchor of key "2"
|
||||
var activeClass = 'active-key';
|
||||
a.classList.remove(activeClass);
|
||||
subject._makeKeyActive(a);
|
||||
assert.isTrue(a.classList.contains(activeClass),
|
||||
'makeActive does not add set active-key CSS class');
|
||||
subject._makeKeyInactive(a);
|
||||
assert.isFalse(a.classList.contains(activeClass),
|
||||
'makeInactive does not remove active-key CSS class');
|
||||
});
|
||||
|
||||
test('correctly visualizes touchmove key transitions', function() {
|
||||
var oldTarget = twoKey; // anchor of key "2"
|
||||
var newTarget = threeKey; // anchor of key "3"
|
||||
var activeClass = 'active-key';
|
||||
oldTarget.classList.remove(activeClass);
|
||||
newTarget.classList.remove(activeClass);
|
||||
|
||||
subject._visualizeKeypress(oldTarget);
|
||||
assert.isTrue(oldTarget.classList.contains(activeClass),
|
||||
'vizualizeKeypress does not make initial keys active');
|
||||
|
||||
oldTarget.classList.remove(activeClass);
|
||||
subject._visualizeKeypress(oldTarget);
|
||||
assert.isFalse(oldTarget.classList.contains(activeClass) &&
|
||||
!newTarget.classList.contains(activeClass),
|
||||
'vizualizeKeypress changes inactive state without key change');
|
||||
|
||||
oldTarget.classList.add(activeClass);
|
||||
subject._visualizeKeypress(oldTarget);
|
||||
assert.isTrue(oldTarget.classList.contains(activeClass) &&
|
||||
!newTarget.classList.contains(activeClass),
|
||||
'vizualizeKeypress changes active state without key change');
|
||||
|
||||
subject._visualizeKeypress(newTarget);
|
||||
assert.isFalse(oldTarget.classList.contains(activeClass),
|
||||
'vizualizeKeypress does not make old keys inactive');
|
||||
assert.isTrue(newTarget.classList.contains(activeClass),
|
||||
'makeInactive does not make new keys active');
|
||||
|
||||
subject._visualizeKeypress(null);
|
||||
assert.isFalse(newTarget.classList.contains(activeClass),
|
||||
'vizualizeKeypress does not reset active keys on null');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('updatePassCodeUI >', function() {
|
||||
test('it would add passcode-entered class while passcode entered',
|
||||
function() {
|
||||
var method = subject.updatePassCodeUI;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
passCodeEntered: 'foo'
|
||||
},
|
||||
passcodePad: document.createElement('div'),
|
||||
passcodeCode: document.createElement('div')
|
||||
};
|
||||
method.apply(mockSubject);
|
||||
assert.isTrue(mockSubject.passcodePad
|
||||
.classList.contains('passcode-entered'),
|
||||
'passcode-entered class not added when one is entered');
|
||||
});
|
||||
|
||||
test('it would clear passcode-entered class while no passcode entered',
|
||||
function() {
|
||||
var method = subject.updatePassCodeUI;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
passCodeEntered: ''
|
||||
},
|
||||
passcodePad: document.createElement('div'),
|
||||
passcodeCode: document.createElement('div')
|
||||
};
|
||||
mockSubject.passcodePad.classList.add('passcode-entered');
|
||||
method.apply(mockSubject);
|
||||
assert.isFalse(mockSubject.passcodePad
|
||||
.classList.contains('passcode-entered'),
|
||||
'passcode-entered class not removed when none is entered');
|
||||
});
|
||||
|
||||
test('it would set error class during error timeout state',
|
||||
function() {
|
||||
var method = subject.updatePassCodeUI;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
passCodeEntered: '',
|
||||
passCodeErrorTimeoutPending: true
|
||||
},
|
||||
passcodePad: document.createElement('div'),
|
||||
passcodeCode: document.createElement('div')
|
||||
};
|
||||
method.apply(mockSubject);
|
||||
assert.isTrue(mockSubject.passcodeCode
|
||||
.classList.contains('error'),
|
||||
'error class was not added during error timeout');
|
||||
});
|
||||
|
||||
test('it would clear error class when not in error timeout state',
|
||||
function() {
|
||||
var mockSubject = {
|
||||
states: {
|
||||
passCodeEntered: '',
|
||||
passCodeErrorTimeoutPending: false
|
||||
},
|
||||
passcodePad: document.createElement('div'),
|
||||
passcodeCode: document.createElement('div')
|
||||
};
|
||||
mockSubject.passcodeCode.classList.add('error');
|
||||
subject.updatePassCodeUI.apply(mockSubject);
|
||||
assert.isFalse(mockSubject.passcodeCode
|
||||
.classList.contains('error'),
|
||||
'error class was not cleared outside error timeout');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Events >', function() {
|
||||
suite('passcode-validationfailed >', function() {
|
||||
var stubUpdatePassCodeUI;
|
||||
setup(function() {
|
||||
stubUpdatePassCodeUI =
|
||||
sinon.stub(subject, 'updatePassCodeUI');
|
||||
});
|
||||
test('event would update the UI', function() {
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationfailed'));
|
||||
assert.isTrue(stubUpdatePassCodeUI.called,
|
||||
'|updatePassCodeUI| method wasn\'t called');
|
||||
});
|
||||
test('sets error timeout state', function() {
|
||||
subject.states.passCodeErrorTimeoutPending = false;
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationfailed'));
|
||||
assert.isTrue(subject.states.passCodeErrorTimeoutPending === true,
|
||||
'error timeout state was not set');
|
||||
});
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('passcode-validationsuccess >', function() {
|
||||
var stubUpdatePassCodeUI;
|
||||
setup(function() {
|
||||
stubUpdatePassCodeUI =
|
||||
sinon.stub(subject, 'updatePassCodeUI');
|
||||
});
|
||||
test('it would reset UI', function() {
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationsuccess'));
|
||||
assert.isTrue(stubUpdatePassCodeUI.called,
|
||||
'|updatePassCodeUI| method wasn\'t called');
|
||||
});
|
||||
test('it would reset internal state', function() {
|
||||
subject.states.passCodeEntered = 'fooo';
|
||||
subject.states.passCodeErrorTimeoutPending = true;
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationsuccess'));
|
||||
assert.isTrue(subject.states.passCodeEntered === '',
|
||||
'entered pass code was not cleared');
|
||||
assert.isTrue(subject.states.passCodeErrorTimeoutPending === false,
|
||||
'timeout error state was not cleared');
|
||||
});
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('passcode-validationreset >', function() {
|
||||
// Currently identical to validationsuccess
|
||||
var stubUpdatePassCodeUI;
|
||||
setup(function() {
|
||||
stubUpdatePassCodeUI =
|
||||
sinon.stub(subject, 'updatePassCodeUI');
|
||||
});
|
||||
test('it would reset UI', function() {
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationreset'));
|
||||
assert.isTrue(stubUpdatePassCodeUI.called,
|
||||
'|updatePassCodeUI| method wasn\'t called');
|
||||
});
|
||||
test('it would reset internal state', function() {
|
||||
subject.states.passCodeEntered = 'fooo';
|
||||
subject.states.passCodeErrorTimeoutPending = true;
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-notify-passcode-validationreset'));
|
||||
assert.isTrue(subject.states.passCodeEntered === '',
|
||||
'entered pass code was not cleared');
|
||||
assert.isTrue(subject.states.passCodeErrorTimeoutPending === false,
|
||||
'timeout error state was not cleared');
|
||||
});
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('lockscreen-inputappopened >', function() {
|
||||
var stubUpdatePassCodeUI;
|
||||
setup(function() {
|
||||
stubUpdatePassCodeUI =
|
||||
sinon.stub(subject, 'updatePassCodeUI');
|
||||
});
|
||||
test('it would update UI', function() {
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-inputappopened'));
|
||||
assert.isTrue(stubUpdatePassCodeUI.called,
|
||||
'the |updatePassCodeUI| method wasn\'t called');
|
||||
});
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('lockscreen-inputappclosed >', function() {
|
||||
// Currently identical to inputappopened
|
||||
var stubUpdatePassCodeUI;
|
||||
setup(function() {
|
||||
stubUpdatePassCodeUI =
|
||||
sinon.stub(subject, 'updatePassCodeUI');
|
||||
});
|
||||
test('it would reset UI', function() {
|
||||
subject.handleEvent(
|
||||
new CustomEvent('lockscreen-inputappclosed'));
|
||||
assert.isTrue(stubUpdatePassCodeUI.called,
|
||||
'the |updatePassCodeUI| method wasn\'t called');
|
||||
});
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('keypad input events >', function() {
|
||||
var target;
|
||||
var evt;
|
||||
var stubPreventDefault;
|
||||
var stubFromPoint;
|
||||
var spyVisualize;
|
||||
var stubVibrate;
|
||||
var stubHandleInput;
|
||||
|
||||
setup(function() {
|
||||
target = mockHtmlKeypad.firstChild.children[1]; // key "2"
|
||||
evt = {
|
||||
type: 'foofoo',
|
||||
target: target,
|
||||
changedTouches: [
|
||||
{
|
||||
clientX: 23,
|
||||
clientY: 42
|
||||
}
|
||||
],
|
||||
preventDefault: function() {}
|
||||
};
|
||||
stubPreventDefault = sinon.stub(evt, 'preventDefault');
|
||||
stubFromPoint = sinon.stub(document, 'elementFromPoint',
|
||||
function() {
|
||||
return evt.target;
|
||||
});
|
||||
// Using a spy when internal state must be updated
|
||||
// _visualizeKeypress keeps track of lastTouchedKey state
|
||||
spyVisualize = sinon.spy(subject, '_visualizeKeypress');
|
||||
stubVibrate = sinon.stub(navigator, 'vibrate');
|
||||
stubHandleInput = sinon.stub(subject, 'handlePassCodeInput');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
stubPreventDefault.restore();
|
||||
stubFromPoint.restore();
|
||||
spyVisualize.restore();
|
||||
stubVibrate.restore();
|
||||
stubHandleInput.restore();
|
||||
});
|
||||
|
||||
suite('touchstart >', function() {
|
||||
setup(function() {
|
||||
evt.type = 'touchstart';
|
||||
});
|
||||
test('updates keypad', function() {
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(spyVisualize.calledWith(evt.target),
|
||||
'does not update keypad');
|
||||
});
|
||||
test('vibrates', function() {
|
||||
subject.states.padVibrationEnabled = true;
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(stubVibrate.called,
|
||||
'onTouchStart does not vibrate');
|
||||
stubVibrate.restore();
|
||||
});
|
||||
});
|
||||
|
||||
suite('touchmove >', function() {
|
||||
setup(function() {
|
||||
evt.type = 'touchmove';
|
||||
});
|
||||
test('updates keypad', function() {
|
||||
// Call with key "2"
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(spyVisualize.calledWith(evt.target),
|
||||
'does not update keypad');
|
||||
});
|
||||
});
|
||||
|
||||
suite('touchend >', function() {
|
||||
setup(function() {
|
||||
evt.type = 'touchend';
|
||||
});
|
||||
test('prevents default click event', function() {
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(stubPreventDefault.called,
|
||||
'does not prevent default click event');
|
||||
});
|
||||
test('updates keypad', function() {
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(spyVisualize.calledWith(null),
|
||||
'does not update keypad');
|
||||
});
|
||||
test('handles the right key', function() {
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(stubHandleInput.calledWith(target),
|
||||
'does not handle the expected key');
|
||||
});
|
||||
});
|
||||
|
||||
suite('click >', function() {
|
||||
setup(function() {
|
||||
evt.type = 'click';
|
||||
});
|
||||
test('calls click handler', function() {
|
||||
var stubOnClick = sinon.stub(subject, 'onClick');
|
||||
evt.type = 'click';
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(stubOnClick.calledWith(evt),
|
||||
'onClick handler is not called as expected');
|
||||
stubOnClick.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('pass code input >', function() {
|
||||
var stubUpdatePassCodeUI;
|
||||
var stubInvokeSecureApp;
|
||||
var stubDispatchEvent;
|
||||
var stubCheckPassCode;
|
||||
|
||||
setup(function() {
|
||||
|
||||
// Build a mock lockscreen
|
||||
subject.lockScreen = {
|
||||
invokeSecureApp: function() {},
|
||||
checkPassCode: function() {},
|
||||
_unlockingMessage: {
|
||||
notificationId: 'fakeid'
|
||||
}
|
||||
};
|
||||
subject.passcodePad = { classList: new Set() };
|
||||
stubUpdatePassCodeUI = sinon.stub(subject, 'updatePassCodeUI');
|
||||
stubInvokeSecureApp =
|
||||
sinon.stub(subject.lockScreen, 'invokeSecureApp');
|
||||
stubDispatchEvent = sinon.stub(subject, 'dispatchEvent');
|
||||
stubCheckPassCode =
|
||||
sinon.stub(subject.lockScreen, 'checkPassCode');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
stubUpdatePassCodeUI.restore();
|
||||
stubInvokeSecureApp.restore();
|
||||
stubDispatchEvent.restore();
|
||||
stubCheckPassCode.restore();
|
||||
});
|
||||
|
||||
test('it correctly handles key input', function() {
|
||||
subject.handlePassCodeInput(twoKey);
|
||||
assert.isTrue(subject.states.passCodeEntered === '2',
|
||||
'it does not correctly handle the first key');
|
||||
subject.handlePassCodeInput(threeKey);
|
||||
assert.isTrue(subject.states.passCodeEntered === '23',
|
||||
'it does not correctly handle the second key');
|
||||
});
|
||||
|
||||
test('back key works as expected', function() {
|
||||
subject.handlePassCodeInput(twoKey);
|
||||
subject.handlePassCodeInput(threeKey);
|
||||
subject.handlePassCodeInput(backKey);
|
||||
assert.isTrue(subject.states.passCodeEntered === '2',
|
||||
'it does not correctly handle the back key');
|
||||
});
|
||||
|
||||
test('it checks the pass code when it should', function() {
|
||||
subject.handlePassCodeInput(twoKey);
|
||||
subject.handlePassCodeInput(threeKey);
|
||||
subject.handlePassCodeInput(twoKey);
|
||||
subject.handlePassCodeInput(threeKey);
|
||||
assert.isTrue(stubCheckPassCode.calledWith('2323'),
|
||||
'it does not properly check the pass code');
|
||||
});
|
||||
|
||||
test('it opens the emergency dialer', function() {
|
||||
subject.handlePassCodeInput(emergencyKey);
|
||||
assert.isTrue(stubInvokeSecureApp.calledWith('emergency-call'),
|
||||
'it fails to open the emergency dialer when requested');
|
||||
});
|
||||
|
||||
test('it would clear notification opening ID', function() {
|
||||
subject.dispatchEvent = function() {};
|
||||
subject.handlePassCodeInput(emergencyKey);
|
||||
assert.isUndefined(
|
||||
subject.lockScreen._unlockingMessage.notificationId);
|
||||
subject.lockScreen._unlockingMessage.notificationId = 'fakeid';
|
||||
subject.handlePassCodeInput(cancelKey);
|
||||
assert.isUndefined(
|
||||
subject.lockScreen._unlockingMessage.notificationId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
290
gaia/apps/system/test/unit/lockscreen/lockscreen_media_playback_test.js
Executable file
290
gaia/apps/system/test/unit/lockscreen/lockscreen_media_playback_test.js
Executable file
|
|
@ -0,0 +1,290 @@
|
|||
/* global MocksHelper, Service, MockL10n,
|
||||
LockScreenMediaPlaybackWidget */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_service.js');
|
||||
|
||||
var mocksForMediaPlayback = new MocksHelper([
|
||||
'Service'
|
||||
]).init();
|
||||
|
||||
suite('system/media playback widget', function() {
|
||||
mocksForMediaPlayback.attachTestHelpers();
|
||||
var realL10n;
|
||||
var widget;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
loadBodyHTML('/lockscreen/lockscreen.html');
|
||||
realL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = MockL10n;
|
||||
requireApp('system/lockscreen/js/lockscreen_media_playback.js', function() {
|
||||
widget = new LockScreenMediaPlaybackWidget(
|
||||
document.getElementById('lockscreen-media-container'),
|
||||
{nowPlayingAction: 'openapp'}
|
||||
);
|
||||
widget.origin = null;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozL10n = realL10n;
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
suite('constructor', function() {
|
||||
var container, subject;
|
||||
|
||||
setup(function() {
|
||||
var id = 'lockscreen-media-container';
|
||||
container = document.getElementById(id).cloneNode(true);
|
||||
delete container.id;
|
||||
});
|
||||
|
||||
['updateplaystatus', 'updatemetadata'].forEach((command) => {
|
||||
test('should request an up to date' + command, function(done) {
|
||||
var evtName = 'lockscreen-request-mediacommand';
|
||||
window.addEventListener(evtName, function gotIt(evt) {
|
||||
if (evt.detail == command) {
|
||||
window.removeEventListener(evtName, gotIt);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// To prevent this instance from interfering with other tests
|
||||
this.sinon.stub(window, 'addEventListener');
|
||||
|
||||
subject = new LockScreenMediaPlaybackWidget(
|
||||
container,
|
||||
{nowPlayingAction: 'openapp'}
|
||||
);
|
||||
subject.origin = null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('handleMessage', function() {
|
||||
var stubAppInfo, stubNowPlaying, stubPlaybackStatus;
|
||||
|
||||
function sendMediaCommsIAC(message) {
|
||||
var evt = new CustomEvent('iac-mediacomms', { detail: message });
|
||||
window.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
stubAppInfo = this.sinon.stub(widget, 'updateAppInfo');
|
||||
stubNowPlaying = this.sinon.stub(widget, 'updateNowPlaying');
|
||||
stubPlaybackStatus = this.sinon.stub(widget, 'updatePlaybackStatus');
|
||||
});
|
||||
|
||||
test('updateAppInfo', function() {
|
||||
sendMediaCommsIAC({ type: 'appinfo', data: 'appinfo-data' });
|
||||
assert.isTrue(stubAppInfo.calledOnce);
|
||||
assert.isTrue(stubAppInfo.calledWith('appinfo-data'));
|
||||
});
|
||||
|
||||
test('updateNowPlaying', function() {
|
||||
sendMediaCommsIAC({ type: 'nowplaying', data: 'nowplaying-data' });
|
||||
assert.isTrue(stubNowPlaying.calledOnce);
|
||||
assert.isTrue(stubNowPlaying.calledWith('nowplaying-data'));
|
||||
});
|
||||
|
||||
test('updatePlaybackStatus', function() {
|
||||
sendMediaCommsIAC({ type: 'status', data: 'status-data' });
|
||||
assert.isTrue(stubPlaybackStatus.calledOnce);
|
||||
assert.isTrue(stubPlaybackStatus.calledWith('status-data'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateAppInfo', function() {
|
||||
teardown(function() {
|
||||
widget.origin = null;
|
||||
});
|
||||
|
||||
test('Do nothing if no app info provided', function() {
|
||||
widget.updateAppInfo();
|
||||
assert.isNull(widget.origin);
|
||||
});
|
||||
|
||||
test('Update app info', function() {
|
||||
var info = { origin: 'origin' };
|
||||
widget.updateAppInfo(info);
|
||||
assert.equal(widget.origin, info.origin);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateNowPlaying', function() {
|
||||
teardown(function() {
|
||||
widget.track.textContent = '';
|
||||
});
|
||||
|
||||
test('Do nothing if no metadata provided', function() {
|
||||
widget.updateNowPlaying();
|
||||
assert.equal(widget.track.textContent, '');
|
||||
});
|
||||
|
||||
test('Update now playing', function() {
|
||||
var metadata = { title: 'title', artist: 'artist' };
|
||||
widget.updateNowPlaying(metadata);
|
||||
assert.equal(widget.track.textContent, 'title — artist');
|
||||
assert.equal(widget.track.innerHTML,
|
||||
'title — <bdi>artist</bdi>');
|
||||
});
|
||||
|
||||
test('Update now playing, no artist', function() {
|
||||
var metadata = { title: 'title', artist: '' };
|
||||
widget.updateNowPlaying(metadata);
|
||||
assert.equal(widget.track.textContent, 'title');
|
||||
assert.equal(widget.track.innerHTML, 'title');
|
||||
});
|
||||
|
||||
test('Update now playing, no title', function() {
|
||||
var metadata = { title: '', artist: 'artist' };
|
||||
widget.updateNowPlaying(metadata);
|
||||
assert.equal(widget.track.textContent, 'artist');
|
||||
assert.equal(widget.track.innerHTML, 'artist');
|
||||
});
|
||||
});
|
||||
|
||||
suite('updatePlaybackStatus', function() {
|
||||
test('play', function() {
|
||||
this.sinon.stub(Service, 'query');
|
||||
|
||||
// widget is hidden and no music app.
|
||||
widget.hidden = true;
|
||||
Service.query.returns(false);
|
||||
widget.updatePlaybackStatus({ playStatus: 'PLAYING' });
|
||||
assert.isTrue(widget.hidden);
|
||||
|
||||
// widget is hidden and music app existed.
|
||||
widget.hidden = true;
|
||||
Service.query.returns(true);
|
||||
widget.updatePlaybackStatus({ playStatus: 'PLAYING' });
|
||||
assert.isFalse(widget.hidden);
|
||||
assert.equal(widget.playPauseButton.dataset.icon, 'pause');
|
||||
|
||||
// widget is displayed.
|
||||
widget.hidden = false;
|
||||
widget.updatePlaybackStatus({ playStatus: 'PLAYING' });
|
||||
assert.isFalse(widget.hidden);
|
||||
assert.equal(widget.playPauseButton.dataset.icon, 'pause');
|
||||
});
|
||||
|
||||
test('pause', function() {
|
||||
widget.updatePlaybackStatus({ playStatus: 'PAUSED' });
|
||||
assert.isFalse(widget.hidden);
|
||||
assert.equal(widget.playPauseButton.dataset.icon, 'play');
|
||||
});
|
||||
|
||||
test('stop', function() {
|
||||
widget.updatePlaybackStatus({ playStatus: 'STOPPED' });
|
||||
assert.isTrue(widget.hidden);
|
||||
});
|
||||
|
||||
test('mozinterruptbegin', function() {
|
||||
widget.updatePlaybackStatus({ playStatus: 'mozinterruptbegin' });
|
||||
assert.isTrue(widget.hidden);
|
||||
});
|
||||
});
|
||||
|
||||
suite('openMediaApp', function() {
|
||||
var stubDispatchEvent;
|
||||
|
||||
setup(function() {
|
||||
stubDispatchEvent = this.sinon.stub(window, 'dispatchEvent');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
widget.origin = null;
|
||||
});
|
||||
|
||||
test('Do nothing if no app origin', function() {
|
||||
widget.openMediaApp();
|
||||
assert.isFalse(stubDispatchEvent.called);
|
||||
});
|
||||
|
||||
test('Open media app', function() {
|
||||
widget.origin = 'app://www.fake';
|
||||
widget.openMediaApp();
|
||||
assert.isTrue(stubDispatchEvent.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handleContextmenu', function() {
|
||||
var stubSendCommandEvent;
|
||||
|
||||
setup(function() {
|
||||
stubSendCommandEvent = this.sinon.stub(widget, 'sendCommandEvent');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
widget.isFastSeeking = false;
|
||||
});
|
||||
|
||||
test('handleContextmenu - rewind start', function() {
|
||||
var event = { target: widget.previousButton };
|
||||
widget.handleContextmenu(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('rewindstart'));
|
||||
assert.isTrue(widget.isFastSeeking);
|
||||
});
|
||||
|
||||
test('handleContextmenu - fastforward start', function() {
|
||||
var event = { target: widget.nextButton };
|
||||
widget.handleContextmenu(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('fastforwardstart'));
|
||||
assert.isTrue(widget.isFastSeeking);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handleTouchend', function() {
|
||||
var stubSendCommandEvent;
|
||||
|
||||
setup(function() {
|
||||
stubSendCommandEvent = this.sinon.stub(widget, 'sendCommandEvent');
|
||||
});
|
||||
|
||||
test('handleTouchend - previous track', function() {
|
||||
var event = { target: widget.previousButton };
|
||||
widget.handleTouchend(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('prevtrack'));
|
||||
});
|
||||
|
||||
test('handleTouchend - next track', function() {
|
||||
var event = { target: widget.nextButton };
|
||||
widget.handleTouchend(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('nexttrack'));
|
||||
});
|
||||
|
||||
test('handleTouchend - rewind end', function() {
|
||||
var event = { target: widget.previousButton };
|
||||
widget.isFastSeeking = true;
|
||||
widget.handleTouchend(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('rewindend'));
|
||||
assert.isFalse(widget.isFastSeeking);
|
||||
});
|
||||
|
||||
test('handleTouchend - fastforward end', function() {
|
||||
var event = { target: widget.nextButton };
|
||||
widget.isFastSeeking = true;
|
||||
widget.handleTouchend(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('fastforwardend'));
|
||||
assert.isFalse(widget.isFastSeeking);
|
||||
});
|
||||
|
||||
test('handleTouchend - play pause', function() {
|
||||
var event = { target: widget.playPauseButton };
|
||||
widget.handleTouchend(event);
|
||||
assert.isTrue(stubSendCommandEvent.calledOnce);
|
||||
assert.isTrue(stubSendCommandEvent.calledWith('playpause'));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
/* global LockScreenNotificationBuilder */
|
||||
|
||||
requireApp('system/lockscreen/js/lockscreen_notification_builder.js');
|
||||
|
||||
suite('System > LockScreenNotificationBuilder', function() {
|
||||
var subject;
|
||||
setup(function(){
|
||||
subject = new LockScreenNotificationBuilder(
|
||||
document.createElement('div'));
|
||||
subject.start();
|
||||
});
|
||||
|
||||
test('it can decorate existing notification node with the actionable button',
|
||||
function() {
|
||||
var stubDispatchEvent = this.sinon.stub(window, 'dispatchEvent');
|
||||
var node = document.createElement('div');
|
||||
node.dataset.notificationId = 'foobar';
|
||||
subject.decorate(node);
|
||||
subject.highlight(node);
|
||||
var button = node.querySelector('.button-actionable');
|
||||
assert.notEqual(null, button,
|
||||
'the node comes without the actionable button');
|
||||
var evtTouchStart = new CustomEvent('touchstart');
|
||||
evtTouchStart.touches = [
|
||||
{pageX: 1, pageY: 1}
|
||||
]; // XXX: To avoid throw error from bootstrap.js.
|
||||
|
||||
var evtTouchEnd = new CustomEvent('touchend');
|
||||
evtTouchStart.changedTouches = [
|
||||
{pageX: 1, pageY: 1}
|
||||
]; // XXX: To avoid throw error from bootstrap.js.
|
||||
button.dispatchEvent(evtTouchStart);
|
||||
button.dispatchEvent(evtTouchEnd);
|
||||
assert.isTrue(stubDispatchEvent.calledWithMatch(function(evt) {
|
||||
return 'lockscreen-notification-request-activate' === evt.type &&
|
||||
node.dataset.notificationId === evt.detail.notificationId;
|
||||
}), 'after pressing the button, the events did\'nt fire');
|
||||
});
|
||||
});
|
||||
732
gaia/apps/system/test/unit/lockscreen/lockscreen_notifications_test.js
Executable file
732
gaia/apps/system/test/unit/lockscreen/lockscreen_notifications_test.js
Executable file
|
|
@ -0,0 +1,732 @@
|
|||
/* global MocksHelper, MockLockScreen, LockScreenNotifications */
|
||||
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/lockscreen_notifications.js');
|
||||
requireApp('system/test/unit/mock_lock_screen.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
|
||||
var mocksForLockScreenNotifications = new MocksHelper([
|
||||
'LockScreen', 'LazyLoader'
|
||||
]).init();
|
||||
|
||||
suite('system/LockScreenNotifications', function() {
|
||||
// little helper to build an array filled with numbers from |from| to |to|.
|
||||
// ex: range(1, 10) -> [1,2,3,...9]
|
||||
// useful for looping a specific number of iterations
|
||||
var range = (from, to) => {
|
||||
if (undefined === to) {
|
||||
to = from;
|
||||
from = 0;
|
||||
}
|
||||
|
||||
var rangeGenerator = function *() {
|
||||
for(var i = from; i < to; i++){
|
||||
yield i;
|
||||
}
|
||||
};
|
||||
|
||||
return Array.from(rangeGenerator());
|
||||
};
|
||||
|
||||
var lockScreenNotifications;
|
||||
var stubLockScreenNotificationBuilder;
|
||||
mocksForLockScreenNotifications.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
stubLockScreenNotificationBuilder = function() {
|
||||
this.stop = this.start = function() {};
|
||||
};
|
||||
window.LockScreenNotificationBuilder =
|
||||
stubLockScreenNotificationBuilder;
|
||||
lockScreenNotifications = new LockScreenNotifications();
|
||||
MockLockScreen.init();
|
||||
lockScreenNotifications.start(MockLockScreen,
|
||||
document.createElement('div'));
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockLockScreen.mTeardown();
|
||||
});
|
||||
|
||||
suite('Event binding >', function() {
|
||||
test('Test When locking state changed, the touchstart would be re-bound',
|
||||
function() {
|
||||
var method = LockScreenNotifications.prototype.handleEvent;
|
||||
var mockThis = {
|
||||
updateTimestamps: function() {}
|
||||
};
|
||||
var stubAddEventListener = this.sinon.stub(window, 'addEventListener');
|
||||
var stubRemoveEventListener =
|
||||
this.sinon.stub(window, 'removeEventListener');
|
||||
method.call(mockThis, { type: 'lockscreen-appclosed' });
|
||||
assert.isTrue(stubRemoveEventListener.calledWith('touchstart'));
|
||||
method.call(mockThis, { type: 'lockscreen-appopened' });
|
||||
assert.isTrue(stubAddEventListener.calledWith('touchstart'));
|
||||
});
|
||||
|
||||
test('Test When visibility changed, the touchstart would be re-bound',
|
||||
function() {
|
||||
var method = LockScreenNotifications.prototype.handleEvent;
|
||||
var mockThis = {
|
||||
updateTimestamps: function() {}
|
||||
};
|
||||
var stubAddEventListener = this.sinon.stub(window, 'addEventListener');
|
||||
var stubRemoveEventListener =
|
||||
this.sinon.stub(window, 'removeEventListener');
|
||||
Object.defineProperty(document, 'hidden',
|
||||
{ value: true, writable: true });
|
||||
method.call(mockThis, { type: 'visibilitychange' });
|
||||
assert.isTrue(stubRemoveEventListener.calledWith('touchstart'));
|
||||
Object.defineProperty(document, 'hidden', { value: false });
|
||||
method.call(mockThis, { type: 'visibilitychange' });
|
||||
assert.isTrue(stubAddEventListener.calledWith('touchstart'));
|
||||
});
|
||||
});
|
||||
|
||||
test('Test Show Colored Masked Background', function() {
|
||||
MockLockScreen.maskedBackground = {
|
||||
style: {
|
||||
backgroundColor: null
|
||||
},
|
||||
dataset: {
|
||||
wallpaperColor: 'rgba(123, 123, 123, 0.5)'
|
||||
},
|
||||
classList: {
|
||||
remove: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
lockScreenNotifications.showColoredMaskBG();
|
||||
|
||||
assert.equal(
|
||||
MockLockScreen.maskedBackground.style.backgroundColor,
|
||||
'rgba(123, 123, 123, 0.5)',
|
||||
'BGColor colors mismatch'
|
||||
);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.maskedBackground.classList.remove.calledWith('blank')
|
||||
);
|
||||
});
|
||||
|
||||
test('Test Hide Colored Masked Background', function() {
|
||||
MockLockScreen.maskedBackground = {
|
||||
style: {
|
||||
backgroundColor: null
|
||||
},
|
||||
classList: {
|
||||
add: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
lockScreenNotifications.hideColoredMaskBG();
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.maskedBackground.classList.add.calledWith('blank')
|
||||
);
|
||||
});
|
||||
|
||||
test('Test Hide Colored Masked Background - will NOT add "transparent"',
|
||||
function() {
|
||||
MockLockScreen.maskedBackground = {
|
||||
style: {
|
||||
backgroundColor: null
|
||||
},
|
||||
classList: {
|
||||
add: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications.hideColoredMaskBG();
|
||||
assert.notEqual(
|
||||
MockLockScreen.maskedBackground.style.backgroundColor,
|
||||
'transparent',
|
||||
'BGColor should not be transparent, or when it is with a white' +
|
||||
'background, the words on screen locker will be unreadable.'
|
||||
);
|
||||
});
|
||||
|
||||
test('Test Collapse Notifications', function() {
|
||||
MockLockScreen.notificationsContainer = {
|
||||
classList: {
|
||||
add: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
MockLockScreen.notificationArrow = {
|
||||
classList: {
|
||||
add: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
lockScreenNotifications.collapseNotifications();
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'collapsed'
|
||||
)
|
||||
);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationArrow.classList.add.calledWith(
|
||||
'collapsed'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('Test Expand Notifications', function() {
|
||||
MockLockScreen.notificationsContainer = {
|
||||
classList: {
|
||||
remove: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
MockLockScreen.notificationArrow = {
|
||||
classList: {
|
||||
remove: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
lockScreenNotifications.expandNotifications();
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'collapsed'
|
||||
)
|
||||
);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationArrow.classList.remove.calledWith(
|
||||
'collapsed'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('Test Adjust Container Visual Hints', function() {
|
||||
MockLockScreen.notificationsContainer = {
|
||||
clientHeight: undefined,
|
||||
scrollHeight: undefined,
|
||||
scrollTop: undefined
|
||||
};
|
||||
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
// container can't be scrolled
|
||||
|
||||
var stubSetMaskVisibility =
|
||||
this.sinon.stub(lockScreenNotifications, '_setMaskVisibility');
|
||||
var stubSetArrowVisibility =
|
||||
this.sinon.stub(lockScreenNotifications, '_setArrowVisibility');
|
||||
|
||||
MockLockScreen.notificationsContainer = {
|
||||
clientHeight: 100,
|
||||
scrollHeight: 100,
|
||||
scrollTop: 0
|
||||
};
|
||||
lockScreenNotifications.adjustContainerVisualHints();
|
||||
|
||||
assert.isTrue(stubSetMaskVisibility.calledWith(false, false));
|
||||
assert.isTrue(stubSetArrowVisibility.calledWith(false));
|
||||
|
||||
// container can be scrolled, currently at top
|
||||
stubSetMaskVisibility.reset();
|
||||
stubSetArrowVisibility.reset();
|
||||
|
||||
MockLockScreen.notificationsContainer = {
|
||||
clientHeight: 100,
|
||||
scrollHeight: 150,
|
||||
scrollTop: 0
|
||||
};
|
||||
lockScreenNotifications.adjustContainerVisualHints();
|
||||
|
||||
assert.isTrue(stubSetMaskVisibility.calledWith(false, false));
|
||||
assert.isTrue(stubSetArrowVisibility.calledWith(true));
|
||||
|
||||
// container can be scrolled, currently at middle
|
||||
stubSetMaskVisibility.reset();
|
||||
stubSetArrowVisibility.reset();
|
||||
|
||||
MockLockScreen.notificationsContainer = {
|
||||
clientHeight: 100,
|
||||
scrollHeight: 150,
|
||||
scrollTop: 20
|
||||
};
|
||||
lockScreenNotifications.adjustContainerVisualHints();
|
||||
|
||||
assert.isTrue(stubSetMaskVisibility.calledWith(false, true));
|
||||
assert.isTrue(stubSetArrowVisibility.calledWith(false));
|
||||
|
||||
// container can be scrolled, currently at bottom
|
||||
stubSetMaskVisibility.reset();
|
||||
stubSetArrowVisibility.reset();
|
||||
|
||||
MockLockScreen.notificationsContainer = {
|
||||
clientHeight: 100,
|
||||
scrollHeight: 150,
|
||||
scrollTop: 50
|
||||
};
|
||||
lockScreenNotifications.adjustContainerVisualHints();
|
||||
|
||||
assert.isTrue(stubSetMaskVisibility.calledWith(true, false));
|
||||
assert.isTrue(stubSetArrowVisibility.calledWith(false));
|
||||
});
|
||||
|
||||
suite('Test Set Mask Visibility', function() {
|
||||
setup(function() {
|
||||
MockLockScreen.notificationsContainer = {
|
||||
classList: {
|
||||
add: this.sinon.spy(),
|
||||
remove: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockLockScreen.notificationsContainer = undefined;
|
||||
});
|
||||
|
||||
test('top = true, both = false', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setMaskVisibility(true, false);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('top = false, both = true', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setMaskVisibility(false, true);
|
||||
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('top = true, both = true', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setMaskVisibility(true, true);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test('top = false, both = false', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setMaskVisibility(false, false);
|
||||
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-top'
|
||||
)
|
||||
);
|
||||
assert.isFalse(
|
||||
MockLockScreen.notificationsContainer.classList.add.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationsContainer.classList.remove.calledWith(
|
||||
'masked-both'
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Test Set Arrow Visibility', function() {
|
||||
setup(function() {
|
||||
MockLockScreen.notificationArrow = {
|
||||
classList: {
|
||||
add: this.sinon.spy(),
|
||||
remove: this.sinon.spy()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockLockScreen.notificationArrow = undefined;
|
||||
});
|
||||
|
||||
test('visible = true', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setArrowVisibility(true);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationArrow.classList.add.calledWith(
|
||||
'visible'
|
||||
)
|
||||
);
|
||||
assert.isFalse(MockLockScreen.notificationArrow.classList.remove.called);
|
||||
});
|
||||
|
||||
test('visible = false', function() {
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
lockScreenNotifications._setArrowVisibility(false);
|
||||
|
||||
assert.isTrue(
|
||||
MockLockScreen.notificationArrow.classList.remove.calledWith(
|
||||
'visible'
|
||||
)
|
||||
);
|
||||
assert.isFalse(MockLockScreen.notificationArrow.classList.add.called);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Test ScrollToTop', function() {
|
||||
MockLockScreen.notificationsContainer = {
|
||||
scrollTop: 1000
|
||||
};
|
||||
|
||||
lockScreenNotifications._lockScreen = MockLockScreen;
|
||||
|
||||
lockScreenNotifications.scrollToTop();
|
||||
|
||||
assert.equal(
|
||||
MockLockScreen.notificationsContainer.scrollTop,
|
||||
0,
|
||||
'Container was not scrolled to scrollTop = 0'
|
||||
);
|
||||
});
|
||||
|
||||
suite('_getNumNotificationInContainerViewport', function() {
|
||||
var stubGetWindowInnerDimension;
|
||||
|
||||
setup(function(){
|
||||
stubGetWindowInnerDimension =
|
||||
this.sinon.stub(lockScreenNotifications, '_getWindowInnerDimension');
|
||||
});
|
||||
|
||||
test('innerHeight > 480, without music player (4 notifications)',
|
||||
function() {
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 640
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications._getNumNotificationInContainerViewport(),
|
||||
4
|
||||
);
|
||||
});
|
||||
|
||||
test('innerHeight > 480, with music player (3 notifications)',
|
||||
function() {
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 640
|
||||
});
|
||||
lockScreenNotifications.container.classList.add('collapsed');
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications._getNumNotificationInContainerViewport(),
|
||||
3
|
||||
);
|
||||
});
|
||||
|
||||
test('innerHeight <= 480, without music player (3 notifications)',
|
||||
function() {
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 360
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications._getNumNotificationInContainerViewport(),
|
||||
3
|
||||
);
|
||||
});
|
||||
|
||||
test('innerHeight <= 480, with music player (2 notifications)',
|
||||
function() {
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 360
|
||||
});
|
||||
lockScreenNotifications.container.classList.add('collapsed');
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications._getNumNotificationInContainerViewport(),
|
||||
2
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('Remove top mask when actionable notification is at the visual top of' +
|
||||
'the container viewport', function(){
|
||||
// for test's sake, add 4 notifications in the notifications container, and
|
||||
// suppose 1-indexed one is the visually top notification in the viewport
|
||||
// (i.e 3 notifications are visible in the viewport)
|
||||
|
||||
const numTestNotifications = 4;
|
||||
const numNotificationInViewport = 3;
|
||||
|
||||
var stubGetNumNotificationInContainerViewport;
|
||||
|
||||
suiteSetup(function(){
|
||||
stubGetNumNotificationInContainerViewport =
|
||||
sinon.stub(lockScreenNotifications,
|
||||
'_getNumNotificationInContainerViewport')
|
||||
.returns(numNotificationInViewport);
|
||||
});
|
||||
|
||||
setup(function(){
|
||||
range(numTestNotifications).forEach(index => {
|
||||
var node = document.createElement('div');
|
||||
node.classList.add('notification');
|
||||
node.classList.add('actionable');
|
||||
node.dataset.notificationId = 'id-' + index;
|
||||
lockScreenNotifications.container.appendChild(node);
|
||||
});
|
||||
});
|
||||
|
||||
suite('top-actionable class is correctly added in ' +
|
||||
'onNotificationHighlighted',
|
||||
function() {
|
||||
|
||||
// We are testing them in HVGA suite.
|
||||
setup(function() {
|
||||
var stubGetWindowInnerDimension =
|
||||
this.sinon.stub(lockScreenNotifications, '_getWindowInnerDimension');
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 480
|
||||
});
|
||||
});
|
||||
|
||||
// we then see if the class is added correctly, or not added,
|
||||
// when we try to highlight each notification.
|
||||
|
||||
var testTopActionableClass = function(notificationIndex, expected) {
|
||||
var id = 'id-' + notificationIndex;
|
||||
|
||||
lockScreenNotifications.onNotificationHighlighted(id, 123000);
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications.container.classList.contains(
|
||||
'top-actionable'
|
||||
),
|
||||
expected,
|
||||
'with notification index ' + notificationIndex + ' container should' +
|
||||
(!expected ? ' not' : '') + ' have the top-actionable class'
|
||||
);
|
||||
};
|
||||
|
||||
range(numTestNotifications).forEach(index => {
|
||||
// the notification is the visually top in the viewport if its rev-index
|
||||
// is the number of notifications visible in the viewport; its (normal)
|
||||
// index would be (numNotifications - numVisibleNotifications);
|
||||
// we expect that notification, when highlighted, to trigger
|
||||
// |top-actionable| class to be added.
|
||||
test('test on ' + index + '-th notification', () => {
|
||||
testTopActionableClass(
|
||||
index,
|
||||
(numTestNotifications - numNotificationInViewport) === index
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('_tryAddTopMaskByNotification is correctly called in ' +
|
||||
'onCleanHighlighted and onNotificationsBlur',
|
||||
function() {
|
||||
const testId = 'id-1';
|
||||
var stubTryAddTopMaskByNotification;
|
||||
var stubRemoveNotificationHighlight;
|
||||
var targetNotificationNode;
|
||||
|
||||
setup(function(){
|
||||
|
||||
// We are testing them in HVGA suite.
|
||||
var stubGetWindowInnerDimension =
|
||||
this.sinon.stub(lockScreenNotifications, '_getWindowInnerDimension');
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 480
|
||||
});
|
||||
|
||||
|
||||
stubTryAddTopMaskByNotification =
|
||||
this.sinon.stub(lockScreenNotifications,
|
||||
'_tryAddTopMaskByNotification');
|
||||
|
||||
stubRemoveNotificationHighlight =
|
||||
this.sinon.stub(lockScreenNotifications,
|
||||
'removeNotificationHighlight');
|
||||
|
||||
targetNotificationNode =
|
||||
lockScreenNotifications.container.querySelector(
|
||||
'[data-notification-id="' + testId + '"]'
|
||||
);
|
||||
|
||||
lockScreenNotifications.states.currentHighlighted =
|
||||
targetNotificationNode;
|
||||
|
||||
lockScreenNotifications.states.currentHighlightedId = testId;
|
||||
|
||||
lockScreenNotifications.states.highlightedNotifications[testId] =
|
||||
123000;
|
||||
});
|
||||
|
||||
test('onCleanHighlighted', function (){
|
||||
var timestamp =
|
||||
123000 + lockScreenNotifications.configs.timeoutHighlighted + 1;
|
||||
|
||||
lockScreenNotifications.onCleanHighlighted(testId, timestamp);
|
||||
|
||||
assert.isTrue(
|
||||
stubTryAddTopMaskByNotification.calledWith(targetNotificationNode)
|
||||
);
|
||||
});
|
||||
|
||||
test('onNotificationsBlur', function (){
|
||||
lockScreenNotifications.onNotificationsBlur();
|
||||
|
||||
assert.isTrue(
|
||||
stubTryAddTopMaskByNotification.calledWith(targetNotificationNode)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('In HVGA mode with music player on thr screen, the highlighting ' +
|
||||
'request should be permitted when there is a new node',
|
||||
function() {
|
||||
this.sinon.stub(lockScreenNotifications, '_getWindowInnerDimension',
|
||||
function() {
|
||||
return { height: 480, width: 320 };
|
||||
});
|
||||
var stubContainer = {
|
||||
getBoundingClientRect () {
|
||||
return { top: 193, bottom: 251};
|
||||
}
|
||||
};
|
||||
var stubNode = {
|
||||
getBoundingClientRect () {
|
||||
return { top: 203, bottom: 252 };
|
||||
}
|
||||
};
|
||||
var originalContainer = lockScreenNotifications.container;
|
||||
lockScreenNotifications.container = stubContainer;
|
||||
assert.isFalse(lockScreenNotifications
|
||||
.notificationOutOfViewport(stubNode),
|
||||
'The request is denied.');
|
||||
lockScreenNotifications.container = originalContainer;
|
||||
});
|
||||
|
||||
suite('top-actionable class is correctly removed with ' +
|
||||
'tryAddTopMaskByNotification',
|
||||
function() {
|
||||
// we then see if the class is removed correctly, or not removed,
|
||||
// when we try to un-highlight each notification.
|
||||
|
||||
setup(function(){
|
||||
// We are testing them in HVGA suite.
|
||||
var stubGetWindowInnerDimension =
|
||||
this.sinon.stub(lockScreenNotifications, '_getWindowInnerDimension');
|
||||
stubGetWindowInnerDimension.returns({
|
||||
width: window.innderWidth,
|
||||
height: 480
|
||||
});
|
||||
|
||||
lockScreenNotifications.container.classList.add('top-actionable');
|
||||
});
|
||||
|
||||
var testTopActionableClass = function(notificationIndex, expected) {
|
||||
var id = 'id-' + notificationIndex;
|
||||
|
||||
var notificationNode =
|
||||
lockScreenNotifications.container.querySelector(
|
||||
'[data-notification-id="' + id + '"]'
|
||||
);
|
||||
|
||||
lockScreenNotifications._tryAddTopMaskByNotification(notificationNode);
|
||||
|
||||
assert.equal(
|
||||
lockScreenNotifications.container.classList.contains(
|
||||
'top-actionable'
|
||||
),
|
||||
expected,
|
||||
'with notification index ' + notificationIndex + ' container should' +
|
||||
(!expected ? ' not' : '') + ' have the top-actionable class'
|
||||
);
|
||||
};
|
||||
|
||||
range(numTestNotifications).forEach(index => {
|
||||
// the notification is the visually top in the viewport if its rev-index
|
||||
// is the number of notifications visible in the viewport; its (normal)
|
||||
// index would be (numNotifications - numVisibleNotifications);
|
||||
// we expect that notification, when unhighlighted, to trigger
|
||||
// |top-actionable| class to be removed.
|
||||
test('test on ' + index + '-th notification', () => {
|
||||
testTopActionableClass(
|
||||
index,
|
||||
(numTestNotifications - numNotificationInViewport) !== index
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
42
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadhiding_test.js
Executable file
42
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadhiding_test.js
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
/* global LockScreenStateKeypadHiding */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_keypadhiding.js');
|
||||
suite('sytem/LockScreenStateKeypadHiding', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
overlay: document.createElement('div'),
|
||||
passcodePad: document.createElement('div'),
|
||||
passcodeCode: document.createElement('div')
|
||||
};
|
||||
subject = (new LockScreenStateKeypadHiding()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function(done) {
|
||||
this.sinon.stub(window, 'addEventListener', function(type, cb) {
|
||||
// When it add the listener, execcute the cb immediately,
|
||||
// since we can't control a closured function which
|
||||
// is hidden inside the promise resolver.
|
||||
cb({
|
||||
target: {
|
||||
classList: {
|
||||
contains: this.sinon.stub().returns(true)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
subject.transferTo().then(() => {
|
||||
assert.isFalse(mockLockScreen.overlay.classList.contains('no-transition'),
|
||||
'it would NOT still play transition even though we need it');
|
||||
assert.equal(mockLockScreen.overlay.dataset.panel, 'main',
|
||||
'it doesn\'t change the panel value to trigger the animation');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
27
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadrising_test.js
Executable file
27
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadrising_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
/* global LockScreenStateKeypadRising */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_keypadrising.js');
|
||||
suite('sytem/LockScreenStateKeypadRising', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
overlay: document.createElement('div')
|
||||
};
|
||||
subject = (new LockScreenStateKeypadRising()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function(done) {
|
||||
subject.transferTo().then(function() {
|
||||
assert.isFalse(mockLockScreen.overlay.classList.contains('no-transition'),
|
||||
'it would NOT still play transition even though we need it');
|
||||
assert.equal(mockLockScreen.overlay.dataset.panel, 'passcode',
|
||||
'it doesn\'t change the panel value to trigger the animation');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
24
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadshow_test.js
Executable file
24
gaia/apps/system/test/unit/lockscreen/lockscreen_state_keypadshow_test.js
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
/* global LockScreenStateKeypadShow */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_keypadshow.js');
|
||||
suite('sytem/LockScreenStateKeypadShow', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
overlay: document.createElement('div')
|
||||
};
|
||||
subject = (new LockScreenStateKeypadShow()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function() {
|
||||
subject.transferTo();
|
||||
assert.isTrue(mockLockScreen.overlay.classList.contains('no-transition'),
|
||||
'it would still play transition even though we needn\'t it');
|
||||
assert.equal(mockLockScreen.overlay.dataset.panel, 'passcode',
|
||||
'it doesn\'t change the panel value to the correct panel');
|
||||
});
|
||||
});
|
||||
51
gaia/apps/system/test/unit/lockscreen/lockscreen_state_logger_test.js
Executable file
51
gaia/apps/system/test/unit/lockscreen/lockscreen_state_logger_test.js
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
/* global LockScreenStateLogger */
|
||||
|
||||
'use strict';
|
||||
requireApp('system/lockscreen/js/lockscreen_state_logger.js');
|
||||
suite('system/LockScreenStateLogger', function() {
|
||||
var subject;
|
||||
setup(function() {
|
||||
subject = new LockScreenStateLogger();
|
||||
});
|
||||
|
||||
test('it should log everything while the verbose flag is true',
|
||||
function() {
|
||||
var stubLog = this.sinon.stub(subject, 'log');
|
||||
subject.start({
|
||||
verbose: true
|
||||
});
|
||||
subject.error('foobar');
|
||||
assert.isTrue(stubLog.calledOnce,
|
||||
'when verbose is true the error function doesn\'t log things');
|
||||
subject.warning('foobar');
|
||||
assert.isTrue(stubLog.calledTwice,
|
||||
'when verbose is true the warning function doesn\'t log things');
|
||||
subject.verbose('foobar');
|
||||
assert.isTrue(stubLog.calledThrice,
|
||||
'when verbose is true the verbose function doesn\'t log things');
|
||||
});
|
||||
|
||||
test('when it comes with the graph flag, it should log transferring',
|
||||
function() {
|
||||
this.sinon.stub(console, 'log'); // to prevent it really print the message
|
||||
subject.start({
|
||||
debug: true,
|
||||
graph: true
|
||||
});
|
||||
var stubPush = this.sinon.stub(subject.stateStack, 'push');
|
||||
subject.transfer('from', 'to', {});
|
||||
assert.isTrue(stubPush.called,
|
||||
'when the graph flag is true, it push no state into stack');
|
||||
});
|
||||
|
||||
test('when it comes without the graph flag, it should not log transferring',
|
||||
function() {
|
||||
subject.start({
|
||||
graph: false
|
||||
});
|
||||
var stubPush = this.sinon.stub(subject.stateStack, 'push');
|
||||
subject.transfer('from', 'to', {});
|
||||
assert.isFalse(stubPush.called,
|
||||
'when the graph flag is false, it still push the state into stack');
|
||||
});
|
||||
});
|
||||
952
gaia/apps/system/test/unit/lockscreen/lockscreen_state_manager_test.js
Executable file
952
gaia/apps/system/test/unit/lockscreen/lockscreen_state_manager_test.js
Executable file
|
|
@ -0,0 +1,952 @@
|
|||
/* global LockScreenStateManager, Promise */
|
||||
|
||||
'use strict';
|
||||
requireApp('system/shared/test/unit/mocks/mock_service.js');
|
||||
requireApp('system/lockscreen/js/lockscreen_state_manager.js');
|
||||
|
||||
var mocksHelper = new window.MocksHelper([
|
||||
'Service'
|
||||
]).init();
|
||||
|
||||
suite('system/LockScreenStateManager', function() {
|
||||
var subject;
|
||||
var mockState, mockLockScreen;
|
||||
var mockMozSettings, mockSettingLock;
|
||||
mocksHelper.attachTestHelpers();
|
||||
setup(function() {
|
||||
var Deferred = function() {
|
||||
this.promise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
mockSettingLock = function() {
|
||||
this.then = (cb) => {
|
||||
this.deferred = new Deferred();
|
||||
this.deferred.promise = this.deferred.promise.then((r) => {
|
||||
return cb(r);
|
||||
});
|
||||
return this.deferred.promise;
|
||||
};
|
||||
};
|
||||
|
||||
mockMozSettings = function() {
|
||||
this._deferredReadings = {};
|
||||
this._deferredObservers = {};
|
||||
this.__onsuccesses = {};
|
||||
this.createLock = () => {
|
||||
return (function() {
|
||||
this.get = (key) => {
|
||||
var lock = new mockSettingLock();
|
||||
navigator.mozSettings._deferredReadings[key] = new Deferred();
|
||||
navigator.mozSettings._deferredReadings[key].promise =
|
||||
navigator.mozSettings._deferredReadings[key].promise
|
||||
.then((fetchingResult) => {
|
||||
lock.result = {};
|
||||
lock.result[key] = fetchingResult;
|
||||
if (lock.onsuccess) {
|
||||
lock.onsuccess();
|
||||
} else if (lock.deferred) {
|
||||
lock.deferred.resolve(fetchingResult);
|
||||
}
|
||||
})
|
||||
.catch(console.error.bind(console));
|
||||
return lock;
|
||||
};
|
||||
return this;
|
||||
}).call(this);
|
||||
};
|
||||
|
||||
this.addObserver = (key, cb) => {
|
||||
if (!this._deferredObservers[key]) {
|
||||
this._deferredObservers[key] = new Deferred();
|
||||
}
|
||||
this._deferredObservers[key].promise =
|
||||
this._deferredObservers[key].promise.then((result) => {
|
||||
cb(result);
|
||||
})
|
||||
.catch(console.error.bind(console));
|
||||
};
|
||||
this._reset = function() {
|
||||
this._deferredReadings = {};
|
||||
this._deferredObservers = {};
|
||||
};
|
||||
return this;
|
||||
};
|
||||
|
||||
mockState = function() {
|
||||
this.start = () => {
|
||||
return this; };
|
||||
this.transferTo = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
this.transferOut = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
};
|
||||
var genMock = function(type) {
|
||||
return function() {
|
||||
var neo = new mockState();
|
||||
neo.type = type;
|
||||
return neo;
|
||||
};
|
||||
};
|
||||
|
||||
window.LockScreenBaseState = genMock('base');
|
||||
window.LockScreenStateSlideShow = genMock('slideShow');
|
||||
window.LockScreenStateUnlock = genMock('slideUnlock');
|
||||
window.LockScreenStateSlideRestore = genMock('slideRestore');
|
||||
window.LockScreenStatePanelHide = genMock('panelHide');
|
||||
window.LockScreenStateKeypadShow = genMock('keypadShow');
|
||||
window.LockScreenStateKeypadHiding = genMock('keypadHiding');
|
||||
window.LockScreenStateKeypadRising = genMock('keypadRising');
|
||||
window.LockScreenStateSecureAppLaunching = genMock('secureAppLaunching');
|
||||
|
||||
window.LockScreenStateLogger = function() {
|
||||
this.configs = {};
|
||||
this.start =
|
||||
this.stop =
|
||||
this.stack =
|
||||
this.debug =
|
||||
this.error =
|
||||
this.warning =
|
||||
this.verbose =
|
||||
this.transfer = function() {
|
||||
return this;
|
||||
};
|
||||
};
|
||||
|
||||
mockLockScreen = function() {
|
||||
this.init = function() {};
|
||||
this.overlay = document.createElement('div');
|
||||
this.checkPassCodeTimeout = function() {};
|
||||
};
|
||||
navigator.mozSettings = new mockMozSettings();
|
||||
subject = (new LockScreenStateManager())
|
||||
.start(new mockLockScreen());
|
||||
navigator.mozSettings._reset();
|
||||
this.sinon.stub(subject, 'resolveInnerStates', function() {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
suite('"integration tests"', function() {
|
||||
var instance;
|
||||
setup(function() {
|
||||
navigator.mozSettings = new mockMozSettings();
|
||||
instance = new LockScreenStateManager();
|
||||
});
|
||||
teardown(function() {
|
||||
navigator.mozSettings._reset();
|
||||
});
|
||||
test('if we turn on the debug setting, it will create '+
|
||||
'a logger with the option enabled', function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
navigator.mozSettings._deferredReadings['debug.gaia.enabled'].promise =
|
||||
navigator.mozSettings
|
||||
._deferredReadings['debug.gaia.enabled'].promise.then(() => {
|
||||
assert.isTrue(instance.logger.configs.debug,
|
||||
'it doesn\'t enable the logger');
|
||||
done();
|
||||
}).catch(done);
|
||||
|
||||
navigator.mozSettings
|
||||
._deferredReadings['debug.gaia.enabled']
|
||||
.resolve({'debug.gaia.enabled': true});
|
||||
});
|
||||
|
||||
test('if we turn off the debug setting, it will create '+
|
||||
'a logger with the option enabled', function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
instance.logger.configs.debug = true;
|
||||
var observerPromise = navigator.mozSettings
|
||||
._deferredObservers['debug.gaia.enabled'].promise;
|
||||
navigator.mozSettings._deferredObservers['debug.gaia.enabled']
|
||||
.promise = observerPromise.then(() => {
|
||||
assert.isFalse(instance.logger.configs.debug,
|
||||
'it doesn\'t disable the logger');
|
||||
done();
|
||||
}).catch(done);
|
||||
|
||||
navigator.mozSettings._deferredObservers['debug.gaia.enabled']
|
||||
.resolve({ 'settingValue': false});
|
||||
});
|
||||
|
||||
test('if passcode setting is still reading, do not transfer ' +
|
||||
'until it is resolved', function(done) {
|
||||
var originalDoTransfer = instance.doTransfer;
|
||||
var stubDoTransfer = this.sinon.stub(instance, 'doTransfer', function() {
|
||||
originalDoTransfer.apply(instance, arguments);
|
||||
});
|
||||
instance.start(new mockLockScreen());
|
||||
var stubTransferToKeypadRising = this.sinon.stub(
|
||||
instance.states.keypadRising, 'transferTo')
|
||||
.returns(Promise.resolve());
|
||||
instance.handleEvent({type: 'lockscreenslide-activate-right'});
|
||||
|
||||
sinon.assert.notCalled(stubDoTransfer,
|
||||
'it shouldn\'t transfer to next state before deferred requests resolved,'+
|
||||
'although the event comes');
|
||||
|
||||
instance.promiseQueue = instance.promiseQueue.then(() => {
|
||||
sinon.assert.called(stubTransferToKeypadRising,
|
||||
'it didn\'t transfer to keypadRising after the event and settings');
|
||||
done();
|
||||
}).catch(done);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.enabled']
|
||||
.resolve(true);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.timeout']
|
||||
.resolve(0);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.unlock-sound.enabled']
|
||||
.resolve(false);
|
||||
});
|
||||
|
||||
test('|holdcamera| will trigger transferring to unlock when there ' +
|
||||
'is no passcode',
|
||||
function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
var stubTransferToUnlock = this.sinon.stub(
|
||||
instance.states.unlock, 'transferTo')
|
||||
.returns(Promise.resolve());
|
||||
instance.handleEvent({type: 'holdcamera'});
|
||||
instance.promiseQueue = instance.promiseQueue.then(() => {
|
||||
sinon.assert.called(stubTransferToUnlock,
|
||||
'it didn\'t transfer to unlock after the event comes');
|
||||
done();
|
||||
}).catch(done);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.enabled']
|
||||
.resolve(false);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.timeout']
|
||||
.resolve(0);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.unlock-sound.enabled']
|
||||
.resolve(false);
|
||||
});
|
||||
|
||||
test('|holdcamera| will trigger transferring to secureAppLaunching ' +
|
||||
'when there is no passcode',
|
||||
function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
var stubTransferToSecureAppLaunching= this.sinon.stub(
|
||||
instance.states.secureAppLaunching, 'transferTo')
|
||||
.returns(Promise.resolve());
|
||||
instance.handleEvent({type: 'holdcamera'});
|
||||
instance.promiseQueue = instance.promiseQueue.then(() => {
|
||||
sinon.assert.called(stubTransferToSecureAppLaunching,
|
||||
'it didn\'t transfer to secureAppLaunching after the event comes');
|
||||
done();
|
||||
}).catch(done);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.enabled']
|
||||
.resolve(true);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.timeout']
|
||||
.resolve(0);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.unlock-sound.enabled']
|
||||
.resolve(false);
|
||||
});
|
||||
|
||||
test('|notification-request-activate-unlock| will trigger ' +
|
||||
'transferring to unlock when there is no passcode',
|
||||
function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
var stubTransferToUnlock = this.sinon.stub(
|
||||
instance.states.unlock, 'transferTo')
|
||||
.returns(Promise.resolve());
|
||||
instance.handleEvent(
|
||||
{type: 'lockscreen-notification-request-activate-unlock'});
|
||||
instance.promiseQueue = instance.promiseQueue.then(() => {
|
||||
sinon.assert.called(stubTransferToUnlock,
|
||||
'it didn\'t transfer to unlock after the event comes');
|
||||
done();
|
||||
}).catch(done);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.enabled']
|
||||
.resolve(false);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.timeout']
|
||||
.resolve(0);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.unlock-sound.enabled']
|
||||
.resolve(false);
|
||||
});
|
||||
|
||||
test('|lockscreen-notification-request-activate-unlock| will trigger' +
|
||||
'transferring to keypadRising when there is no passcode',
|
||||
function(done) {
|
||||
instance.start(new mockLockScreen());
|
||||
var stubTransferToKeypadRising = this.sinon.stub(
|
||||
instance.states.keypadRising, 'transferTo')
|
||||
.returns(Promise.resolve());
|
||||
instance.handleEvent(
|
||||
{type: 'lockscreen-notification-request-activate-unlock'});
|
||||
instance.promiseQueue = instance.promiseQueue.then(() => {
|
||||
sinon.assert.called(stubTransferToKeypadRising,
|
||||
'it didn\'t transfer to keypadRising after the event comes');
|
||||
done();
|
||||
}).catch(done);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.enabled']
|
||||
.resolve(true);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.passcode-lock.timeout']
|
||||
.resolve(0);
|
||||
navigator.mozSettings
|
||||
._deferredReadings['lockscreen.unlock-sound.enabled']
|
||||
.resolve(false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('self-test all methods: ', function() {
|
||||
test('|getSettingsObserverCallback| can handle delay request well',
|
||||
function(done) {
|
||||
var method = LockScreenStateManager.prototype.getSettingsObserverCallback;
|
||||
var mockThis = {
|
||||
lockScreenStates: {
|
||||
passcodeEnabled: new LockScreenStateManager.Deferred()
|
||||
}
|
||||
};
|
||||
mockThis.lockScreenStates.passcodeEnabled.promise.then((val) => {
|
||||
assert.equal(false, val,
|
||||
'The delay request can\'t get the reading value');
|
||||
done();
|
||||
}).catch(done);
|
||||
method.call(mockThis, 'passcodeEnabled')({
|
||||
settingValue: false
|
||||
});
|
||||
assert.isFalse(mockThis.lockScreenStates.passcodeEnabled instanceof
|
||||
LockScreenStateManager.Deferred,
|
||||
'it doesn\'t replace the delay request with the read value');
|
||||
assert.equal(false, mockThis.lockScreenStates.passcodeEnabled,
|
||||
'it doesn\'t replace the delay request with the read value');
|
||||
});
|
||||
|
||||
test('|resolveInnerStates| would wait all states with promises',
|
||||
function(done) {
|
||||
var method = LockScreenStateManager.prototype.resolveInnerStates;
|
||||
var Deferred = LockScreenStateManager.Deferred;
|
||||
var mockInnerStates = {
|
||||
'waiting1': new Deferred(),
|
||||
'waiting2': new Deferred()
|
||||
};
|
||||
method.call({}, mockInnerStates).then(() => {
|
||||
assert.equal(true, mockInnerStates.waiting1,
|
||||
'The method doesn\'t replace the deferred request #1 with the value');
|
||||
assert.equal(false, mockInnerStates.waiting2,
|
||||
'The method doesn\'t replace the deferred request #2 with the value');
|
||||
done();
|
||||
}).catch(done);
|
||||
assert.notEqual(true, mockInnerStates.waiting1,
|
||||
'The method doesn\'t wait the deferred request #1');
|
||||
assert.notEqual(false, mockInnerStates.waiting2,
|
||||
'The method doesn\'t wait the deferred request #1');
|
||||
mockInnerStates.waiting1.resolve(true);
|
||||
mockInnerStates.waiting2.resolve(false);
|
||||
});
|
||||
|
||||
test('|transfer| would wait to resolve all deferred requests',
|
||||
function(done) {
|
||||
var method = LockScreenStateManager.prototype.transfer;
|
||||
var mockThis = {
|
||||
resolveInnerStates: this.sinon.stub().returns(Promise.resolve()),
|
||||
doTransfer: () => {}
|
||||
};
|
||||
method.call(mockThis).then(() => {
|
||||
assert.isTrue(mockThis.resolveInnerStates.called,
|
||||
'It doesn\'t wait to resolve deferred requests before transferring');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
test('|transfer| would call transferTo and transferOut of the state',
|
||||
function(done) {
|
||||
var stubTransferOut = this.sinon.stub().returns(Promise.resolve());
|
||||
// This would be the next step of 'transferOut'.
|
||||
var stubTransferTo = this.sinon.stub().returns(Promise.resolve());
|
||||
subject.rules = new Map();
|
||||
subject.previousState = {
|
||||
transferOut: stubTransferOut,
|
||||
transferTo: stubTransferTo,
|
||||
type: 'foo'
|
||||
};
|
||||
subject.rules.set({}, {
|
||||
transferOut: stubTransferOut,
|
||||
transferTo: stubTransferTo,
|
||||
type: 'bar'
|
||||
});
|
||||
|
||||
this.sinon.stub(subject, 'matchAcceptableState', function() {
|
||||
return true;
|
||||
});
|
||||
this.sinon.stub(subject, 'matchStates', function() {
|
||||
return true;
|
||||
});
|
||||
subject.transfer({}).then(() => {
|
||||
sinon.assert.called(stubTransferTo,
|
||||
'the |transferTo| method isn\'t called');
|
||||
sinon.assert.called(stubTransferOut,
|
||||
'the |transferOut| method isn\'t called');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('transfer while states matched: ', function() {
|
||||
test('After secure app launched, it would restore the slider',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideRestore, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadShow to slideRestore');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
secureAppOpen: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('With passcode enabled, when it activate to unlock, ' +
|
||||
'show the passcode pad with animation',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.keypadRising, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from slideShow to keypadRising');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
passcodeTimeoutExpired: true,
|
||||
screenOn: true,
|
||||
activateUnlock: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('With passcode disabled, when it activate to unlock, ' +
|
||||
'unlock directly',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.unlock, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from slideShow to unlock');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: false,
|
||||
screenOn: true,
|
||||
activateUnlock: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('With passcode enabled but not expired, when it activate to unlock, ' +
|
||||
'unlock directly',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.unlock, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from slideShow to unlock');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
passcodeTimeoutExpired: false,
|
||||
screenOn: true,
|
||||
activateUnlock: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('Resume from screen off (from panelHide)',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideShow, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from panelHide to slideShow');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
screenOn: true,
|
||||
unlocking: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'panelHide'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('Resume from screen off (from panelHide)',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideShow, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from panelHide to slideShow');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
screenOn: true,
|
||||
unlocking: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'panelHide'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When press homekey, show the slide with animation.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.keypadHiding, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadShow to keypadHiding');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
screenOn: true,
|
||||
homePressed: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('After the animation, it should show the slide to response' +
|
||||
'to the homekey pressing.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideShow, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadHiding to slideShow');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
screenOn: true,
|
||||
inputpad: 'close',
|
||||
unlocking: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadHiding'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When the screen is off, the slide should show as cache.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideShow, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadHiding to slideShow');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
screenOn: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadHiding'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When the animation done, show no panel for unlocking.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.panelHide, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadHiding to panelHide');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
screenOn: true,
|
||||
inputpad: 'close',
|
||||
unlocking: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadHiding'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When passcode validated, transfer to keypadHide',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.keypadHiding, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadShow to keypadHiding');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
passcodeEnabled: true,
|
||||
passcodeValidated: true,
|
||||
screenOn: true,
|
||||
inputpad: 'close',
|
||||
unlocking: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When user clean key code, hide the pad.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.keypadHiding, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from keypadShow to keypadHiding');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
keypadInput: 'c'
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'keypadShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When screenchanged, the unlocking value should be false.',
|
||||
function() {
|
||||
subject.handleEvent({
|
||||
type: 'screenchange', detail: {
|
||||
screenEnabled: false
|
||||
}
|
||||
});
|
||||
this.sinon.stub(subject, 'transfer');
|
||||
assert.isFalse(subject.lockScreenStates.unlocking,
|
||||
'the screenchange event doesn\'t restore the unlocking state');
|
||||
});
|
||||
|
||||
test('When user invoke secure app, move to the mode.',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.secureAppLaunching, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from ' +
|
||||
'slideShow to secureAppLaunching');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
unlockingAppActivated: true,
|
||||
passcodeEnabled: true,
|
||||
passcodeTimeoutExpired: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When user unlocks to app with an expired timeout, unlock it',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.unlock, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from ' +
|
||||
'slideShow to unlock');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
unlockingAppActivated: true,
|
||||
passcodeEnabled: true,
|
||||
passcodeTimeoutExpired: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When secure app is closing, restore the slide',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.slideRestore, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from ' +
|
||||
'secureAppLaunching to slidRestore');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
secureAppClose: true
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'secureAppLaunching'
|
||||
};
|
||||
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When secure app terminated, it would map to the state change',
|
||||
function() {
|
||||
var stubOnSecureAppClosing =
|
||||
this.sinon.stub(subject, 'onSecureAppClosing');
|
||||
subject.handleEvent(new CustomEvent(
|
||||
'secure-appterminated'
|
||||
));
|
||||
assert.isTrue(stubOnSecureAppClosing.called);
|
||||
});
|
||||
|
||||
test('When secure app closing, it would map to the state change',
|
||||
function() {
|
||||
var stubOnSecureAppClosing =
|
||||
this.sinon.stub(subject, 'onSecureAppClosing');
|
||||
subject.handleEvent(new CustomEvent(
|
||||
'secure-appclosing'
|
||||
));
|
||||
assert.isTrue(stubOnSecureAppClosing.called);
|
||||
});
|
||||
|
||||
test('When unlocking with app without passcode, unlock it',
|
||||
function(done) {
|
||||
this.sinon.stub(subject.states.unlock, 'transferTo',
|
||||
function() {
|
||||
// This would be the next step of 'transferOut'.
|
||||
try {
|
||||
assert.isTrue(transferOutCalled,
|
||||
'the state wasn\'t transferred from slideShow to unlock');
|
||||
} catch(e) {
|
||||
done(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var states = subject.extend(subject.lockScreenDefaultStates, {
|
||||
unlockingAppActivated: true,
|
||||
passcodeEnabled: false
|
||||
});
|
||||
var transferOutCalled = false;
|
||||
subject.previousState = {
|
||||
transferOut: this.sinon.stub().returns(Promise.resolve().then(() => {
|
||||
transferOutCalled = true;
|
||||
})),
|
||||
type: 'slideShow'
|
||||
};
|
||||
subject.transfer(states).catch(done);
|
||||
});
|
||||
|
||||
test('When actionable noitification want to unlock, ' +
|
||||
'it would trigger activate unlock',
|
||||
function() {
|
||||
var stubOnActiveUnlock = this.sinon.stub(subject, 'onActivateUnlock');
|
||||
subject.handleEvent({
|
||||
type: 'lockscreen-notification-request-activate-unlock'
|
||||
});
|
||||
assert.isTrue(stubOnActiveUnlock.called,
|
||||
'the handler didn\'t handle the event');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Event handler is', function() {
|
||||
setup(function() {
|
||||
this.sinon.spy(window, 'addEventListener');
|
||||
this.sinon.spy(window, 'removeEventListener');
|
||||
});
|
||||
|
||||
test('removed when suspendEvent is called, and added back when ' +
|
||||
'resumeEvent is called', function() {
|
||||
subject.suspendEvent('holdcamera');
|
||||
assert.isTrue(window.removeEventListener.calledWith('holdcamera'),
|
||||
'Event handler is not removed');
|
||||
subject.resumeEvent('holdcamera');
|
||||
assert.isTrue(window.addEventListener.calledWith('holdcamera'),
|
||||
'Event handler is not added back');
|
||||
});
|
||||
|
||||
test('not added when resumeEvent is called on an event not suspended',
|
||||
function() {
|
||||
subject.resumeEvent('holdcamera');
|
||||
assert.isFalse(window.addEventListener.calledWith('holdcamera'),
|
||||
'Event handler is added');
|
||||
});
|
||||
|
||||
test('added only once when resumeEvent is called more than once',
|
||||
function() {
|
||||
subject.suspendEvent('holdcamera');
|
||||
subject.resumeEvent('holdcamera');
|
||||
subject.resumeEvent('holdcamera');
|
||||
assert.isTrue(window.addEventListener.calledWith('holdcamera'),
|
||||
'Event handler is added correctly');
|
||||
assert.isTrue(window.addEventListener.calledOnce,
|
||||
'Event handler is added more than once');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Hardware camera button', function() {
|
||||
test('is suspended when screen is unlocked',
|
||||
function() {
|
||||
var stubSuspendEvent = this.sinon.stub(subject, 'suspendEvent');
|
||||
subject.handleEvent({type: 'lockscreen-appclosed'});
|
||||
assert.ok(stubSuspendEvent.calledWith('holdcamera'));
|
||||
});
|
||||
|
||||
test('is resumed when screen is locked',
|
||||
function() {
|
||||
var stubResumeEvent = this.sinon.stub(subject, 'resumeEvent');
|
||||
subject.handleEvent({type: 'lockscreen-appopened'});
|
||||
assert.ok(stubResumeEvent.calledWith('holdcamera'));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
25
gaia/apps/system/test/unit/lockscreen/lockscreen_state_panelhide_test.js
Executable file
25
gaia/apps/system/test/unit/lockscreen/lockscreen_state_panelhide_test.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/* global LockScreenStatePanelHide */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_panelhide.js');
|
||||
suite('sytem/LockScreenStatePanelHide', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
overlay: document.createElement('div')
|
||||
};
|
||||
subject = (new LockScreenStatePanelHide()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function() {
|
||||
subject.transferTo().then(function() {
|
||||
assert.equal(mockLockScreen.overlay.dataset.panel, 'passcode',
|
||||
'it doesn\'t change the panel to show the slide');
|
||||
assert.equal(mockLockScreen.overlay.dataset.passcodeStatus, 'success',
|
||||
'it doesn\'t set the passcode status to the correct one');
|
||||
});
|
||||
});
|
||||
});
|
||||
26
gaia/apps/system/test/unit/lockscreen/lockscreen_state_sliderestore_test.js
Executable file
26
gaia/apps/system/test/unit/lockscreen/lockscreen_state_sliderestore_test.js
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
/* global LockScreenStateSlideRestore */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_sliderestore.js');
|
||||
suite('sytem/LockScreenStateSlideRestore', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
overlay: document.createElement('div'),
|
||||
_unlocker: {
|
||||
restore: this.sinon.stub()
|
||||
}
|
||||
};
|
||||
subject = (new LockScreenStateSlideRestore()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function() {
|
||||
subject.transferTo().then(function() {
|
||||
assert.isTrue(mockLockScreen._unlocker.restore.called,
|
||||
'the slide doesn\'t restore');
|
||||
});
|
||||
});
|
||||
});
|
||||
39
gaia/apps/system/test/unit/lockscreen/lockscreen_state_slideshow_test.js
Executable file
39
gaia/apps/system/test/unit/lockscreen/lockscreen_state_slideshow_test.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
/* global LockScreenStateSlideShow */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_slideshow.js');
|
||||
suite('sytem/LockScreenStateSlideShow', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
_unlocker: {
|
||||
reset: function() {},
|
||||
_start: function() {}
|
||||
},
|
||||
nextStep: function(cb) {
|
||||
cb();
|
||||
return Promise.resolve();
|
||||
},
|
||||
bootstrapping: {
|
||||
then: function(cb) {
|
||||
cb();
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
overlay: document.createElement('div')
|
||||
};
|
||||
subject = (new LockScreenStateSlideShow()).start(mockLockScreen);
|
||||
});
|
||||
test('it would change the DOM status as it expects', function() {
|
||||
subject.transferTo({ 'passcodeEnabled': true }).then(function() {
|
||||
assert.isTrue(mockLockScreen.overlay.classList.contains('no-transition'),
|
||||
'it still play transition even though we needn\'t it');
|
||||
assert.equal(mockLockScreen.overlay.dataset.panel, 'main',
|
||||
'it doesn\'t change the panel to show the slide');
|
||||
});
|
||||
});
|
||||
});
|
||||
23
gaia/apps/system/test/unit/lockscreen/lockscreen_state_unlock_test.js
Executable file
23
gaia/apps/system/test/unit/lockscreen/lockscreen_state_unlock_test.js
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
/* global LockScreenStateUnlock */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Must mock dependencies here.
|
||||
window.LockScreenBaseState = function() {};
|
||||
requireApp('system/lockscreen/js/lockscreen_state_unlock.js');
|
||||
suite('sytem/LockScreenStateUnlock', function() {
|
||||
var subject;
|
||||
var mockLockScreen;
|
||||
setup(function() {
|
||||
mockLockScreen = {
|
||||
unlock: this.sinon.stub()
|
||||
};
|
||||
subject = (new LockScreenStateUnlock()).start(mockLockScreen);
|
||||
});
|
||||
test('it would call unlock of LockScreen', function() {
|
||||
subject.transferTo().then(function() {
|
||||
assert.isTrue(mockLockScreen.unlock.called,
|
||||
'it doesn\'t unlock the screen');
|
||||
});
|
||||
});
|
||||
});
|
||||
957
gaia/apps/system/test/unit/lockscreen/lockscreen_test.js
Executable file
957
gaia/apps/system/test/unit/lockscreen/lockscreen_test.js
Executable file
|
|
@ -0,0 +1,957 @@
|
|||
/* global MockCanvas, MockCanvasRenderingContext2D, MockImage,
|
||||
MockService, PasscodeHelper */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
require('/shared/test/unit/mocks/mock_image.js');
|
||||
require('/shared/test/unit/mocks/mock_canvas.js');
|
||||
require('/shared/test/unit/mocks/mock_canvas_rendering_context_2d.js');
|
||||
require('/shared/js/passcode_helper.js');
|
||||
requireApp('system/lockscreen/js/lockscreen_charging.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_service.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_settings_listener.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
|
||||
requireApp('system/test/unit/mock_app_window.js');
|
||||
requireApp('system/test/unit/mock_lockscreen_slide.js');
|
||||
requireApp('system/test/unit/mock_lazy_loader.js');
|
||||
|
||||
var mocksForLockScreen = new window.MocksHelper([
|
||||
'AppWindow', 'LockScreenSlide', 'LazyLoader',
|
||||
'SettingsListener', 'Image', 'Canvas', 'Service'
|
||||
]).init();
|
||||
|
||||
var stub = sinon.stub(document, 'getElementById');
|
||||
stub.returns(document.createElement('div'));
|
||||
requireApp('system/lockscreen/js/lockscreen.js', () => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
suite('system/LockScreen >', function() {
|
||||
var subject;
|
||||
var realAudio;
|
||||
var realL10n;
|
||||
var realMozTelephony;
|
||||
var realClock;
|
||||
var realSettingsListener;
|
||||
var realMozSettings;
|
||||
var domPasscodePad;
|
||||
var domEmergencyCallBtn;
|
||||
var domOverlay;
|
||||
var domPasscodeCode;
|
||||
var domMainScreen;
|
||||
var domMaskedBackground;
|
||||
var domCamera;
|
||||
var stubById;
|
||||
var domMessage;
|
||||
var mockGetAllElements;
|
||||
mocksForLockScreen.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
stubById = sinon.stub(document, 'getElementById');
|
||||
stubById.returns(document.createElement('div'));
|
||||
mockGetAllElements = function() {
|
||||
['area', 'areaCamera', 'areaUnlock', 'altCameraButton', 'iconContainer',
|
||||
'overlay', 'clockTime', 'date'].forEach(function(name) {
|
||||
subject[name] = document.createElement('div');
|
||||
});
|
||||
};
|
||||
|
||||
window.LockScreenClockWidget = function() {
|
||||
this.stop = function() {
|
||||
return this;
|
||||
};
|
||||
this.start = function() {
|
||||
return this;
|
||||
};
|
||||
this.destroy = function() {
|
||||
return this;
|
||||
};
|
||||
this.next = function(cb) {
|
||||
cb();
|
||||
return this;
|
||||
};
|
||||
};
|
||||
|
||||
window.lockScreenNotifications = {
|
||||
bindLockScreen: function() {}
|
||||
};
|
||||
window.LockScreenConnInfoManager = function() {
|
||||
this.updateConnStates = function() {};
|
||||
};
|
||||
window.LockScreenMediaPlaybackWidget = function() {};
|
||||
window.SettingsURL = function() {};
|
||||
|
||||
realAudio = window.Audio;
|
||||
window.Audio = function(src) {
|
||||
this.src = src;
|
||||
};
|
||||
|
||||
realL10n = navigator.mozL10n;
|
||||
navigator.mozL10n = window.MockL10n;
|
||||
|
||||
realMozTelephony = navigator.mozTelephony;
|
||||
navigator.mozTelephony = window.MockNavigatorMozTelephony;
|
||||
|
||||
realSettingsListener = window.SettingsListener;
|
||||
window.SettingsListener = window.MockSettingsListener;
|
||||
|
||||
realMozSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = window.MockNavigatorSettings;
|
||||
|
||||
subject = new window.LockScreen();
|
||||
|
||||
domCamera = document.createElement('div');
|
||||
domPasscodePad = document.createElement('div');
|
||||
domPasscodePad.id = 'lockscreen-passcode-pad';
|
||||
domEmergencyCallBtn = document.createElement('a');
|
||||
domEmergencyCallBtn.dataset.key = 'e';
|
||||
domPasscodePad.appendChild(domEmergencyCallBtn);
|
||||
domOverlay = document.createElement('div');
|
||||
domPasscodeCode = document.createElement('div');
|
||||
document.body.appendChild(domPasscodePad);
|
||||
domMainScreen = document.createElement('div');
|
||||
domMaskedBackground = document.createElement('div');
|
||||
domMaskedBackground.id = 'lockscreen-masked-background';
|
||||
subject.passcodePad = domPasscodePad;
|
||||
domMessage = document.createElement('div');
|
||||
subject.message = domMessage;
|
||||
subject.lockScreenClockWidget = new window.LockScreenClockWidget();
|
||||
|
||||
var mockClock = {
|
||||
start: function() {},
|
||||
stop: function() {}
|
||||
};
|
||||
realClock = window.Clock;
|
||||
window.Clock = mockClock;
|
||||
|
||||
subject.overlay = domOverlay;
|
||||
subject.mainScreen = domMainScreen;
|
||||
subject.maskedBackground = domMaskedBackground;
|
||||
subject.camera = domCamera;
|
||||
subject.lock();
|
||||
});
|
||||
|
||||
test('L10n initialization: it should NOT init the conn info manager if ' +
|
||||
'there is already one', function() {
|
||||
var stubConnInfoManager = this.sinon.stub(window,
|
||||
'LockScreenConnInfoManager');
|
||||
var originalMozl10n = window.navigator.mozL10n;
|
||||
window.navigator.mozL10n = {
|
||||
get: function() { return ''; }
|
||||
};
|
||||
var originalMozMobileConnections = window.navigator.mozMobileConnections;
|
||||
window.navigator.mozMobileConnections = {};
|
||||
subject._lockscreenConnInfoManager = {};
|
||||
assert.isTrue(!!(window.navigator.mozMobileConnections),
|
||||
'the first condition is not satisfied: ' +
|
||||
!!(window.navigator.mozMobileConnections));
|
||||
assert.isTrue(!!(subject._lockscreenConnInfoManager),
|
||||
'the second condition is not satisfied: ' +
|
||||
!!(subject._lockscreenConnInfoManager));
|
||||
subject.l10nInit();
|
||||
assert.isFalse(stubConnInfoManager.called,
|
||||
'the l10nInit still instantiate the conn info manager even it\'s NOT' +
|
||||
'undefined');
|
||||
window.navigator.mozMobileConnections = originalMozMobileConnections;
|
||||
window.navigator.mozL10n = originalMozl10n;
|
||||
delete subject._lockscreenConnInfoManager;
|
||||
});
|
||||
|
||||
test('When checkPassCodeTimeout, it would check intervals',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.checkPassCodeTimeout;
|
||||
var mockThis = {
|
||||
fetchLockedInterval: function() {},
|
||||
fetchUnlockedInterval: function() {}
|
||||
};
|
||||
var stubFetchLockedInterval = this.sinon.stub().returns(61 * 1000);
|
||||
var stubFetchUnlockedInterval = this.sinon.stub().returns(59 * 1000);
|
||||
mockThis.fetchLockedInterval = stubFetchLockedInterval;
|
||||
mockThis.fetchUnlockedInterval = stubFetchUnlockedInterval;
|
||||
assert.isTrue(method.call(mockThis, 60));
|
||||
stubFetchLockedInterval = this.sinon.stub().returns(59 * 1000);
|
||||
stubFetchUnlockedInterval = this.sinon.stub().returns(61 * 1000);
|
||||
mockThis.fetchLockedInterval = stubFetchLockedInterval;
|
||||
mockThis.fetchUnlockedInterval = stubFetchUnlockedInterval;
|
||||
assert.isTrue(method.call(mockThis, 60));
|
||||
stubFetchLockedInterval = this.sinon.stub().returns(59 * 1000);
|
||||
stubFetchUnlockedInterval = this.sinon.stub().returns(59 * 1000);
|
||||
mockThis.fetchLockedInterval = stubFetchLockedInterval;
|
||||
mockThis.fetchUnlockedInterval = stubFetchUnlockedInterval;
|
||||
assert.isFalse(method.call(mockThis, 60));
|
||||
});
|
||||
|
||||
test('Fetch locked interval update the interval when it\'s still locked',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.fetchLockedInterval;
|
||||
var mockThis = {
|
||||
locked: true,
|
||||
_lastLockedInterval: -1,
|
||||
_lastLockedTimeStamp: 0
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis._lastLockedInterval > -1);
|
||||
});
|
||||
|
||||
test('Fetch locked interval update no interval after it\'s unlocked',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.fetchLockedInterval;
|
||||
var mockThis = {
|
||||
locked: false,
|
||||
_lastLockedInterval: -1,
|
||||
_lastLockedTimeStamp: 0
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.equal(mockThis._lastLockedInterval, -1);
|
||||
});
|
||||
|
||||
test('Fetch unlocked interval update the interval when it\'s still unlocked',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.fetchUnlockedInterval;
|
||||
var mockThis = {
|
||||
locked: false,
|
||||
_lastUnlockedInterval: -1,
|
||||
_lastUnlockedTimeStamp: 0
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis._lastUnlockedInterval > -1);
|
||||
});
|
||||
|
||||
test('Fetch unlocked interval update no interval after it\'s locked',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.fetchUnlockedInterval;
|
||||
var mockThis = {
|
||||
locked: true,
|
||||
_lastUnlockedInterval: -1,
|
||||
_lastUnlockedTimeStamp: 0
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.equal(mockThis._lastUnlockedInterval, -1);
|
||||
});
|
||||
|
||||
test('L10n initialization: it should init the conn info manager if it\'s' +
|
||||
' undefined', function() {
|
||||
var stubConnInfoManager = this.sinon.stub(window,
|
||||
'LockScreenConnInfoManager');
|
||||
var originalMozl10n = window.navigator.mozL10n;
|
||||
window.navigator.mozL10n = {
|
||||
get: function() { return ''; }
|
||||
};
|
||||
var originalMozMobileConnections = window.navigator.mozMobileConnections;
|
||||
window.navigator.mozMobileConnections = {};
|
||||
window.SIMSlotManager = {};
|
||||
assert.isTrue(!!(window.navigator.mozMobileConnections),
|
||||
'the first condition is not satisfied: ' +
|
||||
!!(window.navigator.mozMobileConnections));
|
||||
assert.isTrue(!subject._lockscreenConnInfoManager,
|
||||
'the second condition is not satisfied: ' +
|
||||
!(subject._lockscreenConnInfoManager));
|
||||
subject.l10nInit();
|
||||
assert.isTrue(stubConnInfoManager.called,
|
||||
'the l10nInit doesn\'t instantiate the conn info manager even it\'s ' +
|
||||
'undefined');
|
||||
window.navigator.mozMobileConnections = originalMozMobileConnections;
|
||||
window.navigator.mozL10n = originalMozl10n;
|
||||
delete subject._lockscreenConnInfoManager;
|
||||
});
|
||||
|
||||
test('Lock: can actually lock', function() {
|
||||
subject._lastUnlockedInterval = -1;
|
||||
subject._lastUnlockedTimeStamp = 0;
|
||||
subject._lastLockedTimeStamp = -1;
|
||||
|
||||
subject.overlay = domOverlay;
|
||||
subject.locked = false;
|
||||
subject.lock();
|
||||
assert.isTrue(subject.locked);
|
||||
assert.isTrue(subject._lastUnlockedInterval > -1,
|
||||
'it didn\'t update "_lastUnlockedInterval" when ends the unlocked session');
|
||||
assert.isTrue(subject._lastLockedTimeStamp > -1,
|
||||
'it didn\'t update "_lastLockedTimeStamp" when it locks');
|
||||
});
|
||||
|
||||
test('Lock: would create the clock widget', function() {
|
||||
subject.overlay = domOverlay;
|
||||
var stubCreateClockWidget = this.sinon.stub(subject, 'createClockWidget');
|
||||
MockService.mockQueryWith('screenEnabled', true);
|
||||
subject.locked = false;
|
||||
subject.lock();
|
||||
assert.isTrue(stubCreateClockWidget.called);
|
||||
});
|
||||
|
||||
test('Unlock: can actually unlock', function() {
|
||||
subject._lastLockedInterval = -1;
|
||||
subject._lastLockedTimeStamp = 0;
|
||||
subject._lastUnlockedTimeStamp = -1;
|
||||
|
||||
subject.overlay = domOverlay;
|
||||
subject.unlock(true);
|
||||
assert.isFalse(subject.locked);
|
||||
assert.isTrue(subject._lastLockedInterval > -1,
|
||||
'it didn\'t update "_lastLockedInterval" when it ends the locked session');
|
||||
assert.isTrue(subject._lastUnlockedTimeStamp > -1,
|
||||
'it didn\'t update "_lastUnlockedTimeStamp" when it unlocks');
|
||||
});
|
||||
|
||||
test('Unlock: uses PasscodeHelper', function() {
|
||||
var StubPasscodeHelper = this.sinon.stub(PasscodeHelper, 'check',
|
||||
function() {
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
|
||||
subject._lastLockedInterval = -1;
|
||||
subject._lastLockedTimeStamp = 0;
|
||||
subject._lastUnlockedTimeStamp = -1;
|
||||
|
||||
subject.overlay = domOverlay;
|
||||
subject.checkPassCode('0000');
|
||||
assert.isTrue(StubPasscodeHelper.called,
|
||||
'lockscreen did not call PasscodeHelper to validate passcode');
|
||||
});
|
||||
|
||||
test('Unlock: play sound in system audio channel', function() {
|
||||
window.Audio.prototype.play = function() {
|
||||
assert.equal(this.mozAudioChannelType, 'system');
|
||||
};
|
||||
subject.unlock(true, { unlockSoundEnabled: true });
|
||||
});
|
||||
|
||||
suite('Pass code validation >', function() {
|
||||
|
||||
setup(function() {
|
||||
subject.init();
|
||||
});
|
||||
|
||||
test('validation fail increases error count and timeout', function() {
|
||||
subject.kPassCodeErrorCounter = 20;
|
||||
var oldTimeout = 1;
|
||||
subject.kPassCodeErrorTimeout = oldTimeout;
|
||||
subject.overlay.dataset.passcodeStatus = 'foofoo';
|
||||
subject.onPasscodeValidationFailed();
|
||||
assert.isTrue(subject.kPassCodeErrorTimeout > oldTimeout,
|
||||
'validation fail does not increase error timeout');
|
||||
assert.isTrue(subject.kPassCodeErrorCounter == 21,
|
||||
'validation fail does not increase error counter');
|
||||
assert.isTrue(subject.overlay.dataset.passcodeStatus !== 'foofoo',
|
||||
'validation fail does not change pass code error status');
|
||||
});
|
||||
|
||||
test('validation success resets error count and timeout', function() {
|
||||
subject.kPassCodeErrorCounter = 20;
|
||||
var oldTimeout = 100000;
|
||||
subject.kPassCodeErrorTimeout = oldTimeout;
|
||||
subject.onPasscodeValidationSuccess();
|
||||
assert.isTrue(subject.kPassCodeErrorTimeout < oldTimeout/10,
|
||||
'validation success does not reset error timeout');
|
||||
assert.isTrue(subject.kPassCodeErrorCounter === 0,
|
||||
'validation success does not reset error counter');
|
||||
});
|
||||
|
||||
test('validation fail triggers validationfailed/reset events', function() {
|
||||
subject.enabled = true;
|
||||
subject.lock();
|
||||
subject.kPassCodeErrorCounter = 0;
|
||||
subject.kPassCodeErrorTimeout = 1;
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
// Force setTimeout to run in sync
|
||||
var setTimeoutStub = this.sinon.stub(window, 'setTimeout', function(f){
|
||||
f();
|
||||
});
|
||||
subject.onPasscodeValidationFailed();
|
||||
assert.isTrue(stubDispatch.firstCall.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type ===
|
||||
'lockscreen-notify-passcode-validationfailed';
|
||||
})),
|
||||
'validation fail does not trigger validationfailed as 1st event');
|
||||
assert.isTrue(stubDispatch.secondCall.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type ===
|
||||
'lockscreen-notify-passcode-validationreset';
|
||||
})),
|
||||
'validation fail does not trigger validationreset as 2nd event');
|
||||
stubDispatch.restore();
|
||||
setTimeoutStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
test('Unlock: would destroy the clock widget', function() {
|
||||
var stubDestroy = this.sinon.stub(subject.lockScreenClockWidget, 'destroy');
|
||||
subject.overlay = domOverlay;
|
||||
subject.locked = true;
|
||||
subject.unlock(true);
|
||||
assert.isTrue(stubDestroy.called);
|
||||
assert.isUndefined(subject.lockScreenClockWidget);
|
||||
});
|
||||
|
||||
|
||||
suite('Charging status updates', function() {
|
||||
test('When lockscreen is on, start charging status updates', function() {
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'start');
|
||||
subject.handleEvent({
|
||||
type: 'screenchange',
|
||||
detail: {screenEnabled: true}
|
||||
});
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.start.restore();
|
||||
});
|
||||
|
||||
test('When lockscreen is off, stop charging status updates', function() {
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'stop');
|
||||
subject.handleEvent({
|
||||
type: 'screenchange',
|
||||
detail: {screenEnabled: false}
|
||||
});
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.stop.restore();
|
||||
});
|
||||
|
||||
test('When unlocked, stop charging status updates', function() {
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'stop');
|
||||
subject.unlock();
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.stop.restore();
|
||||
});
|
||||
|
||||
test('When charging starts, refresh charging status', function() {
|
||||
// we should mock navigator.battery and set charging=true and
|
||||
// see if the element's hidden is removed, but
|
||||
// navigator.battery is read only. See bug 1115921
|
||||
subject.chargingStatus.start();
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'refresh');
|
||||
navigator.battery.dispatchEvent(new CustomEvent('chargingchange', {
|
||||
charging: true
|
||||
}));
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.refresh.restore();
|
||||
subject.chargingStatus.stop();
|
||||
});
|
||||
|
||||
test('When charging stops, refresh charging status', function() {
|
||||
subject.chargingStatus.start();
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'refresh');
|
||||
navigator.battery.dispatchEvent(new CustomEvent('chargingchange', {
|
||||
charging: false
|
||||
}));
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.refresh.restore();
|
||||
subject.chargingStatus.stop();
|
||||
});
|
||||
|
||||
test('When charging level changes, refresh charging status', function() {
|
||||
subject.chargingStatus.start();
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'refresh');
|
||||
navigator.battery.dispatchEvent(new CustomEvent('levelchange', {
|
||||
level: 0.5
|
||||
}));
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.refresh.restore();
|
||||
subject.chargingStatus.stop();
|
||||
});
|
||||
|
||||
test('When charging time changes, refresh charging status', function() {
|
||||
subject.chargingStatus.start();
|
||||
var spy = this.sinon.spy(subject.chargingStatus, 'refresh');
|
||||
navigator.battery.dispatchEvent(new CustomEvent('chargingtimechange', {
|
||||
chargeTime: 1200
|
||||
}));
|
||||
|
||||
assert.isTrue(spy.called);
|
||||
subject.chargingStatus.refresh.restore();
|
||||
subject.chargingStatus.stop();
|
||||
});
|
||||
});
|
||||
|
||||
suite('Handle event: screenchange should propogate to _screenEnabled prop',
|
||||
function() {
|
||||
var stubDispatch;
|
||||
setup(function() {
|
||||
stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject._screenEnabled = undefined;
|
||||
});
|
||||
test('True', function() {
|
||||
subject.handleEvent({
|
||||
type: 'screenchange',
|
||||
detail: {screenEnabled: true}
|
||||
});
|
||||
assert.isTrue(subject._screenEnabled);
|
||||
});
|
||||
test('False', function() {
|
||||
subject.handleEvent({
|
||||
type: 'screenchange',
|
||||
detail: {screenEnabled: false}
|
||||
});
|
||||
assert.isFalse(subject._screenEnabled);
|
||||
});
|
||||
});
|
||||
|
||||
test('Handle event: when screen changed,' +
|
||||
'would fire event to kill all secure apps',
|
||||
function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.handleEvent({type: 'screenchange', detail: {}});
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type === 'secure-killapps';
|
||||
})),
|
||||
'the event was not fired');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('Handle event: when press home,' +
|
||||
'would fire event to close all secure apps',
|
||||
function() {
|
||||
subject.lock();
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.handleEvent({type: 'home', detail: {},
|
||||
stopImmediatePropagation: function() {}});
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type === 'secure-closeapps';
|
||||
})),
|
||||
'the event was not fired');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('Handle event: when unlock,' +
|
||||
'would fire event to turn secure mode off',
|
||||
function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.unlock();
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type === 'secure-modeoff';
|
||||
})),
|
||||
'the event was not fired');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('invokeSecureApp: checking manifest and app URL of the fired Event' +
|
||||
'on secure mode',
|
||||
function() {
|
||||
var urlSamples = [
|
||||
'app://system.gaiamobile.org/index.html',
|
||||
'app://system.gaiamobile.org/index.html#'
|
||||
];
|
||||
var expectedManifest =
|
||||
'app://system.gaiamobile.org/manifest.webapp';
|
||||
|
||||
urlSamples.forEach(function(url) {
|
||||
var manifestUrl = url.replace(/(\/)*(index.html#?)*$/,
|
||||
'/manifest.webapp');
|
||||
assert.equal(manifestUrl, expectedManifest,
|
||||
'the manifestURL generated is not correct');
|
||||
});
|
||||
});
|
||||
|
||||
test('Handle event: when lock,' +
|
||||
'would fire event to turn secure mode on',
|
||||
function() {
|
||||
subject.unlock();
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.lock();
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type === 'secure-modeon';
|
||||
})),
|
||||
'the event was not fired');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('Handle event: when unlock animation ends,' +
|
||||
'it would add `unlocked` class',
|
||||
function() {
|
||||
assert.isFalse(subject.overlay.classList.contains('unlocked'));
|
||||
subject.locked = false;
|
||||
subject.handleEvent({ type: 'transitionend', target: subject.overlay });
|
||||
assert.isTrue(subject.overlay.classList.contains('unlocked'));
|
||||
});
|
||||
|
||||
test('Unlock method will add `unlocked` class if it is not an' +
|
||||
' instant request',
|
||||
function() {
|
||||
assert.isFalse(subject.overlay.classList.contains('unlocked'));
|
||||
subject.enabled = true;
|
||||
subject.unlock(true); // or lock screen is already enabled
|
||||
assert.isTrue(subject.overlay.classList.contains('unlocked'));
|
||||
});
|
||||
|
||||
test('Switch panel: to Camera; should notify SecureWindowFactory\'s method',
|
||||
function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.invokeSecureApp('camera');
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(function(e) {
|
||||
return 'secure-launchapp' === e.type;
|
||||
})),
|
||||
'the corresponding creation method was no invoked');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('Message: message should appear on screen when set', function() {
|
||||
var message = 'message';
|
||||
subject.setLockMessage(message);
|
||||
assert.equal(subject.message.hidden, false);
|
||||
assert.equal(subject.message.textContent, message);
|
||||
});
|
||||
|
||||
test('Message: message should disappear when unset', function() {
|
||||
subject.setLockMessage('');
|
||||
assert.equal(subject.message.textContent, '');
|
||||
assert.equal(subject.message.hidden, true);
|
||||
});
|
||||
|
||||
suite('lockscreen.lock-immediately settings observer >', function() {
|
||||
|
||||
setup(function() {
|
||||
subject.init(); // to register observers
|
||||
});
|
||||
|
||||
test('Locks when lock-immediately setting is set to true', function () {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
subject.enabled = true;
|
||||
subject.unlock(); // or lock screen is already enabled
|
||||
window.MockSettingsListener.mTriggerCallback(
|
||||
'lockscreen.lock-immediately', true);
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return e.type === 'lockscreen-request-lock';
|
||||
})),
|
||||
'does not request lock after lock-immediately setting is changed');
|
||||
stubDispatch.restore();
|
||||
});
|
||||
|
||||
test('resets lock/unlock timestamps', function () {
|
||||
subject.enabled = true;
|
||||
subject.passCodeEnabled = true;
|
||||
var spy = this.sinon.spy(subject, 'resetTimeoutForcibly');
|
||||
window.MockSettingsListener.mTriggerCallback(
|
||||
'lockscreen.lock-immediately', true);
|
||||
assert.isTrue(spy.called,
|
||||
'lock-immediately does not reset timestamps');
|
||||
});
|
||||
});
|
||||
|
||||
test('resetTimeoutForcibly triggers password check after lock', function () {
|
||||
subject.enabled = true;
|
||||
subject.passCodeEnabled = true;
|
||||
subject.passCodeRequestTimeout = 60 * 1000; // 60 seconds
|
||||
subject.unlock(); // or .lock() won't update timestamps
|
||||
subject.lock();
|
||||
assert.isFalse(subject.checkPassCodeTimeout(subject.passCodeRequestTimeout),
|
||||
'pass code timeout check triggers right after lock');
|
||||
subject.resetTimeoutForcibly();
|
||||
assert.isTrue(subject.checkPassCodeTimeout(subject.passCodeRequestTimeout),
|
||||
'resetTimeoutForcibly does not trigger pass code after lock');
|
||||
});
|
||||
|
||||
test('Locks the screen: the overlay would be set as locked', function() {
|
||||
var method = window.LockScreen.prototype.lock;
|
||||
var stubOverlayLocked = this.sinon.stub();
|
||||
var mockThis = {
|
||||
locked: false,
|
||||
overlayLocked: stubOverlayLocked,
|
||||
mainScreen: document.createElement('div'),
|
||||
maskedBackground : document.createElement('div'),
|
||||
createClockWidget: function() {},
|
||||
dispatchEvent: function() {},
|
||||
_checkGenerateMaskedBackgroundColor: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.isTrue(stubOverlayLocked.called);
|
||||
});
|
||||
|
||||
suite('Wallpaper', function() {
|
||||
test('Should update background when inited', function() {
|
||||
this.sinon.stub(subject, 'updateBackground');
|
||||
MockService.mockQueryWith('getWallpaper',
|
||||
'blob:app://wallpaper.gaiamobile.org/b10b-1d');
|
||||
subject.init();
|
||||
assert.isTrue(subject.updateBackground.calledWith(
|
||||
'blob:app://wallpaper.gaiamobile.org/b10b-1d'));
|
||||
});
|
||||
|
||||
test('Should update background when wallpaper changed', function() {
|
||||
this.sinon.stub(subject, 'updateBackground');
|
||||
subject.init();
|
||||
window.dispatchEvent(new CustomEvent('wallpaperchange', {
|
||||
detail: { url: 'blob:app://wallpaper.gaiamobile.org/b10b-1d' }
|
||||
}));
|
||||
assert.isTrue(subject.updateBackground.calledWith(
|
||||
'blob:app://wallpaper.gaiamobile.org/b10b-1d'));
|
||||
});
|
||||
});
|
||||
|
||||
// XXX: Test 'Screen off: by proximity sensor'.
|
||||
|
||||
suite('Background functionality', function() {
|
||||
var bgURL = 'blob:app://wallpaper.gaiamobile.org/b10b-1d';
|
||||
|
||||
suite('updateBackground', function() {
|
||||
var stubGenerate;
|
||||
|
||||
setup(function() {
|
||||
stubGenerate =
|
||||
this.sinon.stub(subject, '_generateMaskedBackgroundColor');
|
||||
});
|
||||
|
||||
test('updateBackground, Screen is not enabled and not locked',
|
||||
function() {
|
||||
subject._screenEnabled = false;
|
||||
subject.locked = false;
|
||||
subject.updateBackground(bgURL);
|
||||
var bgElem = stubById.getCall(0).returnValue;
|
||||
assert.equal(bgElem.style.backgroundImage, 'url("' + bgURL + '")');
|
||||
assert.equal(subject._regenerateMaskedBackgroundColorFrom, bgURL);
|
||||
assert.isFalse(stubGenerate.called);
|
||||
assert.isTrue(subject._shouldRegenerateMaskedBackgroundColor);
|
||||
});
|
||||
|
||||
test('updateBackground, Screen is enabled and not locked', function() {
|
||||
subject._screenEnabled = true;
|
||||
subject.locked = false;
|
||||
subject.updateBackground(bgURL);
|
||||
var bgElem = stubById.getCall(0).returnValue;
|
||||
assert.equal(bgElem.style.backgroundImage, 'url("' + bgURL + '")');
|
||||
assert.equal(subject._regenerateMaskedBackgroundColorFrom, bgURL);
|
||||
assert.isFalse(stubGenerate.called);
|
||||
assert.isTrue(subject._shouldRegenerateMaskedBackgroundColor);
|
||||
});
|
||||
|
||||
test('updateBackground, Screen is not enabled and is locked', function() {
|
||||
subject._screenEnabled = false;
|
||||
subject.locked = true;
|
||||
subject.updateBackground(bgURL);
|
||||
var bgElem = stubById.getCall(0).returnValue;
|
||||
assert.equal(bgElem.style.backgroundImage, 'url("' + bgURL + '")');
|
||||
assert.equal(subject._regenerateMaskedBackgroundColorFrom, bgURL);
|
||||
assert.isFalse(stubGenerate.called);
|
||||
assert.isTrue(subject._shouldRegenerateMaskedBackgroundColor);
|
||||
});
|
||||
|
||||
var testScreenEnabledAndLocked = function testScreenEnabledAndLocked() {
|
||||
subject.locked = true;
|
||||
subject.updateBackground(bgURL);
|
||||
var bgElem = stubById.getCall(0).returnValue;
|
||||
assert.equal(bgElem.style.backgroundImage, 'url("' + bgURL + '")');
|
||||
assert.equal(subject._regenerateMaskedBackgroundColorFrom, bgURL);
|
||||
assert.isTrue(stubGenerate.called);
|
||||
assert.isFalse(subject._shouldRegenerateMaskedBackgroundColor);
|
||||
};
|
||||
|
||||
test('updateBackground, Screen is enabled and is locked', function() {
|
||||
subject._screenEnabled = true;
|
||||
testScreenEnabledAndLocked();
|
||||
});
|
||||
|
||||
test('updateBackground, _screenEnabled = undefined is regarded as ' +
|
||||
'screen is enabled', function() {
|
||||
subject._screenEnabled = undefined;
|
||||
testScreenEnabledAndLocked();
|
||||
});
|
||||
});
|
||||
|
||||
test('checkGenerateMaskedBackgroundColor', function() {
|
||||
subject._shouldRegenerateMaskedBackgroundColor = true;
|
||||
subject._regenerateMaskedBackgroundColorFrom = bgURL;
|
||||
assert.isTrue(subject._checkGenerateMaskedBackgroundColor());
|
||||
|
||||
subject._shouldRegenerateMaskedBackgroundColor = false;
|
||||
subject._regenerateMaskedBackgroundColorFrom = undefined;
|
||||
assert.isFalse(subject._checkGenerateMaskedBackgroundColor());
|
||||
});
|
||||
|
||||
test('Lock : Removes masked Overlay if there are no notifications',
|
||||
function() {
|
||||
var method = window.LockScreen.prototype.lock;
|
||||
subject.maskedBackground.classList.add('blank');
|
||||
var mockThis = {
|
||||
locked: false,
|
||||
overlayLocked: function() {},
|
||||
mainScreen: document.createElement('div'),
|
||||
maskedBackground : subject.maskedBackground,
|
||||
createClockWidget: function() {},
|
||||
dispatchEvent: function() {},
|
||||
_checkGenerateMaskedBackgroundColor: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
method.call(mockThis);
|
||||
assert.strictEqual(subject.maskedBackground.style.backgroundColor,
|
||||
'transparent');
|
||||
});
|
||||
|
||||
suite('generateMaskedBackgroundColor', function() {
|
||||
// unit-test the function is a bit tricky: first we use the mocked image
|
||||
// object (and set fake height/width). then we use the mocked canvas
|
||||
// object that returns a image data that would calculate into red
|
||||
// (#ff0000), green (#00ff00), and blue (#0000ff) for each of the three
|
||||
// tests respectively, and we match the calculatd hsl against the known
|
||||
// result.
|
||||
// i.e. the image src fed into the function is actually not used in the
|
||||
// calculation, for the test's sake.
|
||||
|
||||
var mockCanvas;
|
||||
var mockContext;
|
||||
var mockBgElem;
|
||||
var img;
|
||||
var canvasWidth;
|
||||
var canvasHeight;
|
||||
var fakeImageData;
|
||||
|
||||
setup(function() {
|
||||
mockCanvas = new MockCanvas();
|
||||
this.sinon.stub(document, 'createElement').returns(mockCanvas);
|
||||
|
||||
mockContext = new MockCanvasRenderingContext2D();
|
||||
|
||||
mockBgElem = {
|
||||
dataset: {},
|
||||
classList: {
|
||||
contains: this.sinon.stub().returns(true)
|
||||
},
|
||||
style: {}
|
||||
};
|
||||
|
||||
subject.maskedBackground = mockBgElem;
|
||||
|
||||
MockImage.teardown();
|
||||
|
||||
subject._regenerateMaskedBackgroundColorFrom = bgURL;
|
||||
subject._generateMaskedBackgroundColor();
|
||||
|
||||
assert.isFalse(subject._shouldRegenerateMaskedBackgroundColor);
|
||||
assert.strictEqual(
|
||||
subject._regenerateMaskedBackgroundColorFrom,
|
||||
undefined
|
||||
);
|
||||
|
||||
img = MockImage.instances[0];
|
||||
img.width = 100;
|
||||
img.height = 100;
|
||||
|
||||
canvasWidth = img.width * window.devicePixelRatio;
|
||||
canvasHeight = img.height * window.devicePixelRatio;
|
||||
|
||||
this.sinon.stub(mockCanvas, 'getContext').returns(mockContext);
|
||||
|
||||
// fill the fake imagedata with all red
|
||||
fakeImageData = [];
|
||||
});
|
||||
|
||||
test('red', function() {
|
||||
for (var row = 0; row < canvasHeight; row++) {
|
||||
for (var col = 0; col < canvasWidth; col++) {
|
||||
fakeImageData[((canvasWidth * row) + col) * 4] = 255; // r
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 1] = 0; // g
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 2] = 0; // b
|
||||
}
|
||||
}
|
||||
|
||||
this.sinon.stub(mockContext, 'getImageData').returns({
|
||||
data: fakeImageData
|
||||
});
|
||||
|
||||
img.triggerEvent('onload');
|
||||
|
||||
assert.equal(
|
||||
mockBgElem.dataset.wallpaperColor,
|
||||
'hsla(0, 100%, 45%, 0.7)'
|
||||
);
|
||||
assert.isFalse('backgroundColor' in mockBgElem.style);
|
||||
});
|
||||
|
||||
test('green', function() {
|
||||
for (var row = 0; row < canvasHeight; row++) {
|
||||
for (var col = 0; col < canvasWidth; col++) {
|
||||
fakeImageData[((canvasWidth * row) + col) * 4] = 0; // r
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 1] = 255; // g
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 2] = 0; // b
|
||||
}
|
||||
}
|
||||
|
||||
this.sinon.stub(mockContext, 'getImageData').returns({
|
||||
data: fakeImageData
|
||||
});
|
||||
|
||||
img.triggerEvent('onload');
|
||||
|
||||
assert.equal(
|
||||
mockBgElem.dataset.wallpaperColor,
|
||||
'hsla(120, 100%, 45%, 0.7)'
|
||||
);
|
||||
assert.isFalse('backgroundColor' in mockBgElem.style);
|
||||
});
|
||||
|
||||
test('blue', function() {
|
||||
for (var row = 0; row < canvasHeight; row++) {
|
||||
for (var col = 0; col < canvasWidth; col++) {
|
||||
fakeImageData[((canvasWidth * row) + col) * 4] = 0; // r
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 1] = 0; // g
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 2] = 255; // b
|
||||
}
|
||||
}
|
||||
|
||||
this.sinon.stub(mockContext, 'getImageData').returns({
|
||||
data: fakeImageData
|
||||
});
|
||||
|
||||
img.triggerEvent('onload');
|
||||
|
||||
assert.equal(
|
||||
mockBgElem.dataset.wallpaperColor,
|
||||
'hsla(240, 100%, 45%, 0.7)'
|
||||
);
|
||||
assert.isFalse('backgroundColor' in mockBgElem.style);
|
||||
});
|
||||
|
||||
test('red & update background style directly', function() {
|
||||
for (var row = 0; row < canvasHeight; row++) {
|
||||
for (var col = 0; col < canvasWidth; col++) {
|
||||
fakeImageData[((canvasWidth * row) + col) * 4] = 255; // r
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 1] = 0; // g
|
||||
fakeImageData[((canvasWidth * row) + col) * 4 + 2] = 0; // b
|
||||
}
|
||||
}
|
||||
|
||||
this.sinon.stub(mockContext, 'getImageData').returns({
|
||||
data: fakeImageData
|
||||
});
|
||||
|
||||
mockBgElem.classList.contains.returns(false);
|
||||
|
||||
img.triggerEvent('onload');
|
||||
|
||||
assert.isTrue(mockBgElem.classList.contains.calledWith('blank'));
|
||||
assert.equal(
|
||||
mockBgElem.style.backgroundColor,
|
||||
'hsla(0, 100%, 45%, 0.7)'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.mozL10n = realL10n;
|
||||
navigator.mozTelephony = realMozTelephony;
|
||||
window.Audio = realAudio;
|
||||
window.Clock = realClock;
|
||||
window.SettingsListener = realSettingsListener;
|
||||
navigator.mozSettings = realMozSettings;
|
||||
|
||||
document.body.removeChild(domPasscodePad);
|
||||
subject.passcodePad = null;
|
||||
|
||||
window.MockSettingsListener.mTeardown();
|
||||
window.MockNavigatorSettings.mTeardown();
|
||||
stubById.restore();
|
||||
});
|
||||
});
|
||||
428
gaia/apps/system/test/unit/lockscreen/lockscreen_window_manager_test.js
Executable file
428
gaia/apps/system/test/unit/lockscreen/lockscreen_window_manager_test.js
Executable file
|
|
@ -0,0 +1,428 @@
|
|||
/* global Service */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
requireApp('system/shared/test/unit/mocks/mock_manifest_helper.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_service.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
requireApp('system/test/unit/mock_lock_screen.js');
|
||||
requireApp('system/test/unit/mock_lockscreen_window.js');
|
||||
requireApp('system/js/base_module.js');
|
||||
requireApp('system/js/lock_screen_window_manager.js');
|
||||
|
||||
var mocksForLockScreenWindowManager = new window.MocksHelper([
|
||||
'LockScreen', 'LockScreenWindow', 'Service', 'LazyLoader'
|
||||
]).init();
|
||||
|
||||
suite('system/LockScreenWindowManager', function() {
|
||||
var subject;
|
||||
var stubById;
|
||||
var appFake;
|
||||
var originalSettingsListener;
|
||||
var originalMozActivity;
|
||||
var originalMozSettings;
|
||||
|
||||
mocksForLockScreenWindowManager.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
stubById = this.sinon.stub(document, 'getElementById');
|
||||
stubById.returns(document.createElement('div'));
|
||||
appFake = new window.LockScreenWindow();
|
||||
|
||||
originalSettingsListener = window.SettingsListener;
|
||||
originalMozActivity = window.MozActivity;
|
||||
window.SettingsListener = {
|
||||
observe: function(name, bool, cb) {},
|
||||
getSettingsLock: function() {
|
||||
return {
|
||||
get: function(name) {
|
||||
if ('lockscreen.enabled' === name) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
set: function() {}
|
||||
};
|
||||
}
|
||||
};
|
||||
window.MozActivity = function() {};
|
||||
|
||||
originalMozSettings = window.navigator.mozSettings;
|
||||
window.navigator.mozSettings = window.MockNavigatorSettings;
|
||||
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.SettingsListener = originalSettingsListener;
|
||||
window.MozActivity = originalMozActivity;
|
||||
window.navigator.mozSettings = originalMozSettings;
|
||||
window.MockNavigatorSettings.mTeardown();
|
||||
stubById.restore();
|
||||
});
|
||||
|
||||
suite('Hierarchy functions', function() {
|
||||
setup(function() {
|
||||
subject = new window.LockScreenWindowManager();
|
||||
subject.setup();
|
||||
subject.elements = {};
|
||||
subject.elements.screen =
|
||||
document.createElement('div');
|
||||
});
|
||||
test('Should register hierarchy on start', function() {
|
||||
this.sinon.stub(Service, 'request');
|
||||
subject.start();
|
||||
assert.isTrue(Service.request.calledWith('registerHierarchy'));
|
||||
});
|
||||
|
||||
test('Should activate when openApp is called', function() {
|
||||
subject.states.enabled = true;
|
||||
var app = new window.MockLockScreenWindow();
|
||||
subject.states.instance = app;
|
||||
this.sinon.stub(app, 'isActive').returns(false);
|
||||
this.sinon.stub(subject, 'publish');
|
||||
subject.openApp();
|
||||
assert.isTrue(subject.publish.calledWith(
|
||||
subject.EVENT_PREFIX + '-activated'));
|
||||
});
|
||||
|
||||
test('Should deactivate when closeApp is called', function() {
|
||||
subject.states.enabled = true;
|
||||
var app = new window.MockLockScreenWindow();
|
||||
subject.states.instance = app;
|
||||
this.sinon.stub(app, 'isActive').returns(true);
|
||||
this.sinon.stub(subject, 'publish');
|
||||
subject.closeApp();
|
||||
assert.isTrue(subject.publish.calledWith(
|
||||
subject.EVENT_PREFIX + '-deactivated'));
|
||||
});
|
||||
|
||||
test('Should be active if the instance is active', function() {
|
||||
var app = new window.MockLockScreenWindow();
|
||||
subject.states.instance = app;
|
||||
this.sinon.stub(app, 'isActive').returns(true);
|
||||
assert.isTrue(subject.isActive());
|
||||
});
|
||||
|
||||
test('Should be inactive if the instance is inactive', function() {
|
||||
var app = new window.MockLockScreenWindow();
|
||||
subject.states.instance = app;
|
||||
this.sinon.stub(app, 'isActive').returns(false);
|
||||
assert.isFalse(subject.isActive());
|
||||
});
|
||||
|
||||
test('Should be inactive if there is no instance', function() {
|
||||
assert.isFalse(subject.isActive());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Handle events', function() {
|
||||
setup(function() {
|
||||
subject = new window.LockScreenWindowManager();
|
||||
subject.setup();
|
||||
subject.startObserveSettings();
|
||||
subject.elements = {};
|
||||
subject.elements.screen =
|
||||
document.createElement('div');
|
||||
// Differs from the existing mock which is expected by other components.
|
||||
window.LockScreen = function() {};
|
||||
});
|
||||
|
||||
test('It should stop home event to propagate', function() {
|
||||
var evt = {
|
||||
type: 'home'
|
||||
};
|
||||
// Need to be active to block the home event.
|
||||
this.sinon.stub(subject, 'isActive',
|
||||
function() {
|
||||
return true;
|
||||
});
|
||||
subject.respondToHierarchyEvent(evt);
|
||||
});
|
||||
|
||||
test('App created', function() {
|
||||
subject.handleEvent(
|
||||
{ type: 'lockscreen-appcreated',
|
||||
detail: appFake });
|
||||
assert.equal(
|
||||
subject.states.instance.instanceID,
|
||||
appFake.instanceID,
|
||||
'the app was not activated');
|
||||
window.assert.isObject(subject
|
||||
.states.instance,
|
||||
'the app was not registered in the maanger');
|
||||
subject.unregisterApp(appFake);
|
||||
});
|
||||
|
||||
test('Initialize when screenchange', function() {
|
||||
var originalCreateWindow = subject.createWindow;
|
||||
var originalLockScreenInputWindow = window.LockScreenInputWindow;
|
||||
window.LockScreenInputWindow = function() {};
|
||||
var stubCreateWindow =
|
||||
this.sinon.stub(subject, 'createWindow',
|
||||
function() {
|
||||
return originalCreateWindow.bind(this).call();
|
||||
});
|
||||
subject.states.ready = true;
|
||||
subject.handleEvent(
|
||||
{ type: 'screenchange',
|
||||
detail: { screenEnabled: true } });
|
||||
assert.isTrue(stubCreateWindow.called,
|
||||
'the manage didn\'t create the singleton window');
|
||||
var app = subject.states.instance;
|
||||
if (app) {
|
||||
subject.unregisterApp(app);
|
||||
}
|
||||
subject.stopEventListeners();
|
||||
window.LockScreenInputWindow = originalLockScreenInputWindow =
|
||||
originalLockScreenInputWindow;
|
||||
});
|
||||
|
||||
test('Screenchange by proximity sensor, should not open the LockScreen app',
|
||||
function() {
|
||||
var stubOpenApp = this.sinon.stub(subject,
|
||||
'openApp');
|
||||
subject.states.ready = true;
|
||||
subject.handleEvent(
|
||||
{
|
||||
type: 'screenchange',
|
||||
detail: { screenEnabled: true,
|
||||
screenOffBy: 'proximity'
|
||||
}
|
||||
});
|
||||
assert.isFalse(stubOpenApp.called,
|
||||
'the manager still open the LockScreen app even the ' +
|
||||
'screenchange was caused by proximity sensor');
|
||||
});
|
||||
|
||||
test('When ScreenChange and it\'s enabled, try to lock orientation',
|
||||
function() {
|
||||
var handleEvent =
|
||||
window.LockScreenWindowManager.prototype.handleEvent;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
ready: true,
|
||||
instance: {
|
||||
lockOrientation: this.sinon.stub()
|
||||
}
|
||||
},
|
||||
openApp: function() {},
|
||||
isActive: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
window.secureWindowManager = {
|
||||
isActive: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
handleEvent.call(mockSubject,
|
||||
{
|
||||
type: 'screenchange',
|
||||
detail: { screenEnabled: true }
|
||||
});
|
||||
assert.isTrue(
|
||||
mockSubject.states.instance.lockOrientation.called,
|
||||
'it doesn\'t lock the orientation while screenchage');
|
||||
});
|
||||
|
||||
test('When secure app get killed, try to lock orientation',
|
||||
function() {
|
||||
var handleEvent =
|
||||
window.LockScreenWindowManager.prototype.handleEvent;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
ready: true,
|
||||
instance: {
|
||||
lockOrientation: this.sinon.stub()
|
||||
}
|
||||
},
|
||||
openApp: function() {},
|
||||
isActive: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
window.secureWindowManager = {
|
||||
isActive: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
handleEvent.call(mockSubject,
|
||||
{
|
||||
type: 'secure-appclosed'
|
||||
});
|
||||
assert.isTrue(
|
||||
mockSubject.states.instance.lockOrientation.called,
|
||||
'it doesn\'t lock the orientation while secure app closed');
|
||||
});
|
||||
|
||||
test('Open the app when screen is turned on', function() {
|
||||
subject.registerApp(appFake);
|
||||
var stubOpen = this.sinon.stub(appFake, 'open');
|
||||
subject.states.ready = true;
|
||||
subject.handleEvent(
|
||||
{ type: 'screenchange',
|
||||
detail: { screenEnabled: true } });
|
||||
assert.isTrue(stubOpen.called,
|
||||
'the manager didn\'t call the app.open when screen on');
|
||||
subject.unregisterApp(appFake);
|
||||
});
|
||||
|
||||
test('When FTU occurs, try to close the app', function() {
|
||||
var stubCloseApp = this.sinon.stub(subject,
|
||||
'closeApp');
|
||||
subject.handleEvent({ type: 'ftuopen' });
|
||||
assert.isTrue(stubCloseApp.called,
|
||||
'the LockScreenWindowManager doesn\'t call the closeApp');
|
||||
});
|
||||
|
||||
test('When FTU occurs, the window should not be instantiated', function() {
|
||||
var stubOpenApp = this.sinon.stub(subject,
|
||||
'openApp');
|
||||
subject.states.ready = true;
|
||||
subject.handleEvent({ type: 'ftuopen' });
|
||||
subject.handleEvent(
|
||||
{ type: 'screenchange',
|
||||
detail: { screenEnabled: true } });
|
||||
assert.isFalse(stubOpenApp.called,
|
||||
'the LockScreenWindow still be instantiated while the FTU is opened');
|
||||
});
|
||||
|
||||
test('When the lockscreen settings is not ready, ' +
|
||||
'the window should not be instantiated', function() {
|
||||
var stubOpenApp = this.sinon.stub(subject,
|
||||
'openApp');
|
||||
subject.states.ready = false;
|
||||
subject.handleEvent(
|
||||
{ type: 'screenchange',
|
||||
detail: { screenEnabled: true } });
|
||||
assert.isFalse(stubOpenApp.called,
|
||||
'the LockScreenWindow still be instantiated while the FTU is opened');
|
||||
});
|
||||
|
||||
test('But after FTU done, the window should be instantiated', function() {
|
||||
var stubOpenApp = this.sinon.stub(subject,
|
||||
'openApp');
|
||||
subject.handleEvent({ type: 'ftuopen' });
|
||||
subject.handleEvent({ type: 'ftudone' });
|
||||
subject.states.ready = true;
|
||||
subject.handleEvent(
|
||||
{ type: 'screenchange',
|
||||
detail: { screenEnabled: true } });
|
||||
assert.isTrue(stubOpenApp.called,
|
||||
'the LockScreenWindow is not instantiated after the FTU was closed.');
|
||||
});
|
||||
|
||||
test('Send lockscreen window to background while overlay is there.',
|
||||
function() {
|
||||
var app = new window.MockLockScreenWindow();
|
||||
this.sinon.stub(app, 'isActive').returns(true);
|
||||
subject.states.instance = app;
|
||||
var stubSetVisible = this.sinon.stub(app, 'setVisible');
|
||||
subject.handleEvent({ type: 'overlaystart' });
|
||||
assert.isTrue(stubSetVisible.calledWith(false));
|
||||
});
|
||||
|
||||
test('Send lockscreen window to foreground.', function() {
|
||||
var app = new window.MockLockScreenWindow();
|
||||
this.sinon.stub(app, 'isActive').returns(true);
|
||||
subject.states.instance = app;
|
||||
var stubSetVisible = this.sinon.stub(app, 'setVisible');
|
||||
subject.handleEvent({
|
||||
type: 'showlockscreenwindow'
|
||||
});
|
||||
assert.isTrue(stubSetVisible.calledWith(true));
|
||||
});
|
||||
|
||||
test('When system resizing event comes, try to resize the window',
|
||||
function() {
|
||||
var handleEvent =
|
||||
window.LockScreenWindowManager.prototype.handleEvent;
|
||||
var mockSubject = {
|
||||
states: {
|
||||
instance: {
|
||||
isActive: function() { return true; },
|
||||
resize: this.sinon.stub().returns({ stub: 'promise' })
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var stubWaitUntil = this.sinon.stub();
|
||||
|
||||
handleEvent.call(mockSubject,
|
||||
{
|
||||
type: 'system-resize',
|
||||
detail: {
|
||||
waitUntil: stubWaitUntil
|
||||
}
|
||||
});
|
||||
assert.isTrue(
|
||||
mockSubject.states.instance.resize.called,
|
||||
'it doesn\'t resize the window while system-resize comes');
|
||||
assert.isTrue(stubWaitUntil.calledWith({ stub: 'promise' }),
|
||||
'it doesn\'t pass the promise from the subject to waitUntil() ' +
|
||||
'function.');
|
||||
});
|
||||
|
||||
test('LockScreen request to unlock without activity detail', function() {
|
||||
var evt = { type: 'lockscreen-request-unlock' },
|
||||
stubCloseApp = this.sinon.stub(subject,
|
||||
'closeApp');
|
||||
subject.handleEvent(evt);
|
||||
assert.isTrue(stubCloseApp.called,
|
||||
'it did\'t close the window while unlock request arrive');
|
||||
});
|
||||
|
||||
test('Open the app when asked via lock-immediately setting', function() {
|
||||
subject.registerApp(appFake);
|
||||
var stubOpen = this.sinon.stub(appFake, 'open');
|
||||
window.MockNavigatorSettings.mTriggerObservers(
|
||||
'lockscreen.lock-immediately', {settingValue: true});
|
||||
assert.isTrue(stubOpen.called,
|
||||
'the manager didn\'t open the app when requested');
|
||||
subject.unregisterApp(appFake);
|
||||
});
|
||||
|
||||
test('onInputpadOpen would open the window, setVisible and call resize',
|
||||
function() {
|
||||
subject.states.instance = appFake;
|
||||
appFake.inputWindow = {
|
||||
open: this.sinon.stub(),
|
||||
close: this.sinon.stub(),
|
||||
setVisible: this.sinon.stub()
|
||||
};
|
||||
var stubResize = this.sinon.stub(appFake, 'resize');
|
||||
subject.onInputpadOpen();
|
||||
assert.isTrue(appFake.inputWindow.open.called, 'called |open|');
|
||||
assert.isTrue(stubResize.called, 'called no |resize|');
|
||||
assert.isTrue(appFake.inputWindow.setVisible.calledWith(true));
|
||||
});
|
||||
|
||||
test('onInputpadClose would close the window and call resize', function() {
|
||||
subject.states.instance = appFake;
|
||||
appFake.inputWindow = {
|
||||
open: this.sinon.stub(),
|
||||
close: this.sinon.stub()
|
||||
};
|
||||
var stubResize = this.sinon.stub(appFake, 'resize');
|
||||
subject.onInputpadClose();
|
||||
assert.isTrue(appFake.inputWindow.close.called, 'called |close|');
|
||||
assert.isTrue(stubResize.called, 'called no |resize|');
|
||||
});
|
||||
|
||||
test('Open the app would set the corresponding mozSettings', function() {
|
||||
var originalEnabled = subject.states.enabled;
|
||||
subject.states.enabled = true;
|
||||
subject.registerApp(appFake);
|
||||
var stubToggleSystemSettings = this.sinon.stub(
|
||||
subject, 'toggleLockedSetting');
|
||||
subject.openApp();
|
||||
assert.isTrue(stubToggleSystemSettings.calledWith(true),
|
||||
'the manager didn\'t set the mozSettings value');
|
||||
subject.unregisterApp(appFake);
|
||||
subject.states.enabled = originalEnabled;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
167
gaia/apps/system/test/unit/lockscreen/lockscreen_window_test.js
Executable file
167
gaia/apps/system/test/unit/lockscreen/lockscreen_window_test.js
Executable file
|
|
@ -0,0 +1,167 @@
|
|||
/* global LayoutManager, MockL10n, MockService */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/shared/js/sanitizer.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_service.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_manifest_helper.js');
|
||||
requireApp('system/shared/test/unit/mocks/mock_settings_listener.js');
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
requireApp('system/test/unit/mock_applications.js');
|
||||
requireApp('system/test/unit/mock_layout_manager.js');
|
||||
require('/shared/test/unit/mocks/mock_l10n.js');
|
||||
requireApp('system/test/unit/mock_screen_layout.js');
|
||||
|
||||
var mocksForLockScreenWindow = new window.MocksHelper([
|
||||
'Service', 'Applications', 'SettingsListener', 'LazyLoader',
|
||||
'ManifestHelper', 'ScreenLayout', 'LayoutManager'
|
||||
]).init();
|
||||
|
||||
suite('system/LockScreenWindow', function() {
|
||||
var realL10n, stubById, stubLockScreenAgent;
|
||||
mocksForLockScreenWindow.attachTestHelpers();
|
||||
|
||||
setup(function(done) {
|
||||
stubLockScreenAgent = function() {
|
||||
this.start = function() {};
|
||||
};
|
||||
window.LockScreenAgent = stubLockScreenAgent;
|
||||
stubById = this.sinon.stub(document, 'getElementById', function(id) {
|
||||
|
||||
var element = document.createElement('div');
|
||||
|
||||
if (id.indexOf('AppWindow') >= 0) {
|
||||
var container = document.createElement('div');
|
||||
container.className = 'browser-container';
|
||||
element.appendChild(container);
|
||||
} else {
|
||||
var iframe = document.createElement('div');
|
||||
document.body.appendChild(iframe);
|
||||
return iframe;
|
||||
}
|
||||
|
||||
return element;
|
||||
});
|
||||
// Differs from the existing mock which is expected by other components.
|
||||
window.LockScreen = function() {};
|
||||
window.LockScreen.prototype.init = this.sinon.stub();
|
||||
window.lockScreenNotificationBuilder =
|
||||
window.lockScreenNotifications = {
|
||||
start: function() {}
|
||||
};
|
||||
window.LockScreenStateManager = function() {
|
||||
this.start = function() {};
|
||||
};
|
||||
window.layoutManager = new LayoutManager();
|
||||
window.layoutManager.start();
|
||||
|
||||
realL10n = window.navigator.mozL10n;
|
||||
window.navigator.mozL10n = MockL10n;
|
||||
|
||||
requireApp('system/js/browser_config_helper.js');
|
||||
requireApp('system/js/browser_frame.js');
|
||||
requireApp('system/js/app_window.js');
|
||||
requireApp('system/js/browser_mixin.js');
|
||||
requireApp('system/js/app_window.js');
|
||||
requireApp('system/js/lockscreen_window.js', done);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
window.navigator.mozL10n = realL10n;
|
||||
stubById.restore();
|
||||
});
|
||||
|
||||
suite('Handle events', function() {
|
||||
test('Create lockscreen window would trigger events', function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent');
|
||||
var app = new window.LockScreenWindow();
|
||||
true === app; // To prevent lint complain usless var,
|
||||
// which should actually fired event.
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return 'lockscreen-appcreated' === e.type;
|
||||
})),
|
||||
'the event has not been fired');
|
||||
});
|
||||
|
||||
test('Kill lockscreen window would trigger events', function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent'),
|
||||
app = new window.LockScreenWindow(),
|
||||
stubIsActive = this.sinon.stub(app, 'isActive'),
|
||||
parentElement = document.createElement('div');
|
||||
|
||||
stubIsActive.returns(true);
|
||||
// Or the AppWindow would look for it.
|
||||
app.element = document.createElement('div');
|
||||
parentElement.appendChild(app.element);
|
||||
app.kill();
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return 'lockscreen-apprequestclose' === e.type;
|
||||
})),
|
||||
'the event has not been fired');
|
||||
});
|
||||
|
||||
test('Terminated lockscreen window would trigger events', function() {
|
||||
var stubDispatch = this.sinon.stub(window, 'dispatchEvent'),
|
||||
app = new window.LockScreenWindow(),
|
||||
stubIsActive = this.sinon.stub(app, 'isActive'),
|
||||
parentElement = document.createElement('div');
|
||||
|
||||
stubIsActive.returns(true);
|
||||
// Or the AppWindow would look for it.
|
||||
app.element = document.createElement('div');
|
||||
parentElement.appendChild(app.element);
|
||||
app.kill();
|
||||
app.close();
|
||||
assert.isTrue(stubDispatch.calledWithMatch(sinon.match(
|
||||
function(e) {
|
||||
return 'lockscreen-appterminated' === e.type;
|
||||
})),
|
||||
'the event has not been fired');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Methods', function() {
|
||||
test('Create lockscreen window would render layout', function() {
|
||||
var app = new window.LockScreenWindow();
|
||||
assert.isNotNull(app.iframe,
|
||||
'the layout did\'t draw after window opened');
|
||||
});
|
||||
});
|
||||
|
||||
test('Resize', function() {
|
||||
var originalLockScreenWindowManager = window.lockScreenWindowManager;
|
||||
var app = new window.LockScreenWindow();
|
||||
app.inputWindow = {
|
||||
isActive() { return false; }
|
||||
};
|
||||
var stubIsActive = this.sinon.stub(app, 'isActive');
|
||||
stubIsActive.returns(true);
|
||||
app.resize();
|
||||
assert.equal(app.height, app.layoutHeight());
|
||||
window.lockScreenWindowManager = originalLockScreenWindowManager;
|
||||
});
|
||||
|
||||
test('lockOrientation', function() {
|
||||
var mockScreen = {
|
||||
mozLockOrientation: function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
MockService.mockQueryWith('isOnRealDevice', true);
|
||||
this.sinon.stub(window, 'screen', mockScreen);
|
||||
var method = window.LockScreenWindow.prototype.lockOrientation;
|
||||
this.sinon.stub(window, 'clearInterval');
|
||||
var stubSetInterval = this.sinon.stub(window, 'setInterval', function() {
|
||||
return 1;
|
||||
});
|
||||
var mockThis = {
|
||||
orientationLockID: null
|
||||
};
|
||||
method.call(mockThis);
|
||||
method.call(mockThis);
|
||||
assert.isTrue(stubSetInterval.calledOnce);
|
||||
assert.isFalse(stubSetInterval.calledTwice);
|
||||
});
|
||||
});
|
||||
34
gaia/apps/system/test/unit/lockscreen/source/dom_event_source_test.js
Executable file
34
gaia/apps/system/test/unit/lockscreen/source/dom_event_source_test.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
/* global DOMEventSource */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* To test if DOMEventSource can really register the events and forward to
|
||||
* the target handler.
|
||||
**/
|
||||
requireApp('system/lockscreen/js/source/source_event.js');
|
||||
requireApp('system/lockscreen/js/source/dom_event_source.js');
|
||||
|
||||
suite('DOMEventSource > ', function() {
|
||||
|
||||
test(`DOMEventSource would forward events from native event listener`,
|
||||
function() {
|
||||
var stubForwardTo = this.sinon.stub();
|
||||
var stubAddEventListener =
|
||||
this.sinon.stub(window, 'addEventListener');
|
||||
var stubRemoveEventListener =
|
||||
this.sinon.stub(window, 'removeEventListener');
|
||||
var source = new DOMEventSource({
|
||||
events: ['foo']
|
||||
});
|
||||
source.start(stubForwardTo);
|
||||
assert.isTrue(stubAddEventListener.called);
|
||||
source.onchange(new CustomEvent('foo'));
|
||||
source.onchange(new CustomEvent('foo'));
|
||||
assert.isTrue(stubForwardTo.calledTwice);
|
||||
source.stop();
|
||||
assert.isTrue(stubRemoveEventListener.called);
|
||||
source.onchange(new CustomEvent('foo'));
|
||||
assert.isFalse(stubForwardTo.calledThrice);
|
||||
});
|
||||
});
|
||||
|
||||
36
gaia/apps/system/test/unit/lockscreen/source/minute_clock_source_test.js
Executable file
36
gaia/apps/system/test/unit/lockscreen/source/minute_clock_source_test.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
/* global MinuteClockSource */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* To test if MinuteClockSource can really register the events and forward to
|
||||
* the target handler.
|
||||
**/
|
||||
requireApp('system/lockscreen/js/source/source_event.js');
|
||||
requireApp('system/lockscreen/js/source/minute_clock_source.js');
|
||||
|
||||
suite('MinuteClockSource > ', function() {
|
||||
|
||||
test(`MinuteClockSource is a source fire the event every [interval]`,
|
||||
function() {
|
||||
var clock = sinon.useFakeTimers();
|
||||
var stubForwardTo = sinon.stub();
|
||||
var source = new MinuteClockSource({
|
||||
type: 'dummy-ticking'
|
||||
});
|
||||
// Fake clock would stub the clock and make it as at the second 0
|
||||
// of this minute, so it would wait 60 seconds to set the timeout.
|
||||
source.start(stubForwardTo);
|
||||
clock.tick(100);
|
||||
assert.isTrue(stubForwardTo.calledOnce,
|
||||
`it doesn't wait 0 seconds to start ticking`);
|
||||
clock.tick(60000);
|
||||
assert.isTrue(stubForwardTo.calledTwice,
|
||||
`it doesn't tick after the #1 minute`);
|
||||
clock.tick(59000);
|
||||
assert.isFalse(stubForwardTo.calledThrice,
|
||||
`it ticks too more times before the #2 minute`);
|
||||
clock.tick(1000);
|
||||
assert.isTrue(stubForwardTo.calledThrice,
|
||||
`it doesn't tick 3 times after the #2 minute`);
|
||||
});
|
||||
});
|
||||
55
gaia/apps/system/test/unit/lockscreen/stream/process_test.js
Executable file
55
gaia/apps/system/test/unit/lockscreen/stream/process_test.js
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
/* global Process */
|
||||
|
||||
requireApp('system/lockscreen/js/stream/process.js');
|
||||
|
||||
suite('Process > ', function() {
|
||||
test(`Would execute steps until it get shifted`, function(done) {
|
||||
var process = new Process();
|
||||
process
|
||||
.start()
|
||||
.next(() => {
|
||||
return Promise.resolve().then(() => {
|
||||
process.stop().next(done)
|
||||
.rescue((e) => {
|
||||
done(e || 'fail');
|
||||
});
|
||||
});
|
||||
})
|
||||
.next(() => {
|
||||
done('fail');
|
||||
});
|
||||
});
|
||||
|
||||
test(`It's fine to handle multiple tasks`, function(done) {
|
||||
var number = 0;
|
||||
var process = new Process();
|
||||
var task1 = (new Process())
|
||||
.start()
|
||||
.next(() => {
|
||||
number += 1;
|
||||
return Promise.resolve();
|
||||
});
|
||||
var task2 = (new Process())
|
||||
.start()
|
||||
.next(() => {
|
||||
number += 1;
|
||||
// This line would stuck the test, since it
|
||||
// would never been resolved (as we expect
|
||||
// what the 'all' method should do).
|
||||
//return new Promise(() => {});
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
process
|
||||
.start()
|
||||
.wait(() => task2, () => task1)
|
||||
.next(() => {
|
||||
assert.equal(number, 2,
|
||||
`the tasks had not been executed correctly`);
|
||||
})
|
||||
.next(done)
|
||||
.rescue(done);
|
||||
});
|
||||
});
|
||||
77
gaia/apps/system/test/unit/lockscreen/stream/stream_test.js
Executable file
77
gaia/apps/system/test/unit/lockscreen/stream/stream_test.js
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
/* global Process, Stream */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Since Stream is just wrapping Process, so we could test both of them
|
||||
* at the same time.
|
||||
**/
|
||||
requireApp('system/lockscreen/js/stream/process.js');
|
||||
requireApp('system/lockscreen/js/stream/stream.js');
|
||||
|
||||
suite('Stream > ', function() {
|
||||
test(`stream would only handle events after it's ready,
|
||||
and after the process get stopped, it would stop to handle events`,
|
||||
function(done) {
|
||||
var stubHandler = this.sinon.stub();
|
||||
var stream = new Stream({
|
||||
events: ['foo'],
|
||||
interrupts: ['bar'],
|
||||
// A fake source is enough for testing.
|
||||
sources: [{
|
||||
start: () => {},
|
||||
stop: () => {}
|
||||
}]
|
||||
});
|
||||
stream
|
||||
.start(stubHandler)
|
||||
.ready()
|
||||
.onchange(new CustomEvent('foo'))
|
||||
.onchange(new CustomEvent('foo'))
|
||||
.next(() => {
|
||||
return stream.stop()
|
||||
.onchange(new CustomEvent('foo'))
|
||||
.next(() => {
|
||||
assert.isFalse(stubHandler.calledOnce);
|
||||
assert.isTrue(stubHandler.calledTwice);
|
||||
assert.isFalse(stubHandler.calledThrice);
|
||||
})
|
||||
.next(done)
|
||||
.rescue(done);
|
||||
});
|
||||
});
|
||||
|
||||
test(`if it's interrupt, the handler would not be queued`,
|
||||
function(done) {
|
||||
var stubHandler = this.sinon.spy((evt) => {
|
||||
// The first 'foo' would be handled only after the stack
|
||||
// is empty, so the actual handling order is 'bar->bar->foo',
|
||||
// and then the queue get stuck since we don't resolve it,
|
||||
// which is also the moment we do assertion.
|
||||
if ('foo' === evt.type) {
|
||||
assert.isTrue(stubHandler.calledThrice);
|
||||
done();
|
||||
}
|
||||
var { promise } = Process.defer();
|
||||
return promise;
|
||||
// Don't resolve.
|
||||
// So the next handler would not be executed,
|
||||
// unless the event is an interrupt.
|
||||
});
|
||||
var stream = new Stream({
|
||||
events: ['foo'],
|
||||
interrupts: ['bar'],
|
||||
sources: [{
|
||||
start: () => {},
|
||||
stop: () => {}
|
||||
}]
|
||||
});
|
||||
stream
|
||||
.start(stubHandler)
|
||||
.ready()
|
||||
.onchange(new CustomEvent('foo')) // execute
|
||||
.onchange(new CustomEvent('foo')) // no execute
|
||||
.onchange(new CustomEvent('bar')) // execute
|
||||
.onchange(new CustomEvent('bar')); // execute
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/* global LockScreenClockWidgetSetup */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/source/dom_event_source.js');
|
||||
requireApp('system/lockscreen/js/source/source_event.js');
|
||||
requireApp('system/lockscreen/js/stream/process.js');
|
||||
requireApp('system/lockscreen/js/stream/stream.js');
|
||||
requireApp('system/lockscreen/js/state/lockscreen_basic_state.js');
|
||||
requireApp(
|
||||
'system/lockscreen/js/widgets/clock/lockscreen_clock_widget_setup.js');
|
||||
|
||||
suite('LockScreenClockWidgetSetup > ', function() {
|
||||
setup(function() {
|
||||
window.Stream = function() {
|
||||
this.steps = [];
|
||||
};
|
||||
window.Stream.prototype = {
|
||||
start: function() { return this; },
|
||||
ready: function() { return this; },
|
||||
stop: function() { return this; },
|
||||
destroy: function() { return this; },
|
||||
next: function(step) {
|
||||
this.steps.push(step);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test(`when setup it would do the setup things`,
|
||||
function() {
|
||||
var mockThis = {
|
||||
configs: {},
|
||||
handleSourceEvent: function() {},
|
||||
queryElements: {
|
||||
bind: this.sinon.stub().returns('queryElements')
|
||||
},
|
||||
component: {
|
||||
updateClock: {
|
||||
bind: this.sinon.stub().returns('component.updateClock')
|
||||
},
|
||||
updateFormatters: {
|
||||
bind: this.sinon.stub().returns('component.updateFormatters')
|
||||
},
|
||||
updateAlarm: {
|
||||
bind: this.sinon.stub().returns('component.updateAlarm')
|
||||
},
|
||||
fetchAlarmData: {
|
||||
bind: this.sinon.stub().returns('component.fetchAlarmData')
|
||||
}
|
||||
},
|
||||
transferToTick: {
|
||||
bind: this.sinon.stub().returns('transferToTick')
|
||||
}
|
||||
};
|
||||
var method = LockScreenClockWidgetSetup.prototype.start;
|
||||
method.call(mockThis);
|
||||
assert.include(mockThis.stream.steps, 'queryElements',
|
||||
`it doesn't scheudle to query the elements`);
|
||||
assert.include(mockThis.stream.steps, 'component.updateClock',
|
||||
`it doesn't schedule to update the clock at the setup`);
|
||||
assert.include(mockThis.stream.steps, 'component.updateFormatters',
|
||||
`it doesn't schedule to update the date/time formatters at the setup`);
|
||||
assert.include(mockThis.stream.steps, 'transferToTick',
|
||||
`it doesn't schedule to transfer to the tick state after it's done`);
|
||||
});
|
||||
|
||||
test(`it would transfer to the tick phase`, function() {
|
||||
window.LockScreenClockWidgetTick = {
|
||||
name: 'LockScreenClockWidgetTick'
|
||||
};
|
||||
var mockThis = {
|
||||
transferTo: this.sinon.stub()
|
||||
};
|
||||
var method = LockScreenClockWidgetSetup.prototype.transferToTick;
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis.transferTo.calledWithMatch(
|
||||
sinon.match(function(state) {
|
||||
return 'LockScreenClockWidgetTick' === state.name;
|
||||
})),
|
||||
`it doesn't transfer to the tick state`
|
||||
);
|
||||
});
|
||||
|
||||
test(`it would query elements as the component requires`, function() {
|
||||
var mockThis = {
|
||||
component: {
|
||||
resources: {
|
||||
elements: {
|
||||
date: 'date-query',
|
||||
time: 'time-query'
|
||||
}}}};
|
||||
var dummyDiv = document.createElement('div');
|
||||
this.sinon.stub(document, 'getElementById').returns(dummyDiv);
|
||||
var method = LockScreenClockWidgetSetup.prototype.queryElements;
|
||||
method.call(mockThis);
|
||||
assert.strictEqual(dummyDiv, mockThis.component.resources.elements.date,
|
||||
`the date query hasn't been queried`);
|
||||
assert.strictEqual(dummyDiv, mockThis.component.resources.elements.time,
|
||||
`the time query hasn't been queried`);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/* global LockScreenClockWidgetSuspend */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/source/dom_event_source.js');
|
||||
requireApp('system/lockscreen/js/source/source_event.js');
|
||||
requireApp('system/lockscreen/js/stream/process.js');
|
||||
requireApp('system/lockscreen/js/stream/stream.js');
|
||||
requireApp('system/lockscreen/js/state/lockscreen_basic_state.js');
|
||||
requireApp(
|
||||
'system/lockscreen/js/widgets/clock/lockscreen_clock_widget_suspend.js');
|
||||
|
||||
suite('LockScreenClockWidgetSuspend > ', function() {
|
||||
setup(function() {
|
||||
window.Stream = function() {
|
||||
this.steps = [];
|
||||
};
|
||||
window.Stream.prototype = {
|
||||
start: function() { return this; },
|
||||
ready: function() { return this; },
|
||||
stop: function() { return this; },
|
||||
destroy: function() { return this; },
|
||||
next: function(step) {
|
||||
this.steps.push(step);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test(`when start it should do the setup things`,
|
||||
function() {
|
||||
var mockThis = {
|
||||
configs: {},
|
||||
handleSourceEvent: function() {}
|
||||
};
|
||||
this.sinon.stub(window.Stream.prototype.ready, 'bind').returns('ready');
|
||||
var method = LockScreenClockWidgetSuspend.prototype.start;
|
||||
method.call(mockThis);
|
||||
assert.include(mockThis.stream.steps, 'ready',
|
||||
`it doesn't schedule to become ready`);
|
||||
});
|
||||
|
||||
test(`when stop it should stop things`, function() {
|
||||
var mockThis = {
|
||||
stream: new window.Stream()
|
||||
};
|
||||
var stubStop = this.sinon.stub(mockThis.stream, 'stop');
|
||||
var method = LockScreenClockWidgetSuspend.prototype.stop;
|
||||
method.call(mockThis);
|
||||
assert.isTrue(stubStop.called,
|
||||
`it doesn't stop the stream`);
|
||||
});
|
||||
|
||||
test(`when events it would call the corresponding functions`, function() {
|
||||
var mockThis = {
|
||||
component: { updateClock: this.sinon.stub() },
|
||||
transferToTick: this.sinon.stub()
|
||||
};
|
||||
var method = LockScreenClockWidgetSuspend.prototype.handleSourceEvent;
|
||||
|
||||
method.call(mockThis, {'type': 'screenchange',
|
||||
'detail': {screenEnabled: true} });
|
||||
assert.isTrue(mockThis.transferToTick.called,
|
||||
`when 'screenchange' & screenEnabled it doesn't transfer to tick`);
|
||||
mockThis.transferToSuspend = this.sinon.stub();
|
||||
});
|
||||
|
||||
test(`it would call the component's method to transfer to tick`,
|
||||
function() {
|
||||
window.LockScreenClockWidgetTick = {
|
||||
name: 'LockScreenClockWidgetTick'
|
||||
};
|
||||
var mockThis = {
|
||||
component: {
|
||||
transferTo: this.sinon.stub()
|
||||
}
|
||||
};
|
||||
var method = LockScreenClockWidgetSuspend.prototype.transferToTick;
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis.component.transferTo.calledWithMatch(function(clz) {
|
||||
return 'LockScreenClockWidgetTick' === clz.name;
|
||||
}));
|
||||
});
|
||||
|
||||
test(`it should update the clock before transferring to the Suspend state`,
|
||||
function() {
|
||||
var mockThis = {
|
||||
component: { updateClock: this.sinon.stub() },
|
||||
transferToTick: function() {}
|
||||
};
|
||||
var method = LockScreenClockWidgetSuspend.prototype.handleSourceEvent;
|
||||
method.call(mockThis, {'type': 'screenchange',
|
||||
'detail': { 'screenEnabled': true}});
|
||||
assert.isTrue(mockThis.component.updateClock.called,
|
||||
`it doesn't call updateClock before the transferring`);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/* global LockScreenClockWidget */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/component/lockscreen_basic_component.js');
|
||||
requireApp(
|
||||
'system/lockscreen/js/widgets/clock/lockscreen_clock_widget.js');
|
||||
|
||||
suite('LockScreenClockWidget > ', function() {
|
||||
var domAlarm;
|
||||
setup(function() {
|
||||
window.LockScreenClockWidgetSetup = this.sinon.stub();
|
||||
domAlarm = document.createElement('div');
|
||||
});
|
||||
|
||||
test(`when setup it would kick off the state machine`,
|
||||
function() {
|
||||
var method = LockScreenClockWidget.prototype.setup;
|
||||
method.call({});
|
||||
assert.isTrue(window.LockScreenClockWidgetSetup.called,
|
||||
`it doesn't kick off the state machine`);
|
||||
});
|
||||
|
||||
test(`it would update the clock`, function() {
|
||||
var mockThis = {
|
||||
logger: {
|
||||
debug: function() {}
|
||||
},
|
||||
timeFormatter: new Intl.DateTimeFormat('en-US', {
|
||||
hour12: false,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
}),
|
||||
dateFormatter: new Intl.DateTimeFormat('en-US', {
|
||||
weekday: 'long',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
}),
|
||||
resources: {
|
||||
elements: {
|
||||
time: { firstChild: { data: 'dummy-data' }},
|
||||
date: { firstChild: { data: 'dummy-data' }}
|
||||
}}
|
||||
};
|
||||
var method = LockScreenClockWidget.prototype.updateClock;
|
||||
var now = new Date();
|
||||
method.call(mockThis);
|
||||
assert.equal(mockThis.timeFormatter.format(now),
|
||||
mockThis.resources.elements.time.firstChild.data,
|
||||
`it doesn't update the time.textContent with the time in locale format`);
|
||||
assert.equal(mockThis.dateFormatter.format(now),
|
||||
mockThis.resources.elements.date.firstChild.data,
|
||||
`it doesn't update the date.textContent with the date in locale format`);
|
||||
});
|
||||
|
||||
test(`it would update the alarm info (an alarm is set)`, function(done) {
|
||||
var mockThis = {
|
||||
resources: {
|
||||
elements: {
|
||||
alarm: domAlarm,
|
||||
alarmtime: { textContent: 'dummy-textContent' },
|
||||
}
|
||||
},
|
||||
fetchAlarmData: function() {
|
||||
return new Promise( function(resolve,reject){
|
||||
resolve({hour: '20', minute: '15'});
|
||||
});
|
||||
}
|
||||
};
|
||||
var method = LockScreenClockWidget.prototype.updateAlarm;
|
||||
method.call(mockThis);
|
||||
assert.isFulfilled(mockThis.fetchAlarmData());
|
||||
mockThis.fetchAlarmData().then( function() {
|
||||
assert.equal(mockThis.resources.elements.alarmtime.textContent, '8:15PM');
|
||||
assert.propertyVal( mockThis.resources.elements.alarm.classList,
|
||||
'0', 'has-alarm');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
test(`it would update the alarm info (an alarm is not set)`, function(done) {
|
||||
var mockThis = {
|
||||
resources: {
|
||||
elements: {
|
||||
alarm: domAlarm,
|
||||
alarmtime: { textContent: 'dummy-textContent' },
|
||||
}
|
||||
},
|
||||
fetchAlarmData: function() {
|
||||
return new Promise( function(resolve,reject){
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
};
|
||||
var method = LockScreenClockWidget.prototype.updateAlarm;
|
||||
method.call(mockThis);
|
||||
assert.isFulfilled(mockThis.fetchAlarmData());
|
||||
mockThis.fetchAlarmData().then( function() {
|
||||
assert.propertyVal( mockThis.resources.elements.alarm.classList,
|
||||
'0', 'no-alarms');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/* global LockScreenClockWidgetTick */
|
||||
'use strict';
|
||||
|
||||
requireApp('system/lockscreen/js/source/dom_event_source.js');
|
||||
requireApp('system/lockscreen/js/source/source_event.js');
|
||||
requireApp('system/lockscreen/js/stream/process.js');
|
||||
requireApp('system/lockscreen/js/stream/stream.js');
|
||||
requireApp('system/lockscreen/js/state/lockscreen_basic_state.js');
|
||||
requireApp(
|
||||
'system/lockscreen/js/widgets/clock/lockscreen_clock_widget_tick.js');
|
||||
|
||||
suite('LockScreenClockWidgetTick > ', function() {
|
||||
setup(function() {
|
||||
window.Stream = function() {
|
||||
this.steps = [];
|
||||
};
|
||||
window.Stream.prototype = {
|
||||
start: function() { return this; },
|
||||
ready: function() { return this; },
|
||||
stop: function() { return this; },
|
||||
destroy: function() { return this; },
|
||||
next: function(step) {
|
||||
if ('function' === typeof step) {
|
||||
this.steps.push(step());
|
||||
} else {
|
||||
this.steps.push(step);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
test(`when start it should do the setup things`,
|
||||
function() {
|
||||
var mockThis = {
|
||||
configs: {},
|
||||
handleSourceEvent: function() {},
|
||||
component: {
|
||||
_previousState: { configs: { name: 'LockScreenClockWidgetSetup' }},
|
||||
updateClock: {
|
||||
bind: this.sinon.stub().returns('component.updateClock')
|
||||
},
|
||||
updateFormatters: {
|
||||
bind: this.sinon.stub().returns('component.updateFormatters')
|
||||
},
|
||||
getTimeformat: function() {
|
||||
mockThis.stream.steps.push('component.getTimeformat');
|
||||
}
|
||||
}
|
||||
};
|
||||
this.sinon.stub(window.Stream.prototype.ready, 'bind').returns('ready');
|
||||
var method = LockScreenClockWidgetTick.prototype.start;
|
||||
method.call(mockThis);
|
||||
assert.include(mockThis.stream.steps, 'component.updateFormatters',
|
||||
`it doesn't check out the timeformat`);
|
||||
assert.include(mockThis.stream.steps, 'component.updateClock',
|
||||
`it doesn't schedule to update the clock`);
|
||||
assert.include(mockThis.stream.steps, 'ready',
|
||||
`it doesn't schedule to become ready`);
|
||||
});
|
||||
|
||||
test(`when restart it should call the restart method`,
|
||||
function() {
|
||||
var mockThis = {
|
||||
configs: {},
|
||||
handleSourceEvent: function() {},
|
||||
component: {
|
||||
_previousState: { configs: { name: 'LockScreenClockWidgetSuspend' }},
|
||||
},
|
||||
restart: this.sinon.stub()
|
||||
};
|
||||
var method = LockScreenClockWidgetTick.prototype.start;
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis.restart.called,
|
||||
`it doesn't call the restart method`);
|
||||
});
|
||||
|
||||
test(`when stop it should stop things`, function() {
|
||||
var mockThis = {
|
||||
stream: new window.Stream(),
|
||||
_minuteSource: {
|
||||
stop: {
|
||||
bind: this.sinon.stub().returns(`bound stop called`)
|
||||
}}
|
||||
};
|
||||
this.sinon.stub(mockThis.stream, 'stop').returns({
|
||||
next: function(cb) {
|
||||
assert.equal(cb, `bound stop called`,
|
||||
`it doesn't stop the source when the state is stopping`);
|
||||
}
|
||||
});
|
||||
var method = LockScreenClockWidgetTick.prototype.stop;
|
||||
method.call(mockThis);
|
||||
});
|
||||
|
||||
test(`when events it would call the corresponding functions`, function() {
|
||||
var mockThis = {
|
||||
component: {
|
||||
updateClock: this.sinon.stub(),
|
||||
updateFormatters: this.sinon.stub(),
|
||||
},
|
||||
transferToSuspend: this.sinon.stub()
|
||||
};
|
||||
var method = LockScreenClockWidgetTick.prototype.handleSourceEvent;
|
||||
|
||||
method.call(mockThis, {'type': 'ftudone'});
|
||||
assert.isTrue(mockThis.component.updateClock.called,
|
||||
`when 'ftudone' it doesn't update the clock`);
|
||||
mockThis.component.updateClock = this.sinon.stub();
|
||||
|
||||
method.call(mockThis, {'type': 'moztimechange'});
|
||||
assert.isTrue(mockThis.component.updateClock.called,
|
||||
`when 'moztimechange' it doesn't update the clock`);
|
||||
mockThis.component.updateClock = this.sinon.stub();
|
||||
|
||||
method.call(mockThis, {'type': 'lockscreen-notification-clock-tick'});
|
||||
assert.isTrue(mockThis.component.updateClock.called,
|
||||
`when 'lockscreen-notification-clock-tick' it doesn't update the clock`);
|
||||
mockThis.component.updateClock = this.sinon.stub();
|
||||
|
||||
method.call(mockThis, {'type': 'screenchange',
|
||||
'detail': {screenEnabled: false} });
|
||||
assert.isTrue(mockThis.transferToSuspend.called,
|
||||
`when 'screenchange' & not screenEnabled it doesn't transfer to suspend`);
|
||||
mockThis.transferToSuspend = this.sinon.stub();
|
||||
|
||||
method.call(mockThis, {'type': 'screenchange',
|
||||
'detail': {screenEnabled: true} });
|
||||
assert.isFalse(mockThis.transferToSuspend.called,
|
||||
`when 'screenchange' & screenEnabled it still transfer to suspend`);
|
||||
mockThis.transferToSuspend = this.sinon.stub();
|
||||
});
|
||||
|
||||
test(`it would call the component's method to transfer to suspend`,
|
||||
function() {
|
||||
window.LockScreenClockWidgetSuspend = {
|
||||
name: 'LockScreenClockWidgetSuspend'
|
||||
};
|
||||
var mockThis = {
|
||||
component: {
|
||||
transferTo: this.sinon.stub()
|
||||
}
|
||||
};
|
||||
var method = LockScreenClockWidgetTick.prototype.transferToSuspend;
|
||||
method.call(mockThis);
|
||||
assert.isTrue(mockThis.component.transferTo.calledWithMatch(function(clz) {
|
||||
return 'LockScreenClockWidgetSuspend' === clz.name;
|
||||
}));
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue