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
27
gaia/apps/clock/test/build/integration/clock_test.js
Executable file
27
gaia/apps/clock/test/build/integration/clock_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Clock tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=clock make', function(done) {
|
||||
helper.exec('APP=clock make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/clock.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
18
gaia/apps/clock/test/marionette/.jshintrc
Executable file
18
gaia/apps/clock/test/marionette/.jshintrc
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"node": true,
|
||||
"predef": [
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"marionette",
|
||||
"marionetteScriptFinished",
|
||||
"require",
|
||||
"define",
|
||||
"requireApp",
|
||||
"sinon"
|
||||
]
|
||||
}
|
||||
241
gaia/apps/clock/test/marionette/alarm_test.js
Executable file
241
gaia/apps/clock/test/marionette/alarm_test.js
Executable file
|
|
@ -0,0 +1,241 @@
|
|||
marionette('Alarm', function() {
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var $ = require('./lib/mquery');
|
||||
var utils = require('./lib/utils');
|
||||
var actions = new (require('./lib/actions'))({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var alarm = actions.alarm;
|
||||
|
||||
setup(function() {
|
||||
actions.launch('alarm');
|
||||
});
|
||||
|
||||
// PythonTests: functional/test_clock_switch_clock_type
|
||||
test('Tapping the clock toggles between analog and digital', function() {
|
||||
$('#analog-clock').tap();
|
||||
assert.ok($('#digital-clock').displayed());
|
||||
assert.ok($('#clock-day-date').displayed());
|
||||
$('#digital-clock').tap();
|
||||
assert.ok($('#analog-clock').displayed());
|
||||
assert.ok($('#clock-day-date').displayed());
|
||||
});
|
||||
|
||||
test('Deleting an alarm works between app launches', function() {
|
||||
alarm.create();
|
||||
alarm.openEditForm(0);
|
||||
alarm.delete();
|
||||
actions.restart();
|
||||
assert.equal(alarm.list.length, 0);
|
||||
});
|
||||
|
||||
// PythonTests: functional/test_clock_set_alarm
|
||||
test('Blank "New Alarm" form mutates properly', function() {
|
||||
alarm.openNewForm();
|
||||
assert.equal($('#repeat-menu').text(), 'Never');
|
||||
|
||||
[
|
||||
{ repeat: ['Monday'],
|
||||
label: 'Mon' },
|
||||
{ repeat: [],
|
||||
label: 'Never' },
|
||||
{ repeat: ['Monday', 'Tuesday'],
|
||||
label: 'Mon, Tue' },
|
||||
{ repeat: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
|
||||
'Friday', 'Saturday'],
|
||||
label: 'Every day' },
|
||||
{ repeat: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
||||
label: 'Weekdays' }
|
||||
].forEach(function(item) {
|
||||
$('#repeat-select').val(item.repeat);
|
||||
assert.equal($('#repeat-menu').text(), item.label);
|
||||
});
|
||||
|
||||
var selectedOptions = {
|
||||
snooze: '15 minutes',
|
||||
sound: 'Cycle',
|
||||
repeat: 'Weekdays'
|
||||
};
|
||||
|
||||
assert.notEqual($('#sound-menu').text(), selectedOptions.sound);
|
||||
$('#sound-select').val(selectedOptions.sound);
|
||||
assert.equal($('#sound-menu').text(), selectedOptions.sound);
|
||||
|
||||
assert.notEqual($('#snooze-menu').text(), selectedOptions.snooze);
|
||||
$('#snooze-select').val(selectedOptions.snooze);
|
||||
assert.equal($('#snooze-menu').text(), selectedOptions.snooze);
|
||||
|
||||
alarm.saveForm();
|
||||
|
||||
alarm.openEditForm(0);
|
||||
assert.equal($('#sound-menu').text(), selectedOptions.sound);
|
||||
assert.equal($('#snooze-menu').text(), selectedOptions.snooze);
|
||||
assert.equal($('#repeat-menu').text(), selectedOptions.repeat);
|
||||
// Ensure that the time button shows the value of the time
|
||||
// <select>, since updating the select does not automatically
|
||||
// change the button unless our code makes it do so.
|
||||
utils.assertStringContainsTime($('#time-select + button').text(),
|
||||
utils.stringToDate($('#time-select').val()));
|
||||
});
|
||||
|
||||
test('Volume control saves immediately when changed', function() {
|
||||
// Even if we abort the alarm_edit form without saving, the volume
|
||||
// should be saved.
|
||||
alarm.openNewForm();
|
||||
alarm.volumeInput.val(1);
|
||||
alarm.cancelForm();
|
||||
|
||||
alarm.openNewForm();
|
||||
assert.equal(parseInt(alarm.volumeInput.val(), 10), 1);
|
||||
|
||||
alarm.volumeInput.val(0);
|
||||
alarm.cancelForm();
|
||||
alarm.openNewForm();
|
||||
assert.equal(parseInt(alarm.volumeInput.val(), 10), 0);
|
||||
});
|
||||
|
||||
// PythonTests: functional/test_clock_add_alarm_multiple_times
|
||||
// PythonTests: functional/test_clock_create_new_alarm
|
||||
test('Multiple saved alarms show the proper data', function() {
|
||||
var i;
|
||||
var numAlarms = 3;
|
||||
var createdAlarms = [];
|
||||
|
||||
// Create these alarms in descending order (latest first)
|
||||
for (i = 0; i < numAlarms; i++) {
|
||||
createdAlarms.push(alarm.create('Alarm ' + i, i + 20));
|
||||
}
|
||||
|
||||
var alarms = alarm.list;
|
||||
assert.equal(alarms.length, numAlarms);
|
||||
alarms.reverse();
|
||||
|
||||
for (i = 0; i < numAlarms; i++) {
|
||||
assert.ok(alarms[i].name === 'Alarm ' + i, 'Alarm name matches');
|
||||
utils.assertStringContainsTime(
|
||||
alarms[i].timeString, createdAlarms[i].time);
|
||||
}
|
||||
});
|
||||
|
||||
test('System statusbar shows an icon when an alarm is active', function() {
|
||||
assert.equal(alarm.statusIcon, false);
|
||||
alarm.create();
|
||||
assert.equal(alarm.statusIcon, true);
|
||||
|
||||
alarm.toggle(0); // turn it off
|
||||
assert.equal(alarm.statusIcon, false);
|
||||
|
||||
alarm.toggle(0); // turn it on
|
||||
assert.equal(alarm.statusIcon, true);
|
||||
|
||||
alarm.remove(0); // delete the alarm
|
||||
assert.equal(alarm.statusIcon, false);
|
||||
assert.equal(alarm.list.length, 0);
|
||||
});
|
||||
|
||||
// PythonTests: functional/test_clock_delete_alarm
|
||||
test('New alarm form shows "New Alarm" title after deletion', function() {
|
||||
alarm.create();
|
||||
alarm.remove(0);
|
||||
alarm.openNewForm();
|
||||
assert.ok($('.new-alarm-title').displayed());
|
||||
});
|
||||
|
||||
test('Update alarm name and time', function() {
|
||||
var originalName = 'coffee break';
|
||||
var originalTime = alarm.create(originalName).time;
|
||||
var changedName = 'quitting time';
|
||||
var changedTime = new Date(originalTime + 1000 * 60);
|
||||
|
||||
alarm.openEditForm(0);
|
||||
|
||||
assert.equal(alarm.nameField.val(), originalName);
|
||||
utils.assertStringContainsTime(alarm.timeField.val(), originalTime);
|
||||
|
||||
alarm.nameField.val(changedName);
|
||||
alarm.timeField.val(new Date(changedTime));
|
||||
|
||||
alarm.saveForm();
|
||||
|
||||
$.client.waitFor(function() {
|
||||
return alarm.list[0].name === changedName;
|
||||
}.bind(this));
|
||||
|
||||
utils.assertStringContainsTime(alarm.list[0].timeString, changedTime);
|
||||
|
||||
// Assert that the banner has been displayed.
|
||||
alarm.waitForBanner();
|
||||
});
|
||||
|
||||
// PythonTests: functional/test_clock_create_new_alarm
|
||||
test('Toggle alarm state', function() {
|
||||
alarm.create();
|
||||
alarm.toggle(0);
|
||||
assert(!alarm.list[0].enabled, 'Alarm is disabled after toggling');
|
||||
assert(!$('#banner-countdown').displayed(),
|
||||
'Banner is not displayed after disabling an alarm');
|
||||
|
||||
alarm.toggle(0);
|
||||
assert(alarm.list[0].enabled, 'Alarm is re-enabled after toggling');
|
||||
});
|
||||
|
||||
test('After snoozing, alarm should be canceled.', function() {
|
||||
// Create an alarm and make it fire.
|
||||
alarm.create();
|
||||
alarm.fire(0, new Date(), function() {
|
||||
// Click the snooze button.
|
||||
var el = $('#ring-button-snooze');
|
||||
try {
|
||||
el.click();
|
||||
} catch(e) {
|
||||
// Marionette throws an error because the frame closes while
|
||||
// handling the click event. This is expected.
|
||||
}
|
||||
});
|
||||
|
||||
// Fire the snooze alarm.
|
||||
alarm.fire(0, new Date(), function() {
|
||||
// Click the "stop" button
|
||||
var el = $('#ring-button-stop');
|
||||
try {
|
||||
el.click();
|
||||
} catch(e) {
|
||||
// Marionette throws an error because the frame closes while
|
||||
// handling the click event. This is expected.
|
||||
}
|
||||
}, 'snooze');
|
||||
|
||||
// The alarm must disable itself after the snooze fires.
|
||||
$.client.waitFor(function() {
|
||||
return !alarm.list[0].enabled;
|
||||
}.bind(this));
|
||||
|
||||
// Make sure we can see the analog clock again.
|
||||
$('#analog-clock').waitToAppear();
|
||||
});
|
||||
|
||||
test('Fire an alarm', function() {
|
||||
alarm.create();
|
||||
alarm.fire(0, new Date(), function() {
|
||||
// Click the "stop" button
|
||||
var el = $('#ring-button-stop');
|
||||
try {
|
||||
el.click();
|
||||
} catch(e) {
|
||||
// Marionette throws an error because the frame closes while
|
||||
// handling the click event. This is expected.
|
||||
}
|
||||
});
|
||||
|
||||
// The alarm must disable itself since it is not repeating.
|
||||
$.client.waitFor(function() {
|
||||
return !alarm.list[0].enabled;
|
||||
}.bind(this));
|
||||
|
||||
// Make sure we can see the analog clock again.
|
||||
$('#analog-clock').waitToAppear();
|
||||
});
|
||||
|
||||
});
|
||||
22
gaia/apps/clock/test/marionette/endurance_test.js
Executable file
22
gaia/apps/clock/test/marionette/endurance_test.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
marionette('Endurance', function() {
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var actions = new (require('./lib/actions'))({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
actions.launch('alarm');
|
||||
});
|
||||
|
||||
// PythonTests: endurance/test_endurance_set_alarm
|
||||
test('Set Alarm Endurance', function() {
|
||||
var totalIterations = 3;
|
||||
for (var iteration = 0; iteration < totalIterations; iteration++) {
|
||||
actions.alarm.create('Iteration ' + iteration, iteration + 10);
|
||||
}
|
||||
assert.equal(actions.alarm.list.length, totalIterations);
|
||||
});
|
||||
|
||||
});
|
||||
60
gaia/apps/clock/test/marionette/hour_format_test.js
Executable file
60
gaia/apps/clock/test/marionette/hour_format_test.js
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
marionette('Hour format', function() {
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var $ = require('./lib/mquery');
|
||||
var actions = new (require('./lib/actions'))({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var alarm = actions.alarm;
|
||||
|
||||
setup(function() {
|
||||
actions.launch('alarm');
|
||||
});
|
||||
|
||||
suite('12 hour format', function() {
|
||||
test('Fire an alarm', function() {
|
||||
var alarmInfo = alarm.create();
|
||||
alarm.fire(0, alarmInfo.time, function() {
|
||||
var expectedDatetime = format12Datetime(alarmInfo.time);
|
||||
assert.equal($('#ring-clock-time').text(), expectedDatetime);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('24 hour format', function() {
|
||||
setup(function() {
|
||||
// Set as 24 hour format.
|
||||
$.client.settings.set('locale.hour12', false);
|
||||
});
|
||||
|
||||
test('Fire an alarm', function() {
|
||||
var alarmInfo = alarm.create();
|
||||
alarm.fire(0, alarmInfo.time, function() {
|
||||
var expectedDatetime = format24Datetime(alarmInfo.time);
|
||||
assert.equal($('#ring-clock-time').text(), expectedDatetime);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function format12Datetime(date) {
|
||||
var minutes = date.getMinutes();
|
||||
minutes = minutes < 10 ? (0 + String(minutes)) : minutes;
|
||||
var hours = date.getHours();
|
||||
var amPm = hours >= 12 ? 'PM' : 'AM';
|
||||
if (hours === 0) {
|
||||
hours = 12;
|
||||
} else if (hours > 12) {
|
||||
hours = hours - 12;
|
||||
}
|
||||
return hours + ':' + minutes + ' ' + amPm;
|
||||
}
|
||||
|
||||
function format24Datetime(date) {
|
||||
var minutes = date.getMinutes();
|
||||
minutes = minutes < 10 ? (0 + String(minutes)) : minutes;
|
||||
var hours = date.getHours();
|
||||
hours = hours < 10 ? '0' + String(hours) :hours;
|
||||
return hours + ':' + minutes;
|
||||
}
|
||||
});
|
||||
100
gaia/apps/clock/test/marionette/lib/actions.js
Executable file
100
gaia/apps/clock/test/marionette/lib/actions.js
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
/* global marionette */
|
||||
'use strict';
|
||||
|
||||
var $ = require('./mquery');
|
||||
var StopwatchActions = require('./stopwatch_actions');
|
||||
var TimerActions = require('./timer_actions');
|
||||
var AlarmActions = require('./alarm_actions');
|
||||
|
||||
function ClockAppActions(options) {
|
||||
this._client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
// we need to disable the keyboard to avoid intermittent failures on
|
||||
// Travis (transitions might take longer to run and block UI).
|
||||
'dom.mozInputMethod.enabled': false,
|
||||
// NOTE: We used to require 'focusmanager.testmode=true' for
|
||||
// B2G-Desktop, but Mulet and Device tests do not require it.
|
||||
}
|
||||
},
|
||||
desiredCapabilities: options && options.desiredCapabilities
|
||||
});
|
||||
|
||||
this.stopwatch = new StopwatchActions(this._client);
|
||||
this.timer = new TimerActions(this._client);
|
||||
this.alarm = new AlarmActions(this._client, this);
|
||||
}
|
||||
|
||||
module.exports = ClockAppActions;
|
||||
|
||||
ClockAppActions.prototype = {
|
||||
origin: 'app://clock.gaiamobile.org',
|
||||
|
||||
launch: function(tab) {
|
||||
$.client = this._client;
|
||||
this._client.apps.launch(this.origin);
|
||||
this._client.apps.switchToApp(this.origin);
|
||||
this._client.helper.waitForElement('#clock-view .visible');
|
||||
|
||||
if (tab && tab !== 'alarm') {
|
||||
this.openTab(tab);
|
||||
}
|
||||
|
||||
// Wait until everything has loaded.
|
||||
this._client.waitFor(function() {
|
||||
return !!this.currentPanelId;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
restart: function(tab) {
|
||||
this._client.apps.close(this.origin);
|
||||
this.launch(tab);
|
||||
},
|
||||
|
||||
get currentPanelId() {
|
||||
var activePanel = $('.panel.active');
|
||||
return activePanel && activePanel.data('panelId');
|
||||
},
|
||||
|
||||
tapAndTransition: function(selector) {
|
||||
var previousPanel = this.currentPanelId;
|
||||
|
||||
if (typeof selector === 'string') {
|
||||
selector = this._client.findElement(selector);
|
||||
}
|
||||
selector.tap();
|
||||
this._client.switchToShadowRoot();
|
||||
|
||||
var self = this;
|
||||
this._client.waitFor(function() {
|
||||
var searchTimeout = self._client.searchTimeout;
|
||||
self._client.setSearchTimeout(0);
|
||||
|
||||
var test = this.currentPanelId &&
|
||||
(previousPanel !== this.currentPanelId) &&
|
||||
!$('.slide-in-right, .slide-in-left')[0];
|
||||
self._client.setSearchTimeout(searchTimeout);
|
||||
return test;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// Application Navigation
|
||||
|
||||
openTab: function(tabName) {
|
||||
// Tap the button to open the tab
|
||||
this.tapAndTransition({
|
||||
alarm: '#alarm-tab',
|
||||
timer: '#timer-tab',
|
||||
stopwatch: '#stopwatch-tab'
|
||||
}[tabName]);
|
||||
|
||||
$('[data-panel-id=' + tabName + ']')
|
||||
.waitToAppear();
|
||||
},
|
||||
|
||||
|
||||
isButtonUsable: function(btn) {
|
||||
return $(btn).attr('disabled') === 'false' && $(btn).displayed();
|
||||
}
|
||||
|
||||
};
|
||||
216
gaia/apps/clock/test/marionette/lib/alarm_actions.js
Executable file
216
gaia/apps/clock/test/marionette/lib/alarm_actions.js
Executable file
|
|
@ -0,0 +1,216 @@
|
|||
'use strict';
|
||||
/* global marionetteScriptFinished */
|
||||
|
||||
var utils = require('./utils');
|
||||
var $ = require('./mquery');
|
||||
|
||||
function AlarmActions(client, actions) {
|
||||
this._client = client;
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
module.exports = AlarmActions;
|
||||
|
||||
AlarmActions.prototype = {
|
||||
|
||||
openNewForm: function() {
|
||||
if ($('#banner-countdown').displayed()) {
|
||||
this.waitForBanner();
|
||||
}
|
||||
this.actions.tapAndTransition('#alarm-new');
|
||||
},
|
||||
|
||||
openEditForm: function(idx) {
|
||||
if ($('#banner-countdown').displayed()) {
|
||||
this.waitForBanner();
|
||||
}
|
||||
this.actions.tapAndTransition(
|
||||
'.alarm-cell:nth-child(' + (idx + 1) + ') .alarm-item');
|
||||
},
|
||||
|
||||
toggle: function(idx) {
|
||||
var alarmWasEnabled = this.list[idx].enabled;
|
||||
|
||||
$('.alarm-cell:nth-child(' + (idx + 1) + ') .alarmEnable span')
|
||||
.tap();
|
||||
this._client.waitFor(function() {
|
||||
return this.list[idx].enabled !== alarmWasEnabled;
|
||||
}.bind(this));
|
||||
|
||||
if (!alarmWasEnabled) {
|
||||
this.waitForBanner();
|
||||
}
|
||||
|
||||
var numAlarmsEnabled = this.list.filter(function(alarm) {
|
||||
return alarm.enabled;
|
||||
}).length;
|
||||
|
||||
// If the number of alarms transitioned from 0 <=> 1, ensure the
|
||||
// system status bar updates before continuing.
|
||||
if (numAlarmsEnabled === 1) {
|
||||
this._client.waitFor(function() {
|
||||
return this.statusIcon;
|
||||
}.bind(this));
|
||||
} else if (numAlarmsEnabled === 0) {
|
||||
this._client.waitFor(function() {
|
||||
return !this.statusIcon;
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
cancelForm: function() {
|
||||
var header = this._client.findElement('gaia-header');
|
||||
this._client.switchToShadowRoot(header);
|
||||
var button = this._client.findElement('.action-button');
|
||||
this.actions.tapAndTransition(button);
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this.actions.tapAndTransition('#alarm-delete');
|
||||
},
|
||||
|
||||
saveForm: function() {
|
||||
this.actions.tapAndTransition('#alarm-done');
|
||||
},
|
||||
|
||||
get nameField() {
|
||||
return $('#edit-alarm [name="alarm.label"]');
|
||||
},
|
||||
|
||||
get timeField() {
|
||||
return $('#time-select');
|
||||
},
|
||||
|
||||
get volumeInput() {
|
||||
return $('#alarm-volume-input');
|
||||
},
|
||||
|
||||
fire: function(idx, date, attentionHandler, type) {
|
||||
// Trigger a fake 'alarm' event:
|
||||
this._client.executeScript(function(idx, date, type) {
|
||||
// String to Date object.
|
||||
date = new Date(date);
|
||||
var id = document.querySelector(
|
||||
'.alarm-cell:nth-child(' + (idx + 1) + ') .alarm-item').dataset.id;
|
||||
var alarm = new CustomEvent('test-alarm', {
|
||||
detail: {
|
||||
id: parseInt(id, 10),
|
||||
type: type || 'normal',
|
||||
date: date
|
||||
}
|
||||
});
|
||||
window.dispatchEvent(alarm);
|
||||
}, [idx, date, type]);
|
||||
|
||||
if (attentionHandler) {
|
||||
// Switch to the Attention Screen
|
||||
this._client.switchToFrame();
|
||||
this._client.switchToFrame($('iframe[name="_blank"]').el);
|
||||
// Wait for the onring page is ready.
|
||||
this._client.waitFor(function() {
|
||||
if (this._client.findElement('html')
|
||||
.getAttribute('class')
|
||||
.indexOf('ready') !== -1) {
|
||||
return true;
|
||||
}
|
||||
}.bind(this));
|
||||
attentionHandler();
|
||||
// Now switch back to the system, then to the clock.
|
||||
this._client.switchToFrame();
|
||||
this._client.apps.switchToApp(this.actions.origin);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function(idx) {
|
||||
var count = this.list.length;
|
||||
this.openEditForm(idx);
|
||||
this.actions.tapAndTransition('#alarm-delete');
|
||||
this._client.waitFor(function() {
|
||||
return this.list.length === count - 1;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
create: function(name, minutesFromNow) {
|
||||
name = name || 'Alarm';
|
||||
if (!minutesFromNow) {
|
||||
minutesFromNow = 20;
|
||||
}
|
||||
|
||||
var originalAlarmCount = this.list.length;
|
||||
|
||||
var now = Date.now();
|
||||
this.openNewForm();
|
||||
|
||||
this.nameField.val(name);
|
||||
this.timeField.val(new Date(now + minutesFromNow));
|
||||
|
||||
var time = new Date();
|
||||
time.setHours.apply(time, this.timeField.val().split(':'));
|
||||
|
||||
this.saveForm();
|
||||
|
||||
var alarmData = null;
|
||||
this._client.waitFor(function() {
|
||||
// Verify that the alarm created is in the list of alarms.
|
||||
this.list.forEach(function(alarm) {
|
||||
if (alarm.name === name && alarm.enabled &&
|
||||
utils.stringContainsTime(alarm.timeString, time)) {
|
||||
alarmData = alarm;
|
||||
}
|
||||
});
|
||||
return !!alarmData;
|
||||
}.bind(this));
|
||||
|
||||
this._client.waitFor(function() {
|
||||
return this.list.length === originalAlarmCount + 1;
|
||||
}.bind(this));
|
||||
|
||||
$('[data-panel-id="alarm"]').waitToAppear();
|
||||
this.waitForBanner();
|
||||
return {
|
||||
name: alarmData.name,
|
||||
time: time,
|
||||
timeString: alarmData.timeString,
|
||||
enabled: alarmData.enabled
|
||||
};
|
||||
},
|
||||
|
||||
get list() {
|
||||
// For now, do this all in one executeScript() call, because the
|
||||
// rendering logic for alarms wipes out all elements at once. It
|
||||
// could cause us to read inconsistent state from the DOM.
|
||||
return this._client.executeScript(function() {
|
||||
var domItems = document.querySelectorAll('.alarm-cell');
|
||||
var alarms = [];
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
alarms.push({
|
||||
name: item.querySelector('.label').textContent,
|
||||
timeString: item.querySelector('.time').textContent,
|
||||
enabled: item.querySelector('.input-enable').checked
|
||||
});
|
||||
}
|
||||
return alarms;
|
||||
});
|
||||
},
|
||||
|
||||
// Status Checks
|
||||
|
||||
get statusIcon() {
|
||||
this._client.setScriptTimeout(2000);
|
||||
return this._client.executeAsyncScript(function() {
|
||||
var req = navigator.mozSettings.createLock().get('alarm.enabled');
|
||||
req.onsuccess = function() {
|
||||
marionetteScriptFinished(req.result['alarm.enabled']);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
waitForBanner: function() {
|
||||
$('#banner-countdown')
|
||||
.tap()
|
||||
.waitToDisappear();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
265
gaia/apps/clock/test/marionette/lib/mquery.js
Executable file
265
gaia/apps/clock/test/marionette/lib/mquery.js
Executable file
|
|
@ -0,0 +1,265 @@
|
|||
'use strict';
|
||||
var Marionette = require('marionette-client');
|
||||
|
||||
/**
|
||||
* mQuery allows you to query and modify the DOM through Marionette,
|
||||
* in a style resembling jQuery. While jQuery is often heckled for
|
||||
* being poor for designing large-scale apps, its quick DOM traversal
|
||||
* and manipulation is hard to beat. For integration tests, which tend
|
||||
* to require convenient, clear, mostly-read-only access to the DOM
|
||||
* with fewer architectural concerns, a jQuery-like API fits well.
|
||||
*
|
||||
* Typical use is something like this:
|
||||
*
|
||||
* var $ = require('mquery');
|
||||
*
|
||||
* $.client = marionette.client();
|
||||
*
|
||||
* console.log($('.my-element').text());
|
||||
* console.log($('#textbox').val());
|
||||
*
|
||||
* You can also set values in input fields like so:
|
||||
*
|
||||
* $('#textbox').val('foo');
|
||||
*
|
||||
* This also works with system <select> boxes and other inputs too.
|
||||
*
|
||||
* Architecturally, mQuery does something interesting: An item
|
||||
* returned from mQuery is _both_ an array of elements _and_ a single
|
||||
* Element at the same time. For instance, you can pass the result of
|
||||
* $('.foo') directly to Marionette calls that require a
|
||||
* Marionette.Element; similarly, since mQuery behaves like an array
|
||||
* as well, you can also use this anywhere you previously used
|
||||
* Marionette.findElements().
|
||||
*
|
||||
* While typical usage assigns $.client to your Marionette.Client
|
||||
* instance, you can instead pass { client: myClient } as an option to
|
||||
* mQuery if you'd prefer not to globally scope out your mQuery
|
||||
* instances.
|
||||
*
|
||||
* Read the code to see what functions are implemented; many of the
|
||||
* most commonly-used jQuery features are included. Add more if you
|
||||
* find them useful. While this initially contains some
|
||||
* half-implemented methods (e.g. ".css()" can only read values, not
|
||||
* set them), the eventual goal is to match jQuery's API where it
|
||||
* makes sense.
|
||||
*/
|
||||
var mQuery = function(selectorOrElement, opts) {
|
||||
var client = (opts && opts.client) || mQuery.client;
|
||||
var parent = (opts && opts.parent) || null;
|
||||
|
||||
if (!client) {
|
||||
throw new Error(
|
||||
'You must set mQuery.client to your Marionette client.');
|
||||
}
|
||||
|
||||
var elements;
|
||||
var selector;
|
||||
if (typeof selectorOrElement === 'string') {
|
||||
elements = (parent || mQuery.client).findElements(selectorOrElement);
|
||||
selector = selectorOrElement;
|
||||
} else if (Array.isArray(selectorOrElement)) {
|
||||
elements = selectorOrElement;
|
||||
} else if (selectorOrElement instanceof ElementSet) {
|
||||
return selectorOrElement;
|
||||
} else if (selectorOrElement instanceof Marionette.Element) {
|
||||
elements = [selectorOrElement];
|
||||
} else {
|
||||
throw new Error('mQuery doesn\'t know how to handle ' + selectorOrElement);
|
||||
}
|
||||
|
||||
return new ElementSet(elements, {
|
||||
client: client,
|
||||
selector: selector
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = mQuery;
|
||||
|
||||
mQuery.client = null;
|
||||
|
||||
/**
|
||||
* ElementSet acts as a lightweight wrapper over Marionette.Element,
|
||||
* providing chainable, functional interface somewhat like jQuery.
|
||||
*/
|
||||
function ElementSet(elements, opts) {
|
||||
this.client = opts.client;
|
||||
this.selector = opts.selector;
|
||||
this.el = elements[0] || null;
|
||||
this.id = this.el && this.el.id; // Act like Marionette if we can.
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
this.push(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Behave like an Array of Marionette.Element instances.
|
||||
ElementSet.prototype = Object.create(Array.prototype);
|
||||
|
||||
// Behave like a single instance of Marionette.Element too.
|
||||
Object.keys(Marionette.Element.prototype).forEach(function(funcName) {
|
||||
var elementFn = Marionette.Element.prototype[funcName];
|
||||
ElementSet.prototype[funcName] = function() {
|
||||
return elementFn.apply(this.el, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
// Behave like jQuery. (These are only getters, not setters though.)
|
||||
ElementSet.prototype.attr = ElementSet.prototype.getAttribute;
|
||||
ElementSet.prototype.css = ElementSet.prototype.cssProperty;
|
||||
|
||||
ElementSet.prototype.toString = function() {
|
||||
return '<ElementSet: ' + this.length + '>';
|
||||
};
|
||||
|
||||
// For the purposes of executeScript, behave like Marionette.Element.
|
||||
ElementSet.prototype.toJSON = function() {
|
||||
return { ELEMENT: this.id };
|
||||
};
|
||||
|
||||
// Chainable helper methods.
|
||||
|
||||
ElementSet.prototype.waitToAppear = function() {
|
||||
this.client.helper.waitForElement(this.el);
|
||||
return this;
|
||||
};
|
||||
|
||||
ElementSet.prototype.waitToDisappear = function() {
|
||||
this.client.helper.waitForElementToDisappear(this.el);
|
||||
return this;
|
||||
};
|
||||
|
||||
function makeChainable(proto, funcName) {
|
||||
var origFn = proto[funcName];
|
||||
proto[funcName] = function() {
|
||||
origFn.apply(this, arguments);
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
makeChainable(ElementSet.prototype, 'clear');
|
||||
makeChainable(ElementSet.prototype, 'sendKeys');
|
||||
|
||||
ElementSet.prototype.tap = function() {
|
||||
this.waitToAppear();
|
||||
this.el.scriptWith(function(el) {
|
||||
el.scrollIntoView(false);
|
||||
});
|
||||
this.el.tap();
|
||||
return this;
|
||||
};
|
||||
|
||||
ElementSet.prototype.find = function(selector) {
|
||||
if (!this.el) {
|
||||
throw new Error(
|
||||
'Cannot call mQuery.find() when mQuery did not match any elements.');
|
||||
}
|
||||
return mQuery(selector, {
|
||||
client: this.client,
|
||||
parent: this.el
|
||||
});
|
||||
};
|
||||
|
||||
ElementSet.prototype.eq = function(idx) {
|
||||
return mQuery(this[idx], {
|
||||
client: this.client
|
||||
});
|
||||
};
|
||||
|
||||
ElementSet.prototype.height = function() {
|
||||
if (!this.el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.el.size().height;
|
||||
};
|
||||
|
||||
ElementSet.prototype.width = function() {
|
||||
if (!this.el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.el.size().width;
|
||||
};
|
||||
|
||||
ElementSet.prototype.parent = function() {
|
||||
if (!this.el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mQuery(this.client.executeScript(function(el) {
|
||||
return el.parentElement;
|
||||
}, [this.el]), {
|
||||
client: this.client
|
||||
});
|
||||
};
|
||||
|
||||
ElementSet.prototype.data = function(attr, value) {
|
||||
if (!this.el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (arguments.length === 1) {
|
||||
return this.client.executeScript(function(el, attr) {
|
||||
return el.dataset[attr];
|
||||
}, [this.el, attr]);
|
||||
} else if (arguments.length === 2) {
|
||||
return this.client.executeScript(function(el, attr, value) {
|
||||
el.dataset[attr] = value;
|
||||
}, [this.el, attr, value]);
|
||||
} else {
|
||||
return this.client.executeScript(function(el) {
|
||||
return el.dataset;
|
||||
}, [this.el]);
|
||||
}
|
||||
};
|
||||
|
||||
ElementSet.prototype.val = function(value) {
|
||||
if (!this.el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var el = this.el;
|
||||
if (!arguments.length) {
|
||||
return el.getAttribute('value');
|
||||
} else {
|
||||
if (el.tagName().toLowerCase() === 'select') {
|
||||
// Save the origin so that we know how to switch back from the
|
||||
// system frame to the application frame.
|
||||
var origin = this.client.executeScript(function() {
|
||||
return window.location.origin;
|
||||
});
|
||||
|
||||
this.client.executeScript(function(el) {
|
||||
window.focus(); // Without this line, sometimes el.focus() does nothing.
|
||||
el.focus();
|
||||
}, [el]);
|
||||
|
||||
this.client.switchToFrame(); // Switch to the system frame.
|
||||
|
||||
var options = this.client.findElements(
|
||||
'.value-selector-select-option-popup li');
|
||||
var values = (!Array.isArray(value) ? [value] : value);
|
||||
options.forEach(function(option) {
|
||||
var itemText = option.findElement('label span').text();
|
||||
var shouldBeSelected = (values.indexOf(itemText) !== -1);
|
||||
var isSelected = (option.getAttribute('aria-selected') === 'true');
|
||||
if (shouldBeSelected !== isSelected) {
|
||||
option.scriptWith(function(el) {
|
||||
el.scrollIntoView(false);
|
||||
});
|
||||
option.tap();
|
||||
}
|
||||
});
|
||||
this.client.helper.waitForElement('.value-option-confirm').tap();
|
||||
this.client.apps.switchToApp(origin);
|
||||
} else {
|
||||
this.client.forms.fill(el, value);
|
||||
}
|
||||
el.scriptWith(function(el) {
|
||||
el.dispatchEvent(new CustomEvent('change'));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
40
gaia/apps/clock/test/marionette/lib/picker.js
Executable file
40
gaia/apps/clock/test/marionette/lib/picker.js
Executable file
|
|
@ -0,0 +1,40 @@
|
|||
'use strict';
|
||||
/* global KeyEvent */
|
||||
|
||||
var $ = require('./mquery');
|
||||
|
||||
exports.getSpinnerValue = function(el) {
|
||||
el = $(el);
|
||||
var offsetTop = /translateY\(-?(\d+)/.exec(el.attr('style'))[1];
|
||||
var pickerItems = el.find('.picker-unit');
|
||||
var itemHeight = pickerItems[0].size().height;
|
||||
var index = Math.floor(parseInt(offsetTop, 10) / itemHeight);
|
||||
return parseInt(pickerItems[index].text(), 10);
|
||||
};
|
||||
|
||||
exports.setSpinnerValue = function(el, value) {
|
||||
el = $(el);
|
||||
var currentValue = exports.getSpinnerValue(el);
|
||||
var targetValue = parseInt(value, 10);
|
||||
var delta = targetValue - currentValue;
|
||||
|
||||
el.parent().scriptWith(function(el, delta) {
|
||||
var count = Math.abs(delta);
|
||||
for (var i = 0; i < count; i++) {
|
||||
var evt = new CustomEvent('keypress');
|
||||
evt.keyCode = delta > 0 ? KeyEvent.DOM_VK_DOWN : KeyEvent.DOM_VK_UP;
|
||||
el.dispatchEvent(evt);
|
||||
}
|
||||
}, [delta]);
|
||||
|
||||
exports.waitForSpinStop(el);
|
||||
};
|
||||
|
||||
exports.waitForSpinStop = function(el) {
|
||||
var currentY, lastY;
|
||||
$.client.waitFor(function() {
|
||||
lastY = currentY;
|
||||
currentY = el.location().y;
|
||||
return currentY === lastY;
|
||||
}, { interval: 200 });
|
||||
};
|
||||
71
gaia/apps/clock/test/marionette/lib/stopwatch_actions.js
Executable file
71
gaia/apps/clock/test/marionette/lib/stopwatch_actions.js
Executable file
|
|
@ -0,0 +1,71 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var $ = require('./mquery');
|
||||
|
||||
function StopwatchActions(client) {
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
module.exports = StopwatchActions;
|
||||
|
||||
StopwatchActions.prototype = {
|
||||
get duration() {
|
||||
return utils.extractDuration($('.stopwatch-time').text());
|
||||
},
|
||||
|
||||
get laps() {
|
||||
return this._client.executeScript(function() {
|
||||
var domItems = document.querySelectorAll('.lap-cell');
|
||||
var laps = [];
|
||||
for (var i = 0; i < domItems.length; i++) {
|
||||
var item = domItems[i];
|
||||
laps.push({
|
||||
name: item.querySelector('.lap-name').textContent,
|
||||
durationString: item.querySelector('.lap-duration').textContent
|
||||
});
|
||||
}
|
||||
return laps;
|
||||
}).map(function(lap) {
|
||||
lap.duration = utils.extractDuration(lap.durationString);
|
||||
return lap;
|
||||
});
|
||||
},
|
||||
|
||||
start: function() {
|
||||
$('.stopwatch-start').tap();
|
||||
this.advanceTime();
|
||||
return this;
|
||||
},
|
||||
|
||||
lap: function() {
|
||||
$('.stopwatch-lap').tap();
|
||||
this.advanceTime();
|
||||
return this;
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
$('.stopwatch-pause').tap();
|
||||
return this;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
$('.stopwatch-reset').tap();
|
||||
return this;
|
||||
},
|
||||
|
||||
setMaxLaps: function(num) {
|
||||
$('[data-panel-id="stopwatch"]').data('maxLaps', num);
|
||||
return this;
|
||||
},
|
||||
|
||||
advanceTime: function(milliseconds) {
|
||||
milliseconds = milliseconds || 1;
|
||||
var expectedDuration = this.duration + milliseconds;
|
||||
this._client.waitFor(function() {
|
||||
return this.duration >= expectedDuration;
|
||||
}.bind(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
57
gaia/apps/clock/test/marionette/lib/timer_actions.js
Executable file
57
gaia/apps/clock/test/marionette/lib/timer_actions.js
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var $ = require('./mquery');
|
||||
var pickerUtils = require('./picker');
|
||||
|
||||
function TimerActions(client) {
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
module.exports = TimerActions;
|
||||
|
||||
TimerActions.prototype = {
|
||||
|
||||
get hours() { return pickerUtils.getSpinnerValue('.picker-hours'); },
|
||||
get minutes() { return pickerUtils.getSpinnerValue('.picker-minutes'); },
|
||||
set hours(value) { pickerUtils.setSpinnerValue('.picker-hours', value); },
|
||||
set minutes(value) { pickerUtils.setSpinnerValue('.picker-minutes', value); },
|
||||
|
||||
get countdown() {
|
||||
return utils.extractDuration($('#timer-time').text());
|
||||
},
|
||||
|
||||
get sound() {
|
||||
return $('#timer-sound').val();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
$('#timer-create').tap();
|
||||
// Wait until the timer has moved from its default state of 0:00
|
||||
this._client.waitFor(function() {
|
||||
return this.countdown > 0;
|
||||
}.bind(this));
|
||||
return this;
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
$('#timer-pause').tap();
|
||||
return this;
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
$('#timer-start').tap();
|
||||
return this;
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
$('#timer-cancel').tap();
|
||||
$('#timer-create').waitToAppear();
|
||||
return this;
|
||||
},
|
||||
|
||||
advanceTime: function(milliseconds) {
|
||||
return this._client.helper.wait(milliseconds);
|
||||
}
|
||||
|
||||
};
|
||||
85
gaia/apps/clock/test/marionette/lib/utils.js
Executable file
85
gaia/apps/clock/test/marionette/lib/utils.js
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
/**
|
||||
* Extract a duration from any point in the given string. See the
|
||||
* inline assertions below for expected usage.
|
||||
*
|
||||
* @return Duration found in milliseconds, or null if none found.
|
||||
*/
|
||||
exports.extractDuration = function(str) {
|
||||
var match = /(?:(\d+):)?(\d+):(\d+)(?:[:.](\d)?(\d)?(\d)?)?/i.exec(str);
|
||||
if (!match) { return null; }
|
||||
var factors = [60 * 60 * 1000, 60 * 1000, 1000, 100, 10, 1];
|
||||
return match.slice(1).reduce(function(sum, value, i) {
|
||||
return sum + ((value || 0) * factors[i]);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
assert.equal(exports.extractDuration('It took 0:00:00.000'), 0);
|
||||
assert.equal(exports.extractDuration('It took 00:00.1'), 100);
|
||||
assert.equal(exports.extractDuration('It took 1:00:00.010'), 3600010);
|
||||
assert.equal(exports.extractDuration('It took 1:05:10.050'), 3910050);
|
||||
assert.equal(exports.extractDuration('It took 30:22:22.55'), 109342550);
|
||||
|
||||
/**
|
||||
* Extract a time from any point in the given string. See the inline
|
||||
* assertions below for expected usage.
|
||||
*
|
||||
* @return An object with { hours: N, minutes: N } in 24-hour format.
|
||||
*/
|
||||
function extractTime(str) {
|
||||
var match = /\b([0-9]+):([0-9]+)\s*(am|pm|a\.m\.|p\.m\.)?/i.exec(str);
|
||||
var hr = parseInt(match[1], 10);
|
||||
var min = parseInt(match[2], 10);
|
||||
var hasAmPm = !!match[3];
|
||||
if (hasAmPm && /p/i.test(match[3])) {
|
||||
if (hr < 12) {
|
||||
hr += 12;
|
||||
}
|
||||
} else {
|
||||
if (hr === 12) {
|
||||
hr = 0;
|
||||
}
|
||||
}
|
||||
return { hours: hr, minutes: min, hasAmPm: hasAmPm };
|
||||
}
|
||||
|
||||
exports.assertStringContainsTime = function(str, date) {
|
||||
var time = extractTime(str);
|
||||
if (time.hasAmPm) {
|
||||
assert.equal(time.hours, date.getHours());
|
||||
} else {
|
||||
assert.equal(time.hours % 12, date.getHours() % 12);
|
||||
}
|
||||
assert.equal(time.minutes, date.getMinutes());
|
||||
};
|
||||
|
||||
exports.stringContainsTime = function(str, date) {
|
||||
try {
|
||||
exports.assertStringContainsTime(str, date);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.stringToDate = function(str) {
|
||||
var time = extractTime(str);
|
||||
var date = new Date();
|
||||
date.setHours(time.hours);
|
||||
date.setMinutes(time.minutes);
|
||||
return date;
|
||||
};
|
||||
|
||||
assert.deepEqual(extractTime('It is 12:30 PM.'),
|
||||
{ hours: 12, minutes: 30, hasAmPm: true });
|
||||
assert.deepEqual(extractTime('It is 12:30 AM.'),
|
||||
{ hours: 0, minutes: 30, hasAmPm: true });
|
||||
assert.deepEqual(extractTime('It is 15:22.'),
|
||||
{ hours: 15, minutes: 22, hasAmPm: false });
|
||||
assert.deepEqual(extractTime('It is 0:00.'),
|
||||
{ hours: 0, minutes: 0, hasAmPm: false });
|
||||
assert.deepEqual(extractTime('It is 6:00 p.m..'),
|
||||
{ hours: 18, minutes: 0, hasAmPm: true });
|
||||
81
gaia/apps/clock/test/marionette/stopwatch_test.js
Executable file
81
gaia/apps/clock/test/marionette/stopwatch_test.js
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
marionette('Stopwatch', function() {
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var actions = new (require('./lib/actions'))();
|
||||
var stopwatch = actions.stopwatch;
|
||||
|
||||
setup(function() {
|
||||
actions.launch('stopwatch');
|
||||
});
|
||||
|
||||
test('Stopwatch Resetting', function() {
|
||||
stopwatch
|
||||
.start()
|
||||
.lap()
|
||||
.lap()
|
||||
.pause();
|
||||
|
||||
assert.equal(actions.stopwatch.laps.length, 3);
|
||||
// During pause, the reset button should be usable.
|
||||
assert.ok(actions.isButtonUsable('.stopwatch-reset'));
|
||||
|
||||
stopwatch.reset();
|
||||
|
||||
// After reset, start should be usable, reset should not.
|
||||
assert.ok(!actions.isButtonUsable('.stopwatch-reset'));
|
||||
assert.ok(actions.isButtonUsable('.stopwatch-start'));
|
||||
|
||||
// Everything should be gone.
|
||||
assert.equal(stopwatch.duration, 0);
|
||||
assert.equal(stopwatch.laps.length, 0);
|
||||
});
|
||||
|
||||
test('Stopwatch Advancement', function() {
|
||||
assert.equal(stopwatch.duration, 0);
|
||||
|
||||
stopwatch.start();
|
||||
|
||||
stopwatch.advanceTime(1300);
|
||||
assert.ok(!actions.isButtonUsable('.stopwatch-start'));
|
||||
assert.ok(stopwatch.duration > 1200);
|
||||
});
|
||||
|
||||
test('Lap advancement and ordering', function() {
|
||||
|
||||
stopwatch
|
||||
.start()
|
||||
.lap();
|
||||
|
||||
assert.equal(stopwatch.laps.length, 2);
|
||||
assert.ok(stopwatch.laps[0].duration > 0);
|
||||
assert.ok(stopwatch.laps[1].duration > 0);
|
||||
|
||||
var originalLaps = stopwatch.laps;
|
||||
|
||||
stopwatch.advanceTime(1300);
|
||||
stopwatch.lap();
|
||||
|
||||
var newLaps = stopwatch.laps;
|
||||
|
||||
// New laps should be inserted first.
|
||||
assert.ok(newLaps[0].duration < newLaps[1].duration);
|
||||
// Old laps should preserve their state.
|
||||
assert.ok(newLaps[1].duration >= originalLaps[0].duration);
|
||||
assert.equal(newLaps[2].duration, originalLaps[1].duration);
|
||||
});
|
||||
|
||||
test('Max Laps', function() {
|
||||
var MAX = 5;
|
||||
stopwatch
|
||||
.setMaxLaps(MAX)
|
||||
.start();
|
||||
|
||||
for (var i = 0; i < MAX - 1; i++) {
|
||||
stopwatch.lap();
|
||||
}
|
||||
|
||||
assert.ok(!actions.isButtonUsable('.stopwatch-lap'));
|
||||
});
|
||||
|
||||
});
|
||||
70
gaia/apps/clock/test/marionette/timer_test.js
Executable file
70
gaia/apps/clock/test/marionette/timer_test.js
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
marionette('Timer', function() {
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var actions = new (require('./lib/actions'))();
|
||||
var timer = actions.timer;
|
||||
|
||||
setup(function() {
|
||||
actions.launch('timer');
|
||||
});
|
||||
|
||||
test('Basic Timer Operation', function() {
|
||||
var durationMs = (6 * 60 + 40) * 60 * 1000;
|
||||
timer.hours = 6;
|
||||
timer.minutes = 40;
|
||||
|
||||
timer.start();
|
||||
|
||||
timer.advanceTime(500);
|
||||
|
||||
// Make sure the default sound is properly selected.
|
||||
assert.equal(timer.sound, 'ac_awake.opus');
|
||||
|
||||
assert.ok(timer.countdown > durationMs - 5000);
|
||||
assert.ok(timer.countdown <= durationMs);
|
||||
|
||||
// Switch away from the timer tab and back.
|
||||
// The timer should still show an appropriate countdown.
|
||||
actions.openTab('alarm');
|
||||
actions.openTab('timer');
|
||||
|
||||
assert.ok(timer.countdown > durationMs - 10000);
|
||||
assert.ok(timer.countdown <= durationMs);
|
||||
});
|
||||
|
||||
test('Timer resume after app termination', function() {
|
||||
var durationMs = 60 * 60 * 1000;
|
||||
timer.hours = 1;
|
||||
timer.minutes = 0;
|
||||
|
||||
timer.start();
|
||||
|
||||
timer.advanceTime(500);
|
||||
|
||||
actions.restart('timer');
|
||||
|
||||
assert.ok(timer.countdown < durationMs);
|
||||
});
|
||||
|
||||
test('Timer Pausing and Restarting', function() {
|
||||
timer.hours = 0;
|
||||
timer.minutes = 20;
|
||||
|
||||
timer.start();
|
||||
timer.pause();
|
||||
|
||||
var pausedValue = timer.countdown;
|
||||
timer.advanceTime(1200);
|
||||
|
||||
// The timer should not advance while paused.
|
||||
assert.equal(timer.countdown, pausedValue);
|
||||
|
||||
timer.resume();
|
||||
timer.advanceTime(1200);
|
||||
|
||||
// The timer should advance when unpaused.
|
||||
assert.ok(timer.countdown < pausedValue);
|
||||
});
|
||||
|
||||
});
|
||||
18
gaia/apps/clock/test/unit/.jshintrc
Executable file
18
gaia/apps/clock/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"testRequire",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
229
gaia/apps/clock/test/unit/alarm_database_test.js
Executable file
229
gaia/apps/clock/test/unit/alarm_database_test.js
Executable file
|
|
@ -0,0 +1,229 @@
|
|||
'use strict';
|
||||
suite('AlarmDatabase Test Suite', function() {
|
||||
|
||||
var alarmDatabase;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['alarm', 'alarm_database'], function(alarm, alarm_database) {
|
||||
alarmDatabase = alarm_database;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('from the string version', function() {
|
||||
var alarmConversions = [
|
||||
[{ normalAlarmId: 7,
|
||||
snoozeAlarmId: 42,
|
||||
label: 'TEST 1',
|
||||
hour: 5,
|
||||
minute: 3,
|
||||
enabled: true,
|
||||
repeat: '1010011',
|
||||
sound: 'ac_awake.opus',
|
||||
vibrate: '1',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'0': true,
|
||||
'2': true,
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
label: 'TEST 1',
|
||||
hour: 5,
|
||||
minute: 3,
|
||||
vibrate: true,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ normalAlarmId: 7,
|
||||
snoozeAlarmId: 42,
|
||||
label: 'TEST 2',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
enabled: true,
|
||||
repeat: '1111111',
|
||||
vibrate: '0',
|
||||
sound: '0',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
label: 'TEST 2',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: null,
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ normalAlarmId: 7,
|
||||
snoozeAlarmId: 42,
|
||||
label: 'TEST 3',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
enabled: true,
|
||||
repeat: '0000000',
|
||||
sound: 'OUTDATED_SOUND_FILE.opus',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: { },
|
||||
label: 'TEST 3',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ normalAlarmId: 7,
|
||||
snoozeAlarmId: 42,
|
||||
label: 'TEST 4',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
enabled: true,
|
||||
repeat: '0101100',
|
||||
sound: 'ac_cycle.opus',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'1': true,
|
||||
'3': true,
|
||||
'4': true
|
||||
},
|
||||
label: 'TEST 4',
|
||||
hour: 15,
|
||||
vibrate: false,
|
||||
minute: 43,
|
||||
sound: 'ac_cycle.opus',
|
||||
snooze: 5 } ]
|
||||
];
|
||||
|
||||
test('convert old alarms to new alarms', function() {
|
||||
alarmConversions.forEach(function(item) {
|
||||
var [oldAlarm, newAlarm] = item;
|
||||
assert.deepEqual(
|
||||
alarmDatabase.normalizeAlarmRecord(oldAlarm), newAlarm);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('from the hardcoded days version', function() {
|
||||
var alarmConversions = [
|
||||
[{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'sunday': true,
|
||||
'tuesday': true,
|
||||
'friday': true,
|
||||
'saturday': true
|
||||
},
|
||||
label: 'TEST 1',
|
||||
hour: 5,
|
||||
minute: 3,
|
||||
vibrate: true,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'0': true,
|
||||
'2': true,
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
label: 'TEST 1',
|
||||
hour: 5,
|
||||
minute: 3,
|
||||
vibrate: true,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'sunday': true,
|
||||
'monday': true,
|
||||
'tuesday': true,
|
||||
'wednesday': true,
|
||||
'thursday': true,
|
||||
'friday': true,
|
||||
'saturday': true
|
||||
},
|
||||
label: 'TEST 2',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: null,
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true
|
||||
},
|
||||
label: 'TEST 2',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: null,
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: { },
|
||||
label: 'TEST 3',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: { },
|
||||
label: 'TEST 3',
|
||||
hour: 15,
|
||||
minute: 43,
|
||||
vibrate: false,
|
||||
sound: 'ac_awake.opus',
|
||||
snooze: 5 } ],
|
||||
|
||||
[{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'monday': true,
|
||||
'wednesday': true,
|
||||
'thursday': true
|
||||
},
|
||||
label: 'TEST 4',
|
||||
hour: 15,
|
||||
vibrate: false,
|
||||
minute: 43,
|
||||
sound: 'ac_cycle.opus',
|
||||
snooze: 5 },
|
||||
{ registeredAlarms: { normal: 7, snooze: 42 },
|
||||
repeat: {
|
||||
'1': true,
|
||||
'3': true,
|
||||
'4': true
|
||||
},
|
||||
label: 'TEST 4',
|
||||
hour: 15,
|
||||
vibrate: false,
|
||||
minute: 43,
|
||||
sound: 'ac_cycle.opus',
|
||||
snooze: 5 } ]
|
||||
];
|
||||
|
||||
test('convert old alarms to new alarms', function() {
|
||||
alarmConversions.forEach(function(item) {
|
||||
var [oldAlarm, newAlarm] = item;
|
||||
assert.deepEqual(
|
||||
alarmDatabase.normalizeAlarmRecord(oldAlarm), newAlarm);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
102
gaia/apps/clock/test/unit/alarm_test.js
Executable file
102
gaia/apps/clock/test/unit/alarm_test.js
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
'use strict';
|
||||
/* global MockIntlHelper */
|
||||
|
||||
suite('Alarm Test', function() {
|
||||
|
||||
var Alarm;
|
||||
var activeAlarm;
|
||||
var alarmDatabase;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
|
||||
require(['alarm', 'alarm_database', 'panels/alarm/active_alarm'],
|
||||
function(alarm, alarm_database, ActiveAlarm) {
|
||||
Alarm = alarm;
|
||||
activeAlarm = new ActiveAlarm();
|
||||
alarmDatabase = alarm_database;
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('Date handling', function() {
|
||||
|
||||
//var now = new Date(1398387324081); // thursday
|
||||
var now = new Date(Date.UTC(2014, 3, 24)); // thursday
|
||||
|
||||
setup(function() {
|
||||
this.alarm = new Alarm({
|
||||
hour: 4,
|
||||
minute: 20,
|
||||
repeat: {
|
||||
monday: true, wednesday: true, friday: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('basic properties and serialization', function() {
|
||||
assert.deepEqual(this.alarm.toJSON(), {
|
||||
id: null,
|
||||
registeredAlarms: {},
|
||||
repeat: { monday: true, wednesday: true, friday: true },
|
||||
hour: 4,
|
||||
minute: 20,
|
||||
label: '',
|
||||
sound: 'ac_awake.opus',
|
||||
vibrate: true,
|
||||
snooze: 10
|
||||
});
|
||||
});
|
||||
|
||||
// Bug 1146263
|
||||
test.skip('alarm is enabled when snoozed', function(done) {
|
||||
this.alarm.schedule('snooze').then(function() {
|
||||
assert.ok(this.alarm.isEnabled());
|
||||
done();
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
// Bug 1146263
|
||||
test.skip('schedule saves alarm', function(done) {
|
||||
var spy = sinon.spy(alarmDatabase, 'put');
|
||||
this.alarm.schedule('normal').then(function() {
|
||||
spy.restore();
|
||||
assert.ok(spy.called);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// Bug 1146263
|
||||
test.skip('cancel saves alarm', function(done) {
|
||||
var spy = sinon.spy(alarmDatabase, 'put');
|
||||
this.alarm.cancel().then(function() {
|
||||
spy.restore();
|
||||
assert.ok(spy.called);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('getNextAlarmFireTime', function() {
|
||||
// Today is thursday; the alarm fires first on Friday:
|
||||
var when = this.alarm.getNextAlarmFireTime(now);
|
||||
assert.equal(when.getDay(), 5); // Friday
|
||||
|
||||
// After Friday, it should fire on Monday at the same time:
|
||||
var secondAlarm = this.alarm.getNextAlarmFireTime(when);
|
||||
assert.equal(secondAlarm.getHours(), when.getHours()); // at the same time
|
||||
assert.equal(secondAlarm.getMinutes(), when.getMinutes());
|
||||
assert.equal(secondAlarm.getDay(), 1); // on Monday
|
||||
// And again on Wednesday
|
||||
var thirdAlarm = this.alarm.getNextAlarmFireTime(secondAlarm);
|
||||
assert.equal(thirdAlarm.getHours(), secondAlarm.getHours());
|
||||
assert.equal(thirdAlarm.getMinutes(), secondAlarm.getMinutes());
|
||||
assert.equal(thirdAlarm.getDay(), 3); // on Wednesday
|
||||
});
|
||||
|
||||
test('getNextSnoozeFireTime', function() {
|
||||
assert.equal(this.alarm.getNextSnoozeFireTime(now).getTime(),
|
||||
now.getTime() + (10 * 60 * 1000));
|
||||
});
|
||||
});
|
||||
});
|
||||
60
gaia/apps/clock/test/unit/async_queue_test.js
Executable file
60
gaia/apps/clock/test/unit/async_queue_test.js
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
|
||||
suite('AsyncQueue', function() {
|
||||
var AsyncQueue;
|
||||
suiteSetup(function(done) {
|
||||
require(['async_queue'], function(async_queue) {
|
||||
AsyncQueue = async_queue;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('runs operations serially', function(done) {
|
||||
// Construct the string "hello", one character at a time. We
|
||||
// assign each task different durations to ensure that no matter
|
||||
// how long each operation takes, the result remains in the same
|
||||
// order as the tasks were added.
|
||||
var result = '';
|
||||
var queue = new AsyncQueue();
|
||||
|
||||
var clock = this.sinon.useFakeTimers();
|
||||
|
||||
function addTask(timeout, character) {
|
||||
queue.push(function(taskFinished) {
|
||||
setTimeout(function() {
|
||||
result += character;
|
||||
taskFinished();
|
||||
}, timeout);
|
||||
clock.tick(10);
|
||||
});
|
||||
}
|
||||
|
||||
addTask(50, 'h');
|
||||
addTask(10, 'e');
|
||||
addTask(0, 'l');
|
||||
addTask(30, 'l');
|
||||
addTask(20, 'o');
|
||||
|
||||
queue.push(function() {
|
||||
assert.equal(result, 'hello');
|
||||
clock.restore();
|
||||
done();
|
||||
});
|
||||
|
||||
clock.tick(100);
|
||||
});
|
||||
|
||||
test('continues after queue is empty', function(done) {
|
||||
var queue = new AsyncQueue();
|
||||
queue.push(function(taskFinished) {
|
||||
taskFinished();
|
||||
setTimeout(function() {
|
||||
queue.push(function() {
|
||||
done();
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
65
gaia/apps/clock/test/unit/audio_manager_test.js
Executable file
65
gaia/apps/clock/test/unit/audio_manager_test.js
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
suite('Audio Manager', function() {
|
||||
'use strict';
|
||||
|
||||
var AudioManager;
|
||||
var navigator_mozSettings = navigator.mozSettings;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['mocks/mock_navigator_mozsettings'], function(mockSettings) {
|
||||
navigator.mozSettings = mockSettings;
|
||||
require(['audio_manager'], function(_AudioManager) {
|
||||
AudioManager = _AudioManager;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozSettings = navigator_mozSettings;
|
||||
});
|
||||
|
||||
function assertVolumeEqual(actual, expected, message) {
|
||||
assert.equal(AudioManager.floatToSystemVolume(actual),
|
||||
AudioManager.floatToSystemVolume(expected));
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
this.audio = AudioManager.createAudioPlayer();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.audio = null;
|
||||
});
|
||||
|
||||
test('AudioPlayer lazy load', function() {
|
||||
assert.equal(this.audio._audio, null);
|
||||
});
|
||||
|
||||
test('AudioPlayer does not throw when uninitialized', function() {
|
||||
this.audio.pause();
|
||||
});
|
||||
|
||||
test('AudioPlayer loads sound with correct settings', function() {
|
||||
this.audio.playRingtone('some_ringtone.opus');
|
||||
assert.equal(this.audio._audio.loop, true);
|
||||
assert.ok(/some_ringtone.opus$/.test(this.audio._audio.src),
|
||||
'audio.src contains the ringtone');
|
||||
assertVolumeEqual(this.audio._audio.volume, AudioManager.getAlarmVolume());
|
||||
});
|
||||
|
||||
test('Volume can be set and read from storage', function(done) {
|
||||
// use a callback to check asynchronously
|
||||
AudioManager.setAlarmVolume(0.5, function() {
|
||||
assertVolumeEqual(AudioManager.getAlarmVolume(), 0.5);
|
||||
done();
|
||||
});
|
||||
// also check synchronously
|
||||
assertVolumeEqual(AudioManager.getAlarmVolume(), 0.5);
|
||||
});
|
||||
|
||||
test('Setting global alarm volume changes AudioPlayer', function() {
|
||||
AudioManager.setAlarmVolume(0.2);
|
||||
this.audio.playRingtone('some_ringtone.opus');
|
||||
assertVolumeEqual(this.audio._audio.volume, 0.2);
|
||||
});
|
||||
});
|
||||
81
gaia/apps/clock/test/unit/banner_test.js
Executable file
81
gaia/apps/clock/test/unit/banner_test.js
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
suite('Banner', function() {
|
||||
'use strict';
|
||||
/* global MockIntlHelper, l10nAssert */
|
||||
var Banner;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
window.IntlHelper.define('digit-nopadding', 'number', {
|
||||
style: 'decimal',
|
||||
useGrouping: false
|
||||
});
|
||||
// The timestamp for "Tue Jul 16 2013 06:00:00" according to the local
|
||||
// system's time zone
|
||||
this.sixAm = new Date(2013, 5, 16, 6).getTime();
|
||||
|
||||
// +12 minutes +20 seconds
|
||||
this.minutes = this.sixAm + 740000;
|
||||
|
||||
// +10 hours +12 minutes +20 seconds
|
||||
this.hours = this.minutes + (10 * 60 * 60 * 1000);
|
||||
|
||||
// +2 day +10 hours +12 minutes +20 seconds
|
||||
this.days = this.hours + (48 * 60 * 60 * 1000);
|
||||
|
||||
// Instantiate the Banner once with an element
|
||||
this.noteElem = document.createElement('div');
|
||||
|
||||
require(['banner/main'], function(banner) {
|
||||
Banner = banner;
|
||||
this.banner = new Banner(this.noteElem);
|
||||
done();
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers(this.sixAm);
|
||||
});
|
||||
|
||||
suite('#show', function() {
|
||||
test('should make notification visible ', function() {
|
||||
var visible;
|
||||
|
||||
this.banner.show(this.hours);
|
||||
visible = this.noteElem.className.split(/\s+/).some(function(elem) {
|
||||
return elem === 'visible';
|
||||
});
|
||||
assert.ok(visible);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#render', function() {
|
||||
test('minutes ahead', function() {
|
||||
// build banner
|
||||
this.banner.show(this.minutes);
|
||||
|
||||
// Check l10n arguments
|
||||
l10nAssert(this.banner.notice.querySelector('p'),
|
||||
'countdown_lessThanAnHour',
|
||||
{minutes: 12});
|
||||
});
|
||||
test('hours ahead', function() {
|
||||
// build banner
|
||||
this.banner.show(this.hours);
|
||||
|
||||
// Check l10n arguments
|
||||
l10nAssert(this.banner.notice.querySelector('p'),
|
||||
'countdown_moreThanAnHour',
|
||||
{minutes: 12, hour: 10});
|
||||
});
|
||||
test('days ahead', function() {
|
||||
// build banner
|
||||
this.banner.show(this.days);
|
||||
|
||||
// Check l10n arguments
|
||||
l10nAssert(this.banner.notice.querySelector('p'),
|
||||
'countdown_moreThanADay',
|
||||
{days: 2, hour: 10});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
210
gaia/apps/clock/test/unit/form_button_test.js
Executable file
210
gaia/apps/clock/test/unit/form_button_test.js
Executable file
|
|
@ -0,0 +1,210 @@
|
|||
'use strict';
|
||||
/* global MockIntlHelper */
|
||||
suite('FormButton', function() {
|
||||
var doc, input, formButton, FormButton, Constants, Utils;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
require([
|
||||
'form_button',
|
||||
'constants',
|
||||
'utils'
|
||||
], function(formButton, constants, utils) {
|
||||
FormButton = formButton;
|
||||
Constants = constants;
|
||||
Utils = utils;
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('basic functions', function() {
|
||||
|
||||
setup(function() {
|
||||
doc = document.createElement('div');
|
||||
doc.innerHTML = '<input type="time" id="time-input"/>';
|
||||
input = doc.querySelector('#time-input');
|
||||
formButton = new FormButton(input);
|
||||
});
|
||||
|
||||
test('should insert a button element into the dom', function() {
|
||||
var buttons = doc.querySelectorAll('button');
|
||||
assert.equal(buttons.length, 1);
|
||||
});
|
||||
|
||||
test('should hide the input', function() {
|
||||
|
||||
assert.isTrue(input.classList.contains('form-button-hide'));
|
||||
});
|
||||
|
||||
test('clicking the button should focus the input', function(done) {
|
||||
this.sinon.stub(input, 'focus', function() {
|
||||
assert.ok(input.focus.called);
|
||||
done();
|
||||
});
|
||||
formButton.button.click();
|
||||
});
|
||||
|
||||
test('refresh should update the button text', function(done) {
|
||||
formButton.input.value = '10:10';
|
||||
formButton.refresh().then(() => {
|
||||
assert.equal(formButton.button.textContent, '10:10');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('formatLabel should be overrideable', function() {
|
||||
var formButton = new FormButton(input, {
|
||||
formatLabel: function() {
|
||||
return 'formatted label';
|
||||
}
|
||||
});
|
||||
assert.equal(formButton.formatLabel(), 'formatted label');
|
||||
});
|
||||
|
||||
test('tagName should set the element type of the button', function() {
|
||||
var formButton = new FormButton(input, {
|
||||
tagName: 'span'
|
||||
});
|
||||
assert.equal(formButton.button.nodeName, 'SPAN');
|
||||
});
|
||||
|
||||
test('className should set the classes of the button', function() {
|
||||
var formButton = new FormButton(input, {
|
||||
className: 'foo bar'
|
||||
});
|
||||
assert.equal(formButton.button.className, 'foo bar');
|
||||
});
|
||||
|
||||
test('id should set the id of the button', function() {
|
||||
var formButton = new FormButton(input, {
|
||||
id: 'my-button'
|
||||
});
|
||||
assert.equal(formButton.button.id, 'my-button');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('when the input property is an input element', function() {
|
||||
|
||||
setup(function() {
|
||||
doc = document.createElement('div');
|
||||
doc.innerHTML = '<input type="time" id="time-input"/>';
|
||||
input = doc.querySelector('#time-input');
|
||||
formButton = new FormButton(input);
|
||||
});
|
||||
|
||||
test('the blur event should update the button text', function(done) {
|
||||
formButton.input.value = '10:11';
|
||||
formButton.input.dispatchEvent(new Event('blur'));
|
||||
Promise.resolve().then(() => {
|
||||
assert.equal(formButton.button.textContent, '10:11');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('getValue should return the current value', function() {
|
||||
formButton.input.value = '10:12';
|
||||
assert.equal(formButton.value, '10:12');
|
||||
});
|
||||
|
||||
|
||||
test('setValue should set the current value', function() {
|
||||
formButton.value = '10:13';
|
||||
assert.equal(formButton.input.value, '10:13');
|
||||
});
|
||||
|
||||
test('isSelect should be false', function() {
|
||||
assert.equal(formButton.isSelect, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when the input property is a select element', function() {
|
||||
|
||||
setup(function() {
|
||||
doc = document.createElement('div');
|
||||
doc.innerHTML = ['<select id="vibrate-select"/>',
|
||||
'<option value="1">On</option>',
|
||||
'<option value="0">Off</option>',
|
||||
'<option value="maybe">Maybe</option>',
|
||||
'</select>'].join('');
|
||||
input = doc.querySelector('#vibrate-select');
|
||||
formButton = new FormButton(input);
|
||||
});
|
||||
|
||||
test('the change event should update the button text', function(done) {
|
||||
Utils.changeSelectByValue(formButton.input, '0');
|
||||
formButton.input.dispatchEvent(new Event('change'));
|
||||
Promise.resolve().then(() => {
|
||||
assert.equal(formButton.button.textContent, '0');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('getValue should return the current value', function() {
|
||||
Utils.changeSelectByValue(formButton.input, '1');
|
||||
assert.equal(formButton.value, '1');
|
||||
});
|
||||
|
||||
|
||||
test('setValue should set the current value', function() {
|
||||
formButton.value = 'maybe';
|
||||
assert.equal(formButton.input.value, 'maybe');
|
||||
});
|
||||
|
||||
test('isSelect should be true', function() {
|
||||
assert.equal(formButton.isSelect, true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when the input property is a select multiple', function() {
|
||||
|
||||
setup(function() {
|
||||
doc = document.createElement('div');
|
||||
doc.innerHTML = ['<select id="repeat-select" multiple="true">',
|
||||
'<option value="1">Monday</option>',
|
||||
'<option value="2">Tuesday</option>',
|
||||
'<option value="3">Wednesday</option>',
|
||||
'<option value="4">Thursday</option>',
|
||||
'<option value="5">Friday</option>',
|
||||
'<option value="6">Saturday</option>',
|
||||
'<option value="0">Sunday</option>',
|
||||
'</select>'].join('');
|
||||
input = doc.querySelector('#repeat-select');
|
||||
formButton = new FormButton(input, {
|
||||
formatLabel: function (value) {
|
||||
return {raw: JSON.stringify(value)};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('the change event should update the button text', function(done) {
|
||||
Utils.changeSelectByValue(formButton.input, '5');
|
||||
formButton.input.dispatchEvent(new Event('change'));
|
||||
Promise.resolve().then(() => {
|
||||
assert.equal(formButton.button.textContent, '{"5":true}');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('getValue should return the current value', function(done) {
|
||||
Utils.changeSelectByValue(formButton.input, '2');
|
||||
Promise.resolve().then(() => {
|
||||
assert.deepEqual(formButton.value, { '2': true });
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
|
||||
test('setValue should set the current value', function() {
|
||||
formButton.value = {'2': true, '4': true, '6': true};
|
||||
var options = formButton.input.options;
|
||||
assert.isFalse(options[0].selected); // monday
|
||||
assert.isTrue(options[1].selected); // tuesday
|
||||
assert.isFalse(options[2].selected); // wednesday
|
||||
assert.isTrue(options[3].selected); // tursday
|
||||
assert.isFalse(options[4].selected); // friday
|
||||
assert.isTrue(options[5].selected); // saturday
|
||||
assert.isFalse(options[6].selected); // sunday
|
||||
});
|
||||
|
||||
test('isSelect should be true', function() {
|
||||
assert.equal(formButton.isSelect, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
98
gaia/apps/clock/test/unit/mocks/mock_moz_alarm.js
Executable file
98
gaia/apps/clock/test/unit/mocks/mock_moz_alarm.js
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
|
||||
var mozAlarmsId = 0;
|
||||
|
||||
function MockOSAlarm(date, respectTimezone, data, callback) {
|
||||
this.date = date || new Date();
|
||||
this.data = data || {};
|
||||
this.callback = callback;
|
||||
this.id = mozAlarmsId++;
|
||||
this.respectTimezone = respectTimezone;
|
||||
this.timeout = setTimeout(function() {
|
||||
if ((typeof this.callback) === 'function') {
|
||||
this.callback({
|
||||
id: this.id,
|
||||
respectTimezone: this.respectTimezone,
|
||||
date: this.date,
|
||||
data: this.data
|
||||
});
|
||||
}
|
||||
}.bind(this), this.date.getTime() - Date.now());
|
||||
}
|
||||
|
||||
function MockMozAlarmRequest(config) {
|
||||
this.init(config || {});
|
||||
}
|
||||
|
||||
MockMozAlarmRequest.prototype = {
|
||||
|
||||
init: function mar_init(config) {
|
||||
setTimeout(function() {
|
||||
this.fire(config.error, config.result);
|
||||
}.bind(this), 0);
|
||||
},
|
||||
|
||||
fire: function mar_call(error, result) {
|
||||
if (!error) {
|
||||
this.onsuccess && this.onsuccess({
|
||||
target: {
|
||||
result: result
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.onerror && this.onerror({
|
||||
target: {
|
||||
error: result
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var messageHandlers = [];
|
||||
navigator.mozSetMessageHandler = function(type, callback) {
|
||||
if (type === 'alarm') {
|
||||
messageHandlers.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mozAlarms = {
|
||||
alarms: [],
|
||||
|
||||
getAll: function() {
|
||||
return new MockMozAlarmRequest({
|
||||
result: this.alarms
|
||||
});
|
||||
},
|
||||
|
||||
add: function(date, timezone, data) {
|
||||
if (!(timezone === 'ignoreTimezone' ||
|
||||
timezone === 'honorTimezone')) {
|
||||
throw new Error('invalid timezone argument');
|
||||
}
|
||||
var alarm = new MockOSAlarm(date, timezone, data, function(x) {
|
||||
messageHandlers.forEach(function(callback) {
|
||||
callback(x);
|
||||
});
|
||||
this.remove(x.id);
|
||||
}.bind(this));
|
||||
this.alarms.push(alarm);
|
||||
return new MockMozAlarmRequest({
|
||||
result: alarm.id
|
||||
});
|
||||
},
|
||||
|
||||
remove: function(id) {
|
||||
for (var i = 0; i < this.alarms.length; i++) {
|
||||
var alarm = this.alarms[i];
|
||||
if (alarm && (alarm.id === id)) {
|
||||
clearTimeout(alarm.timeout);
|
||||
this.alarms.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
52
gaia/apps/clock/test/unit/mocks/mock_moz_l10n.js
Executable file
52
gaia/apps/clock/test/unit/mocks/mock_moz_l10n.js
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
|
||||
function DateTimeFormat() {
|
||||
this.mInitialized = true;
|
||||
}
|
||||
DateTimeFormat.prototype = {
|
||||
localeFormat: function mockLocaleFormat(time, strFormat) {
|
||||
return '' + time;
|
||||
}
|
||||
};
|
||||
|
||||
var testDefaults = {'en-US': {}};
|
||||
var currentLanguage = 'en-US';
|
||||
|
||||
// callbacks to be fired onready
|
||||
var onreadycbs = [];
|
||||
|
||||
return {
|
||||
get: function get(key, params) {
|
||||
var res = testDefaults[currentLanguage];
|
||||
|
||||
if (key in res) {
|
||||
return res[key];
|
||||
}
|
||||
if (params) {
|
||||
return key + JSON.stringify(params);
|
||||
}
|
||||
return key;
|
||||
},
|
||||
ready: function(cb) {
|
||||
setTimeout(cb);
|
||||
onreadycbs.push(cb);
|
||||
},
|
||||
setAttributes: function() {},
|
||||
DateTimeFormat: DateTimeFormat,
|
||||
|
||||
// for unit tests
|
||||
setResources: function(lang, res) {
|
||||
testDefaults[lang] = res;
|
||||
},
|
||||
language: {
|
||||
set code(lang) {
|
||||
currentLanguage = lang;
|
||||
for (var cb of onreadycbs) {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
});
|
||||
45
gaia/apps/clock/test/unit/mocks/mock_navigator_mozsettings.js
Executable file
45
gaia/apps/clock/test/unit/mocks/mock_navigator_mozsettings.js
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
/* global define */
|
||||
define(function() {
|
||||
'use strict';
|
||||
|
||||
var allSettings = {};
|
||||
var settingsObservers = {};
|
||||
|
||||
return {
|
||||
createLock: function() {
|
||||
return {
|
||||
get: function(key) {
|
||||
var ret = { onsuccess: null, result: {} };
|
||||
setTimeout(function() {
|
||||
ret.result[key] = allSettings[key];
|
||||
ret.onsuccess && ret.onsuccess();
|
||||
});
|
||||
return ret;
|
||||
},
|
||||
set: function(settings) {
|
||||
var ret = { onsuccess: null };
|
||||
/* jshint loopfunc:true */
|
||||
for (var key in settings) {
|
||||
allSettings[key] = settings[key];
|
||||
var listeners = settingsObservers[key];
|
||||
if (listeners) {
|
||||
listeners.forEach(function(cb) {
|
||||
cb({ settingValue: settings[key] });
|
||||
});
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
ret.onsuccess && ret.onsuccess();
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
addObserver: function(key, cb) {
|
||||
var listeners = settingsObservers[key] || [];
|
||||
listeners.push(cb);
|
||||
settingsObservers[key] = listeners;
|
||||
},
|
||||
};
|
||||
});
|
||||
45
gaia/apps/clock/test/unit/mocks/mock_request_wake_lock.js
Executable file
45
gaia/apps/clock/test/unit/mocks/mock_request_wake_lock.js
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
define(function() {
|
||||
'use strict';
|
||||
|
||||
function MockLock(type) {
|
||||
this.type = type;
|
||||
this.locked = true;
|
||||
this.unlocks = 0;
|
||||
}
|
||||
|
||||
MockLock.prototype = {
|
||||
unlock: function() {
|
||||
this.locked = false;
|
||||
this.unlocks++;
|
||||
}
|
||||
};
|
||||
|
||||
function MockRequestWakeLock() {
|
||||
this.issued = new Map();
|
||||
}
|
||||
|
||||
MockRequestWakeLock.prototype = {
|
||||
|
||||
requestWakeLock: function mock_requestWakeLock(type) {
|
||||
var lock = new MockLock(type);
|
||||
this.issued.set(lock, true);
|
||||
return lock;
|
||||
},
|
||||
|
||||
getissued: function mock_getissued() {
|
||||
var ret = [];
|
||||
for (var el of this.issued) {
|
||||
ret.push(el[0]);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.issued.clear();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return MockRequestWakeLock;
|
||||
|
||||
});
|
||||
6
gaia/apps/clock/test/unit/mocks/mock_shared/js/accessibility_helper.js
Executable file
6
gaia/apps/clock/test/unit/mocks/mock_shared/js/accessibility_helper.js
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
define(function() {
|
||||
return {
|
||||
setAriaSelected: function() {}
|
||||
};
|
||||
});
|
||||
75
gaia/apps/clock/test/unit/onring_test.js
Executable file
75
gaia/apps/clock/test/unit/onring_test.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
/* global require, document, require, loadBodyHTML */
|
||||
/* global MockIntlHelper, MockMozIntl */
|
||||
|
||||
suite('Ring Test', function() {
|
||||
'use strict';
|
||||
|
||||
var RingView;
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
window.mozIntl = MockMozIntl;
|
||||
require(['ring_view'],
|
||||
function(_RingView) {
|
||||
RingView = _RingView;
|
||||
loadBodyHTML('/onring.html');
|
||||
// Force document.hidden to be 'false' so that the alarm
|
||||
// properly triggers visible callbacks in ring_view.js
|
||||
Object.defineProperty(document, 'hidden', {value: false});
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
[
|
||||
{ vibrate: true, shouldVibrate: true },
|
||||
{ vibrate: '1', shouldVibrate: true },
|
||||
{ vibrate: false, shouldVibrate: false }
|
||||
].forEach(function(testCase) {
|
||||
var { vibrate, shouldVibrate } = testCase;
|
||||
|
||||
test('should ' + (shouldVibrate ? '' : 'not ') +
|
||||
'vibrate when vibrate is set to ' + vibrate, function() {
|
||||
var clock = this.sinon.useFakeTimers();
|
||||
var mock = this.sinon.mock(navigator);
|
||||
if (shouldVibrate) {
|
||||
mock.expects('vibrate').atLeast(1);
|
||||
} else {
|
||||
mock.expects('vibrate').never();
|
||||
}
|
||||
var view = new RingView();
|
||||
view.addAlert({
|
||||
type: 'alarm',
|
||||
vibrate: vibrate,
|
||||
label: 'hi',
|
||||
sound: null,
|
||||
hour: 1,
|
||||
minute: 1,
|
||||
time: new Date(),
|
||||
});
|
||||
clock.tick(5000); // vibrate starts after a setInterval
|
||||
mock.verify();
|
||||
mock.restore();
|
||||
});
|
||||
});
|
||||
|
||||
test('should silence itself after a timeout', function() {
|
||||
var clock = this.sinon.useFakeTimers();
|
||||
var view = new RingView();
|
||||
view.addAlert({
|
||||
type: 'alarm',
|
||||
vibrate: true,
|
||||
label: 'hi',
|
||||
sound: null,
|
||||
hour: 1,
|
||||
minute: 1,
|
||||
time: new Date(),
|
||||
});
|
||||
|
||||
var silenceSpy = this.sinon.spy(view, 'silence');
|
||||
clock.tick(1000); // Wait one second... the alarm should still be ringing
|
||||
assert.isFalse(silenceSpy.called);
|
||||
clock.tick(10 * 60 * 1000); // After 10 minutes, it should be quiet.
|
||||
assert.isTrue(silenceSpy.called);
|
||||
});
|
||||
|
||||
});
|
||||
124
gaia/apps/clock/test/unit/panel_test.js
Executable file
124
gaia/apps/clock/test/unit/panel_test.js
Executable file
|
|
@ -0,0 +1,124 @@
|
|||
'use strict';
|
||||
suite('Panel', function() {
|
||||
var Panel;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['panel'], function(panel) {
|
||||
Panel = panel;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.element = document.createElement('div');
|
||||
this.panel = new Panel(this.element);
|
||||
});
|
||||
|
||||
suite('active', function() {
|
||||
test('starts out active false', function() {
|
||||
assert.isFalse(this.panel.active);
|
||||
});
|
||||
test('class not on element', function() {
|
||||
assert.isFalse(this.element.classList.contains('active'));
|
||||
});
|
||||
|
||||
suite('setting false', function() {
|
||||
setup(function() {
|
||||
this.panel.active = false;
|
||||
});
|
||||
test('class not on element', function() {
|
||||
assert.isFalse(this.element.classList.contains('active'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('setting true', function() {
|
||||
setup(function() {
|
||||
this.panel.active = true;
|
||||
});
|
||||
test('active is true', function() {
|
||||
assert.isTrue(this.panel.active);
|
||||
});
|
||||
test('class on element', function() {
|
||||
assert.isTrue(this.element.classList.contains('active'));
|
||||
});
|
||||
|
||||
suite('setting true', function() {
|
||||
setup(function() {
|
||||
this.panel.active = true;
|
||||
});
|
||||
test('class on element', function() {
|
||||
assert.isTrue(this.element.classList.contains('active'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('setting false', function() {
|
||||
setup(function() {
|
||||
this.panel.active = false;
|
||||
});
|
||||
test('active is false', function() {
|
||||
assert.isFalse(this.panel.active);
|
||||
});
|
||||
test('class not on element', function() {
|
||||
assert.isFalse(this.element.classList.contains('active'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('transition', function() {
|
||||
setup(function() {
|
||||
this.panel.transition = 'test-transition';
|
||||
});
|
||||
test('adds class', function() {
|
||||
assert.isTrue(this.element.classList.contains('test-transition'));
|
||||
});
|
||||
|
||||
suite('change transition', function() {
|
||||
setup(function() {
|
||||
this.panel.transition = 'test-transition-2';
|
||||
});
|
||||
test('removes old class', function() {
|
||||
assert.isFalse(this.element.classList.contains('test-transition'));
|
||||
});
|
||||
test('adds new class', function() {
|
||||
assert.isTrue(this.element.classList.contains('test-transition-2'));
|
||||
});
|
||||
});
|
||||
|
||||
suite('animationend event (not active)', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('animationend', { canBubble: true });
|
||||
this.element.dispatchEvent(event);
|
||||
});
|
||||
test('sets transition to false', function() {
|
||||
assert.isFalse(this.panel.transition);
|
||||
});
|
||||
test('removes class', function() {
|
||||
assert.isFalse(this.element.classList.contains('test-transition'));
|
||||
});
|
||||
test('sets hidden', function() {
|
||||
assert.isTrue(this.element.classList.contains('hidden'));
|
||||
assert.isFalse(this.panel.visible);
|
||||
});
|
||||
});
|
||||
|
||||
suite('animationend event (active)', function() {
|
||||
setup(function() {
|
||||
this.panel.visible = true;
|
||||
this.panel.active = true;
|
||||
var event = new CustomEvent('animationend', { canBubble: true });
|
||||
this.element.dispatchEvent(event);
|
||||
});
|
||||
test('sets transition to false', function() {
|
||||
assert.isFalse(this.panel.transition);
|
||||
});
|
||||
test('removes class', function() {
|
||||
assert.isFalse(this.element.classList.contains('test-transition'));
|
||||
});
|
||||
test('does not set hidden', function() {
|
||||
assert.isFalse(this.element.classList.contains('hidden'));
|
||||
assert.isTrue(this.panel.visible);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
162
gaia/apps/clock/test/unit/panels/alarm/alarm_list_test.js
Executable file
162
gaia/apps/clock/test/unit/panels/alarm/alarm_list_test.js
Executable file
|
|
@ -0,0 +1,162 @@
|
|||
suite('AlarmList', function() {
|
||||
'use strict';
|
||||
|
||||
/** Disabled due to intermittent, see bug 1089543
|
||||
|
||||
var alarm, dom;
|
||||
var alarmListPanel, Alarm, panel;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
// Account for potentially-slow file loading operations
|
||||
this.timeout(60000);
|
||||
|
||||
require([
|
||||
'panels/alarm/main',
|
||||
'panels/alarm/alarm_list',
|
||||
'alarm'
|
||||
], function(AlarmPanel, AlarmListPanel, alarm) {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
panel = new AlarmPanel(div);
|
||||
|
||||
alarmListPanel = new AlarmListPanel(document.createElement('div'));
|
||||
Alarm = alarm;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
alarm = new Alarm({
|
||||
id: 42,
|
||||
hour: 14,
|
||||
minute: 32,
|
||||
label: 'ALARM',
|
||||
registeredAlarms: {
|
||||
normal: 37
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('render()', function() {
|
||||
setup(function() {
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
});
|
||||
|
||||
suite('markup contains correct information', function() {
|
||||
|
||||
test('id', function() {
|
||||
assert.ok(dom.querySelector('[data-id="42"]'));
|
||||
});
|
||||
|
||||
test('enabled', function() {
|
||||
assert.ok(dom.querySelector('input').checked);
|
||||
});
|
||||
|
||||
test('disabled', function() {
|
||||
alarm = new Alarm({
|
||||
hour: 14,
|
||||
minute: 32
|
||||
});
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.ok(!dom.querySelector('input').checked);
|
||||
});
|
||||
|
||||
test('labeled', function() {
|
||||
assert.equal(dom.querySelector('.label').textContent, 'ALARM');
|
||||
});
|
||||
|
||||
test('unlabeled', function() {
|
||||
alarm.label = '';
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.equal(dom.querySelector('.label').textContent, 'alarm');
|
||||
});
|
||||
|
||||
test('repeat', function() {
|
||||
alarm.repeat = { monday: true };
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.equal(
|
||||
dom.querySelector('.repeat').textContent, 'weekday-1-short'
|
||||
);
|
||||
});
|
||||
|
||||
test('no repeat', function() {
|
||||
alarm.label = '';
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.equal(dom.querySelector('.repeat').textContent, '');
|
||||
});
|
||||
|
||||
test('repeat, with-repeat class', function() {
|
||||
alarm.repeat = { monday: true };
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.isTrue(
|
||||
dom.querySelector('.alarm-item').classList.contains('with-repeat')
|
||||
);
|
||||
});
|
||||
|
||||
test('no repeat, without with-repeat class', function() {
|
||||
alarm.label = '';
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.isFalse(
|
||||
dom.querySelector('.alarm-item').classList.contains('with-repeat')
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('toggleAlarmEnableState', function() {
|
||||
|
||||
var origSetEnabled = Alarm.prototype.setEnabled;
|
||||
setup(function() {
|
||||
Alarm.prototype.setEnabled = sinon.spy(function(enabled) {
|
||||
if (enabled) {
|
||||
this.registeredAlarms.normal = 1;
|
||||
} else {
|
||||
delete this.registeredAlarms.normal;
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
this.sinon.spy(alarmListPanel, 'addOrUpdateAlarm');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
Alarm.prototype.setEnabled = origSetEnabled;
|
||||
});
|
||||
|
||||
test('refreshes the list when enabled changes to false', function(done) {
|
||||
alarmListPanel.toggleAlarm(alarm, false, () => {
|
||||
sinon.assert.calledWith(alarmListPanel.addOrUpdateAlarm, alarm);
|
||||
assert.isFalse(alarm.isEnabled());
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.isFalse(dom.querySelector('input').checked);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('refreshes the list when enabled changes to true', function(done) {
|
||||
alarmListPanel.toggleAlarm(alarm, true, () => {
|
||||
sinon.assert.calledWith(alarmListPanel.addOrUpdateAlarm, alarm);
|
||||
assert.isTrue(alarm.isEnabled());
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.isTrue(dom.querySelector('input').checked);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('toggling multiple times works correctly', function(done) {
|
||||
alarmListPanel.toggleAlarm(alarm, true);
|
||||
alarmListPanel.toggleAlarm(alarm, false);
|
||||
alarmListPanel.toggleAlarm(alarm, true);
|
||||
alarmListPanel.toggleAlarm(alarm, false);
|
||||
alarmListPanel.toggleAlarm(alarm, true, () => {
|
||||
sinon.assert.calledWith(alarmListPanel.addOrUpdateAlarm, alarm);
|
||||
assert.isTrue(alarm.isEnabled());
|
||||
dom = alarmListPanel.renderAlarm(alarm);
|
||||
assert.isTrue(dom.querySelector('input').checked);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
});
|
||||
395
gaia/apps/clock/test/unit/panels/alarm/clock_view_test.js
Executable file
395
gaia/apps/clock/test/unit/panels/alarm/clock_view_test.js
Executable file
|
|
@ -0,0 +1,395 @@
|
|||
'use strict';
|
||||
suite('ClockView', function() {
|
||||
|
||||
/** Disabled due to intermittent, see bug 1089543
|
||||
|
||||
|
||||
|
||||
var ClockView;
|
||||
var asyncStorage;
|
||||
var panel;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
// The timestamp for "Tue Jul 16 2013 06:00:00" according to the local
|
||||
// system's time zone
|
||||
this.sixAm = new Date(2013, 5, 16, 6).getTime();
|
||||
|
||||
require([
|
||||
'panels/alarm/main',
|
||||
'panels/alarm/clock_view',
|
||||
'shared/js/async_storage'
|
||||
], function(
|
||||
AlarmPanel,
|
||||
clockView,
|
||||
mockAsyncStorage
|
||||
) {
|
||||
|
||||
// Instantiate an Alarm Panel to ensure that elements are initialized
|
||||
// properly
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
panel = new AlarmPanel(div);
|
||||
|
||||
ClockView = clockView;
|
||||
|
||||
asyncStorage = mockAsyncStorage;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('ClockView.isInitialized ', function() {
|
||||
assert.ok(ClockView.isInitialized);
|
||||
});
|
||||
|
||||
suite('updateDayDate', function() {
|
||||
|
||||
suiteSetup(function() {
|
||||
ClockView.dayDate = document.getElementById('clock-day-date');
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers(this.sixAm);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
ClockView.dayDate.innerHTML = '';
|
||||
});
|
||||
|
||||
test('date element is updated with the current time', function() {
|
||||
ClockView.updateDayDate();
|
||||
assert.equal(Date.parse(ClockView.dayDate.textContent), this.sixAm);
|
||||
});
|
||||
|
||||
test('date element is not updated twice in the same day', function() {
|
||||
ClockView.updateDayDate();
|
||||
this.clock.tick(18 * 60 * 60 * 1000 - 1);
|
||||
assert.equal(Date.parse(ClockView.dayDate.textContent), this.sixAm);
|
||||
});
|
||||
|
||||
test('date element is updated each day', function() {
|
||||
ClockView.updateDayDate();
|
||||
this.clock.tick(18 * 60 * 60 * 1000);
|
||||
assert.equal(Date.parse(ClockView.dayDate.textContent),
|
||||
this.sixAm + 18 * 60 * 60 * 1000);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('updateDigitalClock', function() {
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers(this.sixAm + 1000);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
ClockView.time.innerHTML = '';
|
||||
this.clock.restore();
|
||||
});
|
||||
|
||||
test('time element is updated immediately',
|
||||
function() {
|
||||
ClockView.updateDigitalClock();
|
||||
assert.equal(Date.parse(ClockView.time.innerHTML), this.sixAm + 1000);
|
||||
});
|
||||
|
||||
test('time element is not updated twice in the same minute', function() {
|
||||
ClockView.updateDigitalClock();
|
||||
this.clock.tick(59 * 1000 - 1);
|
||||
assert.equal(Date.parse(ClockView.time.innerHTML), this.sixAm + 1000);
|
||||
});
|
||||
|
||||
test('time element is updated each minute', function() {
|
||||
ClockView.updateDigitalClock();
|
||||
this.clock.tick(59 * 1000);
|
||||
assert.equal(Date.parse(ClockView.time.innerHTML),
|
||||
this.sixAm + 60 * 1000);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('updateAnalogClock', function() {
|
||||
|
||||
suiteSetup(function() {
|
||||
this.second = document.getElementById('secondhand');
|
||||
this.minute = document.getElementById('minutehand');
|
||||
this.hour = document.getElementById('hourhand');
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers(this.sixAm + 1200);
|
||||
});
|
||||
|
||||
function assertRotation(rotate, n) {
|
||||
assert.ok(rotate.style.transform.indexOf('rotate(' + n + 'deg') !== -1,
|
||||
'Expected rotate(' + n + 'deg) for clock hand');
|
||||
}
|
||||
|
||||
test('second-, minute-, and hour- hands are updated immediately',
|
||||
function() {
|
||||
var rotate;
|
||||
ClockView.updateAnalogClock();
|
||||
|
||||
rotate = this.second;
|
||||
assert.ok(rotate, 'Second hand rotation element exists');
|
||||
assertRotation(rotate, 6);
|
||||
|
||||
rotate = this.minute;
|
||||
assert.ok(rotate, 'Minute hand rotation element exists');
|
||||
assertRotation(rotate, 0);
|
||||
|
||||
rotate = this.hour;
|
||||
assert.ok(rotate, 'Hour hand rotation element exists');
|
||||
assertRotation(rotate, 180);
|
||||
});
|
||||
|
||||
test('second-, minute-, and hour- hands are not updated twice in the ' +
|
||||
'same second', function() {
|
||||
var rotate;
|
||||
ClockView.updateAnalogClock();
|
||||
this.clock.tick(799);
|
||||
|
||||
rotate = this.second;
|
||||
assert.ok(rotate, 'Second hand rotation element exists');
|
||||
assertRotation(rotate, 6);
|
||||
|
||||
rotate = this.minute;
|
||||
assert.ok(rotate, 'Minute hand rotation element exists');
|
||||
assertRotation(rotate, 0);
|
||||
|
||||
rotate = this.hour;
|
||||
assert.ok(rotate, 'Hour hand rotation element exists');
|
||||
assertRotation(rotate, 180);
|
||||
});
|
||||
|
||||
test('second-, minute-, and hour- hands are updated each second',
|
||||
function() {
|
||||
var rotate;
|
||||
ClockView.updateAnalogClock();
|
||||
this.clock.tick(800);
|
||||
|
||||
rotate = this.second;
|
||||
assert.ok(rotate, 'Second hand rotation element exists');
|
||||
assertRotation(rotate, 12);
|
||||
|
||||
rotate = this.minute;
|
||||
assert.ok(rotate, 'Minute hand rotation element exists');
|
||||
assertRotation(rotate, 0);
|
||||
|
||||
rotate = this.hour;
|
||||
assert.ok(rotate, 'Hour hand rotation element exists');
|
||||
assertRotation(rotate, 180);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('show', function() {
|
||||
setup(function() {
|
||||
ClockView.mode = 'analog';
|
||||
window.location.hash = 'alarm-view';
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
ClockView.mode = 'analog';
|
||||
});
|
||||
|
||||
test('show() [no mode, no settings, defer to default] ', function() {
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
this.sinon.stub(asyncStorage, 'getItem', function() {
|
||||
return null;
|
||||
});
|
||||
|
||||
// Clear the locally stored "mode"
|
||||
ClockView.mode = null;
|
||||
|
||||
ClockView.show();
|
||||
|
||||
// null => analog (default), asyncStorage.setItem should be called
|
||||
assert.ok(asyncStorage.setItem.called);
|
||||
|
||||
// analog is the default, analog is visible
|
||||
assert.ok(
|
||||
ClockView.analog.classList.contains('visible')
|
||||
);
|
||||
|
||||
// analog is the default, digital is not visible
|
||||
assert.isFalse(
|
||||
ClockView.digital.classList.contains('visible')
|
||||
);
|
||||
});
|
||||
|
||||
test('show() [no mode, defer to settings] ', function() {
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
ClockView.show();
|
||||
|
||||
// Nothing changed, asyncStorage.setItem should not be called
|
||||
assert.isFalse(asyncStorage.setItem.called);
|
||||
|
||||
// Nothing changed, analog is still visible
|
||||
assert.ok(
|
||||
ClockView.analog.classList.contains('visible')
|
||||
);
|
||||
|
||||
// Nothing changed, digital is NOT visible
|
||||
assert.isFalse(
|
||||
ClockView.digital.classList.contains('visible')
|
||||
);
|
||||
});
|
||||
|
||||
test('show(analog) [no mode, defer to settings] ', function() {
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
ClockView.show('analog');
|
||||
|
||||
// Nothing changed, asyncStorage.setItem should not be called
|
||||
assert.isFalse(asyncStorage.setItem.called);
|
||||
|
||||
// Nothing changed, analog is still visible
|
||||
assert.ok(
|
||||
ClockView.analog.classList.contains('visible')
|
||||
);
|
||||
|
||||
// Nothing changed, digital is NOT visible
|
||||
assert.isFalse(
|
||||
ClockView.digital.classList.contains('visible')
|
||||
);
|
||||
});
|
||||
|
||||
test('show(analog) ', function() {
|
||||
ClockView.mode = 'digital';
|
||||
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
ClockView.show('analog');
|
||||
|
||||
// Nothing changed, asyncStorage.setItem should not be called
|
||||
assert.ok(asyncStorage.setItem.called);
|
||||
|
||||
assert.equal(
|
||||
asyncStorage.setItem.args[0][0],
|
||||
'settings_clockoptions_mode'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
asyncStorage.setItem.args[0][1],
|
||||
'analog'
|
||||
);
|
||||
|
||||
// Nothing changed, analog is still visible
|
||||
assert.ok(
|
||||
ClockView.analog.classList.contains('visible')
|
||||
);
|
||||
|
||||
// Nothing changed, digital is NOT visible
|
||||
assert.isFalse(
|
||||
ClockView.digital.classList.contains('visible')
|
||||
);
|
||||
});
|
||||
|
||||
test('show(digital) ', function() {
|
||||
ClockView.mode = 'analog';
|
||||
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
ClockView.show('digital');
|
||||
|
||||
// Updated, asyncStorage.setItem IS called
|
||||
assert.ok(asyncStorage.setItem.called);
|
||||
|
||||
assert.equal(
|
||||
asyncStorage.setItem.args[0][0],
|
||||
'settings_clockoptions_mode'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
asyncStorage.setItem.args[0][1],
|
||||
'digital'
|
||||
);
|
||||
|
||||
// Updated, analog is NOT visible
|
||||
assert.isFalse(
|
||||
ClockView.analog.classList.contains('visible')
|
||||
);
|
||||
|
||||
// Updated, digital IS visible
|
||||
assert.ok(
|
||||
ClockView.digital.classList.contains('visible')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
test('ClockView.analog.click() shows digital ', function(done) {
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
this.sinon.stub(ClockView, 'show', function(mode) {
|
||||
ClockView.show.restore();
|
||||
|
||||
assert.equal(mode, 'digital');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
ClockView.analog.click();
|
||||
});
|
||||
|
||||
test('ClockView.digital.click() shows analog ', function(done) {
|
||||
ClockView.mode = 'digital';
|
||||
|
||||
this.sinon.spy(asyncStorage, 'setItem');
|
||||
|
||||
this.sinon.stub(ClockView, 'show', function(mode) {
|
||||
ClockView.show.restore();
|
||||
|
||||
assert.equal(mode, 'analog');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
ClockView.digital.click();
|
||||
});
|
||||
});
|
||||
|
||||
suite('resizeAnalogClock', function() {
|
||||
|
||||
suiteSetup(function() {
|
||||
this.analogClockContainer = document.getElementById(
|
||||
'analog-clock-container'
|
||||
);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.getAlarmCountStub = this.sinon.stub(ClockView, 'getAlarmCount');
|
||||
});
|
||||
|
||||
test('large size when no alarms in alarms list', function() {
|
||||
this.getAlarmCountStub.returns(0);
|
||||
ClockView.resizeAnalogClock();
|
||||
assert.isTrue(this.analogClockContainer.classList.contains('large'));
|
||||
});
|
||||
|
||||
test('large size when 1 alarm in alarms list', function() {
|
||||
this.getAlarmCountStub.returns(1);
|
||||
ClockView.resizeAnalogClock();
|
||||
assert.isTrue(this.analogClockContainer.classList.contains('large'));
|
||||
});
|
||||
|
||||
test('medium size when 2 alarms in alarms list', function() {
|
||||
this.getAlarmCountStub.returns(2);
|
||||
ClockView.resizeAnalogClock();
|
||||
assert.isTrue(this.analogClockContainer.classList.contains('medium'));
|
||||
});
|
||||
|
||||
test('small size when 3 or more alarms in alarms list', function() {
|
||||
this.getAlarmCountStub.returns(3);
|
||||
ClockView.resizeAnalogClock();
|
||||
assert.isTrue(this.analogClockContainer.classList.contains('small'));
|
||||
|
||||
this.getAlarmCountStub.returns(4);
|
||||
ClockView.resizeAnalogClock();
|
||||
assert.isTrue(this.analogClockContainer.classList.contains('small'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
});
|
||||
270
gaia/apps/clock/test/unit/panels/alarm_edit/alarm_edit_test.js
Executable file
270
gaia/apps/clock/test/unit/panels/alarm_edit/alarm_edit_test.js
Executable file
|
|
@ -0,0 +1,270 @@
|
|||
'use strict';
|
||||
|
||||
/** TEST TEMPORARILY DISABLED, SEE BUG 1033213 ****************************
|
||||
|
||||
|
||||
suite('AlarmEditView', function() {
|
||||
var Alarm, AlarmEdit, activeAlarm, alarmDatabase,
|
||||
alarmListPanel, alarmEdit, panel;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
this.slow(25000);
|
||||
require([
|
||||
'alarm',
|
||||
'panels/alarm/active_alarm',
|
||||
'panels/alarm_edit/main',
|
||||
'alarm_database',
|
||||
'panels/alarm/main',
|
||||
'panels/alarm/alarm_list',
|
||||
], function(alarm, ActiveAlarm, alarmEdit, alarm_database, AlarmPanel,
|
||||
AlarmListPanel) {
|
||||
// Instantiate an Alarm Panel to ensure that elements are initialized
|
||||
// properly
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
panel = new AlarmPanel(div);
|
||||
|
||||
Alarm = alarm;
|
||||
activeAlarm = new ActiveAlarm();
|
||||
AlarmEdit = alarmEdit;
|
||||
alarmDatabase = alarm_database;
|
||||
alarmListPanel = new AlarmListPanel(document.createElement('div'));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
alarmEdit = new AlarmEdit(document.createElement('div'));
|
||||
});
|
||||
|
||||
suite('Alarm persistence', function() {
|
||||
|
||||
setup(function(done) {
|
||||
// Create an Alarm
|
||||
var alarm = alarmEdit.alarm = new Alarm({
|
||||
id: 42,
|
||||
hour: 6,
|
||||
minute: 34
|
||||
});
|
||||
alarm.repeat = {
|
||||
monday: true, wednesday: true, friday: true
|
||||
};
|
||||
alarmEdit.element.dataset.id = alarm.id;
|
||||
|
||||
// shim the edit alarm view
|
||||
alarmEdit.buttons.time.input = alarmEdit.selects.time;
|
||||
alarmEdit.initTimeSelect();
|
||||
|
||||
this.sinon.stub(alarmEdit, 'getTimeSelect');
|
||||
this.sinon.stub(alarmEdit, 'getSoundSelect');
|
||||
this.sinon.stub(alarmEdit, 'getSnoozeSelect');
|
||||
this.sinon.stub(alarmEdit, 'getRepeatSelect');
|
||||
|
||||
// Define the stubs to return the same values set in the
|
||||
// default alarm object.
|
||||
alarmEdit.getTimeSelect.returns({
|
||||
hour: alarm.hour,
|
||||
minute: alarm.minute
|
||||
});
|
||||
alarmEdit.getSoundSelect.returns(alarmEdit.alarm.sound);
|
||||
alarmEdit.checkboxes.vibrate.checked = alarmEdit.alarm.vibrate;
|
||||
alarmEdit.getSnoozeSelect.returns(alarmEdit.alarm.snooze);
|
||||
alarmEdit.getRepeatSelect.returns(alarmEdit.alarm.repeat);
|
||||
|
||||
// Store to alarmsdb
|
||||
alarmDatabase.put(alarm).then(done);
|
||||
});
|
||||
|
||||
test('should save an alarm, no id', function(done) {
|
||||
this.sinon.spy(alarmListPanel, 'addOrUpdateAlarm');
|
||||
this.sinon.stub(alarmListPanel.banner, 'show');
|
||||
|
||||
alarmEdit.alarm = new Alarm({
|
||||
hour: 5,
|
||||
minute: 17,
|
||||
repeat: {
|
||||
monday: true, wednesday: true, friday: true
|
||||
}
|
||||
});
|
||||
alarmEdit.element.dataset.id = '';
|
||||
|
||||
alarmEdit.save(function(err, alarm) {
|
||||
assert.ok(!err);
|
||||
|
||||
// Refreshed AlarmList
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.called, 'addCalledOnce');
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.calledWithExactly(alarm));
|
||||
|
||||
// Rendered BannerBar
|
||||
assert.ok(alarmListPanel.banner.show.called);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should save an alarm, existing id', function(done) {
|
||||
|
||||
this.sinon.stub(alarmListPanel, 'addOrUpdateAlarm');
|
||||
this.sinon.stub(alarmListPanel.banner, 'show');
|
||||
alarmEdit.alarm = new Alarm({
|
||||
id: 1,
|
||||
hour: 5,
|
||||
minute: 17,
|
||||
repeat: {
|
||||
monday: true, wednesday: true, friday: true
|
||||
}
|
||||
});
|
||||
alarmEdit.element.dataset.id = alarmEdit.alarm.id;
|
||||
|
||||
alarmEdit.save(function(err, alarm) {
|
||||
assert.ok(!err);
|
||||
// Refreshed AlarmList
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.called);
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.calledWithExactly(alarm));
|
||||
|
||||
// Rendered BannerBar
|
||||
assert.ok(alarmListPanel.banner.show.called);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('should delete an alarm', function(done) {
|
||||
this.sinon.stub(alarmListPanel, 'removeAlarm');
|
||||
|
||||
alarmEdit.delete(function(err, alarm) {
|
||||
assert.ok(!err, 'delete reported error');
|
||||
assert.ok(alarmListPanel.removeAlarm.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should add an alarm with sound, no vibrate', function(done) {
|
||||
this.sinon.stub(alarmListPanel, 'addOrUpdateAlarm');
|
||||
|
||||
alarmEdit.checkboxes.vibrate.checked = false;
|
||||
|
||||
alarmEdit.alarm = new Alarm({
|
||||
hour: 5,
|
||||
minute: 17,
|
||||
repeat: {
|
||||
monday: true, wednesday: true, friday: true
|
||||
}
|
||||
});
|
||||
alarmEdit.element.dataset.id = '';
|
||||
|
||||
alarmEdit.save(function(err, alarm) {
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.called);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('should update existing alarm with no sound, vibrate', function(done) {
|
||||
this.sinon.stub(alarmListPanel, 'addOrUpdateAlarm');
|
||||
// mock the view to turn sound on and vibrate off
|
||||
alarmEdit.checkboxes.vibrate.checked = true;
|
||||
alarmEdit.getSoundSelect.returns(null);
|
||||
alarmEdit.save(function(err, alarm) {
|
||||
assert.ok(alarm.id);
|
||||
assert.ok(alarmListPanel.addOrUpdateAlarm.called);
|
||||
alarmDatabase.get(alarm.id).then(function(alarm) {
|
||||
assert.equal(alarm.vibrate, true);
|
||||
assert.equal(alarm.sound, null);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
test('should update start of week for l10n, Monday first', function(done) {
|
||||
var sunday = alarmEdit.element.querySelector('#repeat-select-sunday');
|
||||
var parent = sunday.parentElement;
|
||||
// Sunday gets moved to the end.
|
||||
parent.appendChild(sunday);
|
||||
|
||||
mozL10n.language.code = 'en-US';
|
||||
document.l10n.requestLanguages(['en-US']).then(() => {
|
||||
assert.ok(!sunday.previousSibling, 'Sunday should be first (prev)');
|
||||
assert.ok(sunday.nextSibling, 'Sunday should be first (next)');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should update start of week for l10n, Sunday first', function(done) {
|
||||
var sunday = alarmEdit.element.querySelector('#repeat-select-sunday');
|
||||
var parent = sunday.parentElement;
|
||||
// Sunday goes first.
|
||||
parent.insertBefore(sunday, parent.firstChild);
|
||||
|
||||
document.l10n.requestLanguages(['fr']).then(() => {
|
||||
assert.ok(sunday.previousSibling, 'Sunday should be last (prev)');
|
||||
assert.ok(!sunday.nextSibling, 'Sunday should be last (next)');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('initTimeSelect', function() {
|
||||
var alarm;
|
||||
|
||||
setup(function() {
|
||||
alarm = alarmEdit.alarm = new Alarm({});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
alarmEdit.alarm = alarm;
|
||||
});
|
||||
|
||||
test('0:0, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '0';
|
||||
alarmEdit.alarm.minute = '0';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '00:00');
|
||||
});
|
||||
|
||||
test('3:5, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '3';
|
||||
alarmEdit.alarm.minute = '5';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '03:05');
|
||||
});
|
||||
|
||||
test('9:25, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '9';
|
||||
alarmEdit.alarm.minute = '25';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '09:25');
|
||||
});
|
||||
|
||||
test('12:55, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '12';
|
||||
alarmEdit.alarm.minute = '55';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '12:55');
|
||||
});
|
||||
|
||||
test('15:5, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '15';
|
||||
alarmEdit.alarm.minute = '5';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '15:05');
|
||||
});
|
||||
|
||||
test('23:0, should init time select with format of system time picker',
|
||||
function() {
|
||||
alarmEdit.alarm.hour = '23';
|
||||
alarmEdit.alarm.minute = '0';
|
||||
alarmEdit.initTimeSelect();
|
||||
assert.equal(alarmEdit.selects.time.value, '23:00');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
347
gaia/apps/clock/test/unit/panels/stopwatch/stopwatch_panel_test.js
Executable file
347
gaia/apps/clock/test/unit/panels/stopwatch/stopwatch_panel_test.js
Executable file
|
|
@ -0,0 +1,347 @@
|
|||
'use strict';
|
||||
/* global MockL10n, MockMozIntl, MockIntlHelper */
|
||||
suite('Stopwatch.Panel', function() {
|
||||
|
||||
var sevenMinSw, fourSecPausedSw, withLapsSw, runningSw;
|
||||
var isHidden;
|
||||
var panel;
|
||||
var clock;
|
||||
var Stopwatch;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
document.l10n = MockL10n;
|
||||
window.mozIntl = MockMozIntl;
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
var threeMin = 3 * 60 * 1000 + 130;
|
||||
var sevenMin = 7 * 60 * 1000 + 170;
|
||||
var fourSec = 4 * 1000 + 230;
|
||||
var thirtyMins = 30 * 60 * 1000 + 270;
|
||||
|
||||
runningSw = function() {
|
||||
return new Stopwatch({
|
||||
startTime: Date.now(),
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: []
|
||||
});
|
||||
};
|
||||
|
||||
sevenMinSw = function() {
|
||||
return new Stopwatch({
|
||||
startTime: Date.now() - sevenMin,
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: []
|
||||
});
|
||||
};
|
||||
|
||||
fourSecPausedSw = function() {
|
||||
return new Stopwatch({
|
||||
startTime: Date.now(),
|
||||
totalElapsed: fourSec,
|
||||
state: Stopwatch.PAUSED,
|
||||
laps: []
|
||||
});
|
||||
};
|
||||
|
||||
withLapsSw = function() {
|
||||
var sw = new Stopwatch();
|
||||
sw.start();
|
||||
clock.tick(threeMin);
|
||||
panel.update();
|
||||
sw.lap();
|
||||
clock.tick(fourSec);
|
||||
panel.update();
|
||||
sw.lap();
|
||||
clock.tick(thirtyMins + threeMin * 3 + fourSec * 2);
|
||||
panel.update();
|
||||
return sw;
|
||||
};
|
||||
|
||||
isHidden = function(element) {
|
||||
return element.className.contains('hidden');
|
||||
};
|
||||
|
||||
require([
|
||||
'stopwatch', 'panels/stopwatch/main'
|
||||
], function(stopwatch, stopwatchPanel) {
|
||||
Stopwatch = stopwatch;
|
||||
Stopwatch.Panel = stopwatchPanel;
|
||||
panel = new Stopwatch.Panel(
|
||||
document.createElement('div')
|
||||
);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
clock = this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
test('Default', function() {
|
||||
assert.isFalse(isHidden(panel.nodes.start));
|
||||
assert.isFalse(isHidden(panel.nodes.reset));
|
||||
|
||||
assert.isTrue(isHidden(panel.nodes.pause));
|
||||
assert.isTrue(isHidden(panel.nodes.lap));
|
||||
assert.isTrue(isHidden(panel.nodes.resume));
|
||||
});
|
||||
|
||||
test('Seven Minute Running', function(done) {
|
||||
var x = sevenMinSw();
|
||||
panel.setStopwatch(x).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 420170,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
assert.isFalse(isHidden(panel.nodes.pause));
|
||||
assert.isFalse(isHidden(panel.nodes.lap));
|
||||
|
||||
assert.isTrue(isHidden(panel.nodes.start));
|
||||
assert.isTrue(isHidden(panel.nodes.reset));
|
||||
assert.isTrue(isHidden(panel.nodes.resume));
|
||||
}).then(() => {
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 420170 + 3000,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
|
||||
});
|
||||
|
||||
test('Pause a stopwatch', function(done) {
|
||||
panel.setStopwatch(sevenMinSw());
|
||||
panel.nodes.pause.click();
|
||||
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 420170,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Four Second Paused', function(done) {
|
||||
panel.setStopwatch(fourSecPausedSw());
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 4230,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
assert.isFalse(isHidden(panel.nodes.resume));
|
||||
assert.isFalse(isHidden(panel.nodes.reset));
|
||||
|
||||
assert.isTrue(isHidden(panel.nodes.pause));
|
||||
assert.isTrue(isHidden(panel.nodes.start));
|
||||
assert.isTrue(isHidden(panel.nodes.lap));
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 4230,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Resume a stopwatch', function(done) {
|
||||
panel.setStopwatch(fourSecPausedSw());
|
||||
panel.nodes.resume.click();
|
||||
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 7230,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Pre-existing laps', function(done) {
|
||||
var tmp = withLapsSw();
|
||||
panel.setStopwatch(tmp).then(() => {
|
||||
var laps = panel.nodes.laps.querySelectorAll('li');
|
||||
assert.equal(laps.length, 3);
|
||||
|
||||
assert.equal(laps[0].children[1].textContent, JSON.stringify({
|
||||
value: 2349120,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
|
||||
assert.equal(laps[1].children[1].textContent, JSON.stringify({
|
||||
value: 4230,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
|
||||
assert.equal(laps[2].children[1].textContent, JSON.stringify({
|
||||
value: 180130,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Add laps', function(done) {
|
||||
|
||||
function getLapInfo() {
|
||||
var laps = panel.nodes.laps.querySelectorAll('li');
|
||||
laps = Array.prototype.slice.call(laps);
|
||||
return laps.map(function(e) {
|
||||
return {
|
||||
lapName: e.children[0],
|
||||
lapTime: e.children[1].textContent.trim()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
panel.setStopwatch(runningSw()).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 0,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(() => {
|
||||
//Advance and click the lap button
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 3000,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
panel.nodes.lap.click();
|
||||
}).then(() => {
|
||||
var laps = getLapInfo();
|
||||
assert.equal(laps.length, 2);
|
||||
|
||||
assert.equal(laps[1].lapTime, JSON.stringify({
|
||||
value: 3000,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
|
||||
//Advance and add another lap
|
||||
clock.tick(9000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 9000 + 3000,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
|
||||
panel.nodes.lap.click();
|
||||
}).then(() => {
|
||||
var laps = getLapInfo();
|
||||
assert.equal(laps.length, 3);
|
||||
|
||||
assert.equal(laps[1].lapTime, JSON.stringify({
|
||||
value: 9000,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Reset stopwatch', function(done) {
|
||||
var laps;
|
||||
|
||||
panel.setStopwatch(runningSw()).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 0,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
//Advance and click the lap button (twice)
|
||||
clock.tick(3000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update();
|
||||
panel.nodes.lap.click();
|
||||
clock.tick(9000);
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update();
|
||||
panel.nodes.lap.click();
|
||||
}).then(() => {
|
||||
laps = panel.nodes.laps.querySelectorAll('li');
|
||||
assert.equal(laps.length, 3);
|
||||
|
||||
//Reset
|
||||
panel.nodes.pause.click();
|
||||
panel.nodes.reset.click();
|
||||
}).then(() => {
|
||||
laps = panel.nodes.laps.querySelectorAll('li');
|
||||
assert.equal(laps.length, 0);
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 0,
|
||||
options: {
|
||||
minUnit: 'millisecond',
|
||||
maxUnit: 'minute'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Stopwatch 100 minutes display', function(done) {
|
||||
panel.setStopwatch(runningSw());
|
||||
|
||||
assert.ok(!panel.nodes.time.classList.contains('over-100-minutes'));
|
||||
|
||||
clock.tick(1000 * 60 * 105); // over 100 minutes
|
||||
// required due to requestAnimationFrame not being called
|
||||
panel.update().then(() => {
|
||||
assert.ok(panel.nodes.time.classList.contains('over-100-minutes'));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
});
|
||||
390
gaia/apps/clock/test/unit/panels/timer/timer_panel_test.js
Executable file
390
gaia/apps/clock/test/unit/panels/timer/timer_panel_test.js
Executable file
|
|
@ -0,0 +1,390 @@
|
|||
'use strict';
|
||||
/* global asyncStorage, MockL10n, MockMozIntl, MockIntlHelper */
|
||||
|
||||
suite('Timer.Panel', function() {
|
||||
var clock, activeAlarm;
|
||||
var isHidden;
|
||||
var View, Timer, Utils;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
document.l10n = MockL10n;
|
||||
window.mozIntl = MockMozIntl;
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
window.IntlHelper.define('digit-nopadding', 'number', {
|
||||
style: 'decimal',
|
||||
useGrouping: false
|
||||
});
|
||||
isHidden = function(element) {
|
||||
return element.className.contains('hidden');
|
||||
};
|
||||
|
||||
require(['panels/alarm/active_alarm', 'timer', 'panels/timer/main',
|
||||
'view', 'utils'],
|
||||
function(ActiveAlarm, timer, timerPanel, view, utils) {
|
||||
Timer = timer;
|
||||
Timer.Panel = timerPanel;
|
||||
View = view;
|
||||
Utils = utils;
|
||||
activeAlarm = new ActiveAlarm();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
clock = this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
test('shape:prototype ', function() {
|
||||
assert.ok(Timer.Panel);
|
||||
assert.ok(Timer.Panel.prototype.showDialog);
|
||||
assert.ok(Timer.Panel.prototype.hideDialog);
|
||||
assert.ok(Timer.Panel.prototype.update);
|
||||
assert.ok(Timer.Panel.prototype.onclick);
|
||||
});
|
||||
|
||||
test('shape:instance ', function() {
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
assert.ok(panel.nodes);
|
||||
assert.isNull(panel.timer);
|
||||
});
|
||||
|
||||
test('dialog ', function() {
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
var dialog = View.instance(panel.nodes.dialog);
|
||||
|
||||
panel.showDialog();
|
||||
|
||||
assert.isTrue(dialog.visible);
|
||||
|
||||
panel.hideDialog();
|
||||
|
||||
assert.isFalse(dialog.visible);
|
||||
});
|
||||
|
||||
test('update ', function(done) {
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
|
||||
// The timer panel should display rounded seconds.
|
||||
var timer = { remaining: 10000 };
|
||||
panel.timer = timer;
|
||||
panel.update().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: 10000,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('toggleButtons ', function() {
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
var start = panel.nodes.start;
|
||||
var pause = panel.nodes.pause;
|
||||
|
||||
panel.timer = {};
|
||||
|
||||
panel.timer.state = Timer.PAUSED;
|
||||
panel.toggleButtons();
|
||||
|
||||
assert.isFalse(isHidden(start));
|
||||
assert.isTrue(isHidden(pause));
|
||||
|
||||
panel.timer.state = Timer.STARTED;
|
||||
panel.toggleButtons();
|
||||
|
||||
assert.isTrue(isHidden(start));
|
||||
assert.isFalse(isHidden(pause));
|
||||
});
|
||||
|
||||
test('Set timer state (paused)', function(done) {
|
||||
var now = Date.now();
|
||||
var oneHour = 60 * 60 * 1000;
|
||||
var timer = new Timer({
|
||||
startTime: now,
|
||||
configuredDuration: oneHour
|
||||
});
|
||||
timer.start();
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
panel.timer = timer;
|
||||
panel.onvisibilitychange({ detail: { isVisible: true } });
|
||||
|
||||
panel.update().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
clock.tick(5000);
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour - 5000,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
|
||||
panel.onclick({
|
||||
target: panel.nodes.pause
|
||||
});
|
||||
panel.onTimerEvent({ type: 'timer-pause' });
|
||||
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.isTrue(isHidden(panel.nodes.dialog));
|
||||
assert.isTrue(isHidden(panel.nodes.pause));
|
||||
|
||||
assert.isFalse(isHidden(panel.nodes.start));
|
||||
assert.isFalse(isHidden(panel.nodes.time));
|
||||
assert.isFalse(isHidden(panel.nodes.cancel));
|
||||
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour - 5000,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
clock.tick(5000);
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour - 5000,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Set timer state (started)', function(done) {
|
||||
var now = Date.now();
|
||||
var oneHour = 60 * 60 * 1000;
|
||||
var timer = new Timer({
|
||||
configuredDuration: now + oneHour
|
||||
});
|
||||
timer.start();
|
||||
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
panel.timer = timer;
|
||||
panel.onvisibilitychange({ detail: { isVisible: true } });
|
||||
|
||||
assert.isTrue(isHidden(panel.nodes.dialog));
|
||||
assert.isTrue(isHidden(panel.nodes.start));
|
||||
|
||||
assert.isFalse(isHidden(panel.nodes.time));
|
||||
assert.isFalse(isHidden(panel.nodes.pause));
|
||||
assert.isFalse(isHidden(panel.nodes.cancel));
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
clock.tick(5000);
|
||||
return panel.update();
|
||||
}).then(() => {
|
||||
assert.equal(panel.nodes.time.textContent, JSON.stringify({
|
||||
value: oneHour - 5000,
|
||||
options: {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'hour'
|
||||
}
|
||||
}));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Create button is disabled when picker is set to 0:00', function() {
|
||||
var panel = new Timer.Panel(document.createElement('div'));
|
||||
var create = panel.nodes.create;
|
||||
var nodes = panel.picker.nodes;
|
||||
|
||||
create = {
|
||||
setAttribute: function() {},
|
||||
removeAttribute: function() {}
|
||||
};
|
||||
|
||||
this.sinon.spy(create, 'removeAttribute');
|
||||
this.sinon.spy(create, 'setAttribute');
|
||||
|
||||
assert.isTrue(panel.nodes.create.disabled);
|
||||
|
||||
panel.picker.value = '3:00';
|
||||
nodes.hours.dispatchEvent(
|
||||
new CustomEvent('transitionend')
|
||||
);
|
||||
|
||||
assert.isFalse(panel.nodes.create.disabled);
|
||||
|
||||
panel.picker.value = '0:00';
|
||||
nodes.hours.dispatchEvent(
|
||||
new CustomEvent('transitionend')
|
||||
);
|
||||
|
||||
assert.isTrue(panel.nodes.create.disabled);
|
||||
|
||||
panel.picker.value = '0:15';
|
||||
nodes.minutes.dispatchEvent(
|
||||
new CustomEvent('transitionend')
|
||||
);
|
||||
|
||||
assert.isFalse(panel.nodes.create.disabled);
|
||||
|
||||
panel.picker.value = '0:00';
|
||||
nodes.minutes.dispatchEvent(
|
||||
new CustomEvent('transitionend')
|
||||
);
|
||||
|
||||
assert.isTrue(panel.nodes.create.disabled);
|
||||
});
|
||||
|
||||
suite('Timer.Panel, Events', function() {
|
||||
var panel;
|
||||
|
||||
setup(function() {
|
||||
this.sinon.spy(Timer.Panel.prototype, 'onclick');
|
||||
|
||||
asyncStorage.getItem = function(key, callback) {
|
||||
callback({duration: 5000});
|
||||
};
|
||||
|
||||
panel = new Timer.Panel(document.createElement('div'));
|
||||
|
||||
this.sinon.spy(panel.timer, 'start');
|
||||
this.sinon.spy(panel.timer, 'pause');
|
||||
this.sinon.spy(panel.timer, 'cancel');
|
||||
this.sinon.spy(panel.nodes.sound, 'focus');
|
||||
this.sinon.spy(panel.timer, 'plus');
|
||||
});
|
||||
|
||||
test('click: start ', function() {
|
||||
var start = panel.nodes.start;
|
||||
|
||||
start.dispatchEvent(
|
||||
new CustomEvent('click')
|
||||
);
|
||||
|
||||
assert.ok(panel.onclick.called);
|
||||
assert.ok(panel.timer.start.called);
|
||||
});
|
||||
|
||||
test('click: pause ', function() {
|
||||
var pause = panel.nodes.pause;
|
||||
|
||||
pause.dispatchEvent(
|
||||
new CustomEvent('click')
|
||||
);
|
||||
|
||||
assert.ok(panel.onclick.called);
|
||||
assert.ok(panel.timer.pause.called);
|
||||
});
|
||||
|
||||
test('click: cancel ', function() {
|
||||
var cancel = panel.nodes.cancel;
|
||||
var timer = panel.timer;
|
||||
|
||||
cancel.dispatchEvent(
|
||||
new CustomEvent('click')
|
||||
);
|
||||
assert.ok(panel.onclick.called);
|
||||
assert.ok(timer.cancel.called);
|
||||
});
|
||||
|
||||
test('click: create', function() {
|
||||
panel.picker.value = '1:00';
|
||||
|
||||
panel.picker.nodes.hours.dispatchEvent(
|
||||
new CustomEvent('transitionend')
|
||||
);
|
||||
|
||||
panel.nodes.create.dispatchEvent(
|
||||
new CustomEvent('click')
|
||||
);
|
||||
assert.ok(panel.onclick.called);
|
||||
// Duration from picker value
|
||||
assert.equal(panel.timer.duration, 3600000);
|
||||
});
|
||||
|
||||
test('click: plus ', function() {
|
||||
var plus = panel.nodes.plus;
|
||||
var timer = panel.timer;
|
||||
|
||||
|
||||
plus.dispatchEvent(
|
||||
new CustomEvent('click')
|
||||
);
|
||||
assert.ok(panel.onclick.called);
|
||||
assert.ok(timer.plus.called);
|
||||
});
|
||||
|
||||
test('blur: sound', function(done) {
|
||||
var menu = panel.soundButton.button;
|
||||
var sound = panel.nodes.sound;
|
||||
Utils.changeSelectByValue(sound, 'ac_digicloud.opus');
|
||||
sound.dispatchEvent(
|
||||
new CustomEvent('blur')
|
||||
);
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
assert.equal(menu.getAttribute('data-l10n-id'), 'ac_digicloud_opus');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('change: sound', function() {
|
||||
var sound = panel.nodes.sound;
|
||||
Utils.changeSelectByValue(sound, 'ac_digicloud.opus');
|
||||
var mockAudio = {
|
||||
pause: this.sinon.spy(),
|
||||
play: this.sinon.spy(),
|
||||
addEventListener: function() { },
|
||||
load: function() { }
|
||||
};
|
||||
this.sinon.stub(window, 'Audio').returns(mockAudio);
|
||||
|
||||
sound.dispatchEvent(
|
||||
new CustomEvent('change')
|
||||
);
|
||||
|
||||
assert.isTrue(mockAudio.play.called);
|
||||
assert.isTrue(mockAudio.loop);
|
||||
assert.equal(mockAudio.mozAudioChannelType, 'alarm');
|
||||
var expected = 'shared/resources/media/alarms/ac_digicloud.opus';
|
||||
assert.equal(mockAudio.src, expected);
|
||||
});
|
||||
|
||||
test('blur: pause playing alarm', function() {
|
||||
var sound = panel.nodes.sound;
|
||||
Utils.changeSelectByValue(sound, 'ac_digicloud.opus');
|
||||
|
||||
var mockAudio = {
|
||||
pause: this.sinon.spy(),
|
||||
play: this.sinon.spy(),
|
||||
addEventListener: function() { },
|
||||
load: function() { }
|
||||
};
|
||||
this.sinon.stub(window, 'Audio').returns(mockAudio);
|
||||
|
||||
sound.dispatchEvent(
|
||||
new CustomEvent('change')
|
||||
);
|
||||
|
||||
assert.isTrue(mockAudio.play.called);
|
||||
assert.isTrue(mockAudio.pause.calledOnce);
|
||||
|
||||
sound.dispatchEvent(
|
||||
new CustomEvent('blur')
|
||||
);
|
||||
|
||||
assert.isTrue(mockAudio.pause.calledTwice);
|
||||
});
|
||||
});
|
||||
});
|
||||
85
gaia/apps/clock/test/unit/picker/picker_test.js
Executable file
85
gaia/apps/clock/test/unit/picker/picker_test.js
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
/* global MockIntlHelper */
|
||||
|
||||
suite('Picker', function() {
|
||||
var Picker, Spinner;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
window.IntlHelper.define('digit-nopadding', 'number', {
|
||||
style: 'decimal',
|
||||
useGrouping: false
|
||||
});
|
||||
require(['picker/picker', 'picker/spinner'],
|
||||
function(picker, spinner) {
|
||||
Picker = picker;
|
||||
Spinner = spinner;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('shape:prototype ', function() {
|
||||
assert.ok(Picker);
|
||||
assert.typeOf(Picker.prototype.reset, 'function');
|
||||
assert.typeOf(Picker.prototype.value, 'null');
|
||||
assert.isNull(Picker.prototype.value);
|
||||
});
|
||||
|
||||
var picker;
|
||||
setup(function() {
|
||||
var pickerEl = document.createElement('div');
|
||||
pickerEl.appendChild(document.createElement('div'))
|
||||
.classList.add('picker-hours');
|
||||
pickerEl.appendChild(document.createElement('div'))
|
||||
.classList.add('picker-minutes');
|
||||
picker = new Picker({
|
||||
element: pickerEl,
|
||||
pickers: {
|
||||
hours: {
|
||||
range: [0, 23]
|
||||
},
|
||||
minutes: {
|
||||
range: [0, 59],
|
||||
isPadded: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('shape:instance ', function() {
|
||||
assert.typeOf(picker.nodes, 'object');
|
||||
assert.typeOf(picker.spinners, 'object');
|
||||
assert.typeOf(picker.pickers, 'Array');
|
||||
|
||||
assert.notEqual(typeof picker.nodes.hours, 'undefined');
|
||||
assert.notEqual(typeof picker.nodes.minutes, 'undefined');
|
||||
|
||||
assert.notEqual(typeof picker.spinners.hours, 'undefined');
|
||||
assert.notEqual(typeof picker.spinners.hours, 'undefined');
|
||||
|
||||
assert.deepEqual(picker.pickers, ['hours', 'minutes']);
|
||||
});
|
||||
|
||||
test('values ', function() {
|
||||
var spinners = picker.spinners;
|
||||
|
||||
assert.equal(spinners.hours.values[0], 0);
|
||||
assert.equal(spinners.hours.values[23], 23);
|
||||
assert.equal(spinners.hours.values[24], undefined);
|
||||
|
||||
assert.equal(spinners.minutes.values[0], 0);
|
||||
assert.equal(spinners.minutes.values[59], 59);
|
||||
assert.equal(spinners.minutes.values[60], undefined);
|
||||
|
||||
assert.equal(picker.value, '0:0');
|
||||
});
|
||||
|
||||
test('get and set value ', function() {
|
||||
picker.value = '1:59';
|
||||
|
||||
var spinners = picker.spinners;
|
||||
|
||||
assert.equal(spinners.hours.value, 1);
|
||||
assert.equal(spinners.minutes.value, 59);
|
||||
});
|
||||
});
|
||||
396
gaia/apps/clock/test/unit/picker/spinner_test.js
Executable file
396
gaia/apps/clock/test/unit/picker/spinner_test.js
Executable file
|
|
@ -0,0 +1,396 @@
|
|||
suite('Spinner', function() {
|
||||
'use strict';
|
||||
/* global MockIntlHelper, IntlHelper */
|
||||
|
||||
var Spinner, GestureDetector;
|
||||
var _clientHeight;
|
||||
var VALUE_ELEMENT_HEIGHT = 20;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
IntlHelper.define('digit-nopadding', 'number', {
|
||||
style: 'decimal',
|
||||
useGrouping: false
|
||||
});
|
||||
// override native clientHeight
|
||||
_clientHeight = Object.getOwnPropertyDescriptor(
|
||||
Element.prototype, 'clientHeight');
|
||||
Object.defineProperty(Element.prototype, 'clientHeight', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function clockTestClientHeightStub() {
|
||||
if (this.classList.contains('picker-unit')) {
|
||||
return VALUE_ELEMENT_HEIGHT;
|
||||
}
|
||||
return _clientHeight.get.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
require([
|
||||
'picker/spinner',
|
||||
'shared/js/gesture_detector'
|
||||
], function(spinner, GD) {
|
||||
Spinner = spinner;
|
||||
GestureDetector = GD;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
Object.defineProperty(Element.prototype, 'clientHeight', _clientHeight);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
VALUE_ELEMENT_HEIGHT = 20;
|
||||
this.container = document.createElement('div');
|
||||
this.container.classList.add('test-spinner-container');
|
||||
this.element = document.createElement('div');
|
||||
this.element.classList.add('test-spinner-element');
|
||||
this.container.appendChild(this.element);
|
||||
document.body.appendChild(this.container);
|
||||
this.clock = this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
function checkTranslate(value) {
|
||||
test('Checking translate is ' + value, function() {
|
||||
assert.equal(this.element.style.transform,
|
||||
'translateY(' + value + 'px)');
|
||||
assert.equal(this.spinner.top, value);
|
||||
});
|
||||
}
|
||||
|
||||
function checkIndex(index) {
|
||||
test('Checking index is ' + index, function() {
|
||||
assert.equal(this.spinner.index, index);
|
||||
assert.equal(this.spinner.value, this.spinner.values[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function checkCreation() {
|
||||
checkTranslate(0);
|
||||
checkIndex(0);
|
||||
}
|
||||
|
||||
suite('values: [0, 1, 2]', function() {
|
||||
setup(function() {
|
||||
this.spinner = new Spinner({
|
||||
element: this.element,
|
||||
values: [0, 1, 2]
|
||||
});
|
||||
});
|
||||
checkCreation();
|
||||
suite('select option 2', function() {
|
||||
setup(function() {
|
||||
this.spinner.select(2);
|
||||
});
|
||||
checkTranslate(-40);
|
||||
checkIndex(2);
|
||||
});
|
||||
suite('select option 3 (out of range)', function() {
|
||||
setup(function() {
|
||||
this.spinner.select(3);
|
||||
});
|
||||
checkTranslate(-40);
|
||||
checkIndex(2);
|
||||
});
|
||||
suite('set value 2', function() {
|
||||
setup(function() {
|
||||
this.spinner.value = 2;
|
||||
});
|
||||
checkTranslate(-40);
|
||||
checkIndex(2);
|
||||
});
|
||||
suite('set value out of range', function() {
|
||||
setup(function() {
|
||||
this.spinner.value = 'nope';
|
||||
});
|
||||
checkTranslate(0);
|
||||
checkIndex(0);
|
||||
});
|
||||
suite('touchstart', function() {
|
||||
setup(function() {
|
||||
this.event = new CustomEvent('touchstart', {
|
||||
cancelable: true,
|
||||
bubbles: true
|
||||
});
|
||||
this.event.changedTouches = [];
|
||||
this.element.dispatchEvent(this.event);
|
||||
});
|
||||
test('default is prevented', function() {
|
||||
assert.ok(this.event.defaultPrevented);
|
||||
});
|
||||
});
|
||||
suite('pan down by 15px over 1 second', function() {
|
||||
setup(function() {
|
||||
var event;
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 15,
|
||||
timeStamp: 1000
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
// top should still be at zero, can't go "down" in the spinner
|
||||
checkTranslate(0);
|
||||
});
|
||||
suite('pan up by 15px over 1 second', function() {
|
||||
setup(function() {
|
||||
var event;
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 15,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 1000
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-15);
|
||||
checkIndex(1);
|
||||
suite('pan finished w/ low (therefore zero) momentum', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('swipe', {
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-20);
|
||||
checkIndex(1);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('values: [0...30]', function() {
|
||||
setup(function() {
|
||||
this.spinner = new Spinner({
|
||||
element: this.element,
|
||||
values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
|
||||
});
|
||||
});
|
||||
checkCreation();
|
||||
suite('pan up by 35px over 70ms', function() {
|
||||
setup(function() {
|
||||
var event;
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 35,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 70
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-35);
|
||||
checkIndex(2);
|
||||
suite('pan finished w/ momentum', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('swipe', {
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-200);
|
||||
checkIndex(10);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
});
|
||||
suite('pan times out', function() {
|
||||
setup(function() {
|
||||
this.clock.tick(201);
|
||||
});
|
||||
checkTranslate(-40);
|
||||
checkIndex(2);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('pan up by 100px over 1ms', function() {
|
||||
setup(function() {
|
||||
var event;
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 100,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 1
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-100);
|
||||
checkIndex(5);
|
||||
suite('pan finished w/ insane momentum', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('swipe', {
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-400);
|
||||
checkIndex(20);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
suite('start pan', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
test('animation-on removed', function() {
|
||||
assert.isFalse(
|
||||
this.element.classList.contains('animation-on'));
|
||||
});
|
||||
suite('finish pan down 20px over 100ms', function() {
|
||||
// this is a smaller scale pan, moving one unit per 100ms
|
||||
setup(function() {
|
||||
var event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 20,
|
||||
timeStamp: 100
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('swipe', {
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
// total difference = 4, 1 moved + 3 inertia
|
||||
checkTranslate(-320);
|
||||
checkIndex(16);
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('pan times out', function() {
|
||||
setup(function() {
|
||||
this.clock.tick(201);
|
||||
});
|
||||
checkTranslate(-100);
|
||||
checkIndex(5);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('values: [0...30] - big elements', function() {
|
||||
// results should have same index, as previous test,
|
||||
// but double the px values for translates and motion because of bigger
|
||||
// elements
|
||||
setup(function() {
|
||||
VALUE_ELEMENT_HEIGHT = 40;
|
||||
this.spinner = new Spinner({
|
||||
element: this.element,
|
||||
values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
|
||||
});
|
||||
});
|
||||
checkCreation();
|
||||
suite('pan up by 70px over 70ms', function() {
|
||||
setup(function() {
|
||||
var event;
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 70,
|
||||
timeStamp: 0
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
event = new CustomEvent('pan', {
|
||||
detail: {
|
||||
position: {
|
||||
clientY: 0,
|
||||
timeStamp: 70
|
||||
}
|
||||
},
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-70);
|
||||
checkIndex(2);
|
||||
suite('pan finished w/ momentum', function() {
|
||||
setup(function() {
|
||||
var event = new CustomEvent('swipe', {
|
||||
bubbles: true
|
||||
});
|
||||
this.container.dispatchEvent(event);
|
||||
});
|
||||
checkTranslate(-400);
|
||||
checkIndex(10);
|
||||
test('animation-on', function() {
|
||||
assert.ok(this.element.classList.contains('animation-on'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
75
gaia/apps/clock/test/unit/post_message_proxy_test.js
Executable file
75
gaia/apps/clock/test/unit/post_message_proxy_test.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
suite('PostMessageProxy', function() {
|
||||
|
||||
var PostMessageProxy;
|
||||
suiteSetup(function(done) {
|
||||
require(['panels/alarm/post_message_proxy'], function(proxy) {
|
||||
PostMessageProxy = proxy;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('sends and receives a message', function(done) {
|
||||
var proxy = PostMessageProxy.create(window, 'me');
|
||||
PostMessageProxy.receive('me', {
|
||||
method: function(arg) {
|
||||
assert.ok(arg === 'argument');
|
||||
done();
|
||||
}
|
||||
});
|
||||
proxy.method('argument');
|
||||
});
|
||||
|
||||
test('passes a return value', function(done) {
|
||||
var proxy = PostMessageProxy.create(window, 'me');
|
||||
PostMessageProxy.receive('me', {
|
||||
method: function(arg) {
|
||||
return 'SOME RESULT';
|
||||
}
|
||||
});
|
||||
proxy.method('argument').then(function(value) {
|
||||
assert.equal(value, 'SOME RESULT');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
function MyError(message) {
|
||||
this.name = 'MyError';
|
||||
}
|
||||
MyError.prototype = new Error();
|
||||
MyError.prototype.constructor = MyError;
|
||||
|
||||
test('passes an exception', function(done) {
|
||||
var proxy = PostMessageProxy.create(window, 'me');
|
||||
PostMessageProxy.receive('me', {
|
||||
method: function(arg) {
|
||||
throw new MyError('foo');
|
||||
}
|
||||
});
|
||||
proxy.method('argument').catch(function(error) {
|
||||
assert.equal(error.name, 'MyError');
|
||||
assert.ok(error.stack.length > 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('only receives messages for proper ID', function(done) {
|
||||
PostMessageProxy.create(window, 'proxy1');
|
||||
var proxy2 = PostMessageProxy.create(window, 'proxy2');
|
||||
PostMessageProxy.receive('proxy1', {
|
||||
callProxy: function(arg) {
|
||||
assert.fail('should not have received this');
|
||||
done();
|
||||
}
|
||||
});
|
||||
PostMessageProxy.receive('proxy2', {
|
||||
callProxy: function(arg) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
proxy2.callProxy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
32
gaia/apps/clock/test/unit/setup.js
Executable file
32
gaia/apps/clock/test/unit/setup.js
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
requireApp('clock/test/unit/mocks/mock_moz_alarm.js');
|
||||
require('/shared/test/unit/load_body_html_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_intl_helper.js');
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
require('/shared/test/unit/mocks/mock_moz_intl.js');
|
||||
|
||||
requireApp('clock/js/alameda.js', () => {
|
||||
this.require = requirejs.config({
|
||||
baseUrl: '/js',
|
||||
paths: {
|
||||
shared: '../shared',
|
||||
mocks: '../test/unit/mocks'
|
||||
},
|
||||
urlArgs: 'cache_bust=' + Date.now(),
|
||||
shim: {
|
||||
'shared/js/template': {
|
||||
exports: 'Template'
|
||||
},
|
||||
'shared/js/gesture_detector': {
|
||||
exports: 'GestureDetector'
|
||||
},
|
||||
'shared/js/accessibility_helper': {
|
||||
exports: 'AccessibilityHelper'
|
||||
},
|
||||
'shared/js/async_storage': {
|
||||
exports: 'asyncStorage'
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
309
gaia/apps/clock/test/unit/stopwatch_test.js
Executable file
309
gaia/apps/clock/test/unit/stopwatch_test.js
Executable file
|
|
@ -0,0 +1,309 @@
|
|||
'use strict';
|
||||
suite('Stopwatch', function() {
|
||||
|
||||
var Stopwatch;
|
||||
var oneHour = 1 * 60 * 60 * 1000;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['stopwatch'], function(stopwatch) {
|
||||
Stopwatch = stopwatch;
|
||||
this.sw = new Stopwatch();
|
||||
done();
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.clock.restore();
|
||||
this.sw.reset();
|
||||
});
|
||||
|
||||
suite('constructor', function() {
|
||||
|
||||
test('defaults', function() {
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.RESET);
|
||||
assert.equal(this.sw.getElapsedTime(), 0);
|
||||
assert.deepEqual(this.sw.getLaps(), []);
|
||||
});
|
||||
|
||||
test('from object', function() {
|
||||
var input = {
|
||||
startTime: Date.now(),
|
||||
totalElapsed: oneHour,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: [{time: Date.now(), duration: oneHour}]
|
||||
};
|
||||
this.sw = new Stopwatch(input);
|
||||
assert.deepEqual(input, this.sw.toSerializable());
|
||||
});
|
||||
|
||||
test('deep copy', function() {
|
||||
var input = {
|
||||
startTime: Date.now(),
|
||||
totalElapsed: oneHour,
|
||||
isStarted: true,
|
||||
laps: [{time: Date.now(), duration: oneHour}]
|
||||
};
|
||||
this.sw = new Stopwatch(input);
|
||||
var serialized = this.sw.toSerializable();
|
||||
serialized.laps.push({ time: 0, duration: oneHour });
|
||||
assert.notEqual(serialized.laps, input.laps, 'laps should not be equal');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('isRunning', function() {
|
||||
|
||||
test('before start', function() {
|
||||
assert.isFalse(this.sw.getState() === Stopwatch.RUNNING);
|
||||
assert.isFalse(this.sw.getState() === Stopwatch.PAUSED);
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.RESET);
|
||||
});
|
||||
|
||||
test('start and elapse 1hr', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.RUNNING);
|
||||
});
|
||||
|
||||
test('elapse 1hr and pause', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.pause();
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.PAUSED);
|
||||
});
|
||||
|
||||
test('pause and resume', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.start();
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.RUNNING);
|
||||
});
|
||||
|
||||
test('pause and reset', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.reset();
|
||||
assert.isTrue(this.sw.getState() === Stopwatch.RESET);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('getElapsedTime', function() {
|
||||
|
||||
test('before start', function() {
|
||||
assert.equal(this.sw.getElapsedTime(), 0);
|
||||
});
|
||||
|
||||
test('after start', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
assert.equal(this.sw.getElapsedTime(), oneHour);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('pause', function() {
|
||||
|
||||
setup(function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
});
|
||||
|
||||
test('elapse 1hr & pause', function() {
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour); // do not elapse this hour
|
||||
assert.equal(this.sw.getElapsedTime(), oneHour);
|
||||
});
|
||||
|
||||
test('pause & elapse 1 hr & resume', function() {
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour); // do not elapse this hour
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
assert.equal(this.sw.getElapsedTime(), oneHour + oneHour);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('lap', function() {
|
||||
|
||||
test('before start', function() {
|
||||
var l = this.sw.lap();
|
||||
assert.equal(l, 0);
|
||||
});
|
||||
|
||||
test('elapse 1hr & lap', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
var l = this.sw.lap();
|
||||
assert.deepEqual(l, { duration: oneHour, time: oneHour });
|
||||
});
|
||||
|
||||
test('3 times', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
var l1 = this.sw.lap();
|
||||
this.clock.tick(oneHour);
|
||||
var l2 = this.sw.lap();
|
||||
this.clock.tick(oneHour);
|
||||
var l3 = this.sw.lap();
|
||||
assert.deepEqual(l1, { duration: oneHour, time: oneHour });
|
||||
assert.deepEqual(l2, { duration: oneHour, time: 2 * oneHour });
|
||||
assert.deepEqual(l3, { duration: oneHour, time: 3 * oneHour });
|
||||
});
|
||||
|
||||
test('lap & pause & start & lap', function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
var l1 = this.sw.lap();
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
var l2 = this.sw.lap();
|
||||
assert.deepEqual(l1, { duration: oneHour, time: oneHour });
|
||||
assert.deepEqual(l2, { duration: oneHour, time: 2 * oneHour });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('getLaps', function() {
|
||||
|
||||
setup(function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
});
|
||||
|
||||
test('after 3 laps', function() {
|
||||
this.sw.lap();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.lap();
|
||||
this.clock.tick(oneHour + oneHour);
|
||||
this.sw.lap();
|
||||
assert.equal(this.sw.getLaps()[0].duration, oneHour);
|
||||
assert.equal(this.sw.getLaps()[1].duration, oneHour);
|
||||
assert.equal(this.sw.getLaps()[2].duration, oneHour + oneHour);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('reset', function() {
|
||||
|
||||
setup(function() {
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
});
|
||||
|
||||
test('elapse 1hr & reset', function() {
|
||||
this.sw.reset();
|
||||
assert.equal(this.sw.getElapsedTime(), 0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('toSerializable', function() {
|
||||
|
||||
test('default', function() {
|
||||
var expected = {
|
||||
startTime: 0,
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RESET,
|
||||
laps: []
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
suite('started', function() {
|
||||
|
||||
var d;
|
||||
|
||||
setup(function() {
|
||||
d = Date.now();
|
||||
this.sw.start();
|
||||
this.clock.tick(oneHour);
|
||||
});
|
||||
|
||||
test('deep copy', function() {
|
||||
this.sw.pause();
|
||||
var serialized = this.sw.toSerializable();
|
||||
serialized.laps.push({ duration: 23, time: 0 });
|
||||
assert.deepEqual(this.sw.getLaps(), []);
|
||||
});
|
||||
|
||||
test('elapse 1hr', function() {
|
||||
var expected = {
|
||||
startTime: d,
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: []
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
test('elapse 1hr and pause', function() {
|
||||
this.sw.pause();
|
||||
var expected = {
|
||||
startTime: d,
|
||||
totalElapsed: oneHour,
|
||||
state: Stopwatch.PAUSED,
|
||||
laps: []
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
test('elapse 1hr and lap', function() {
|
||||
this.sw.lap();
|
||||
var expected = {
|
||||
startTime: d,
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: [{time: Date.now(), duration: oneHour}]
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
test('pause and reset', function() {
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.reset();
|
||||
var expected = {
|
||||
startTime: 0,
|
||||
totalElapsed: 0,
|
||||
state: Stopwatch.RESET,
|
||||
laps: []
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
test('pause and resume', function() {
|
||||
this.sw.pause();
|
||||
this.clock.tick(oneHour);
|
||||
this.sw.start();
|
||||
var expected = {
|
||||
startTime: oneHour,
|
||||
totalElapsed: oneHour,
|
||||
state: Stopwatch.RUNNING,
|
||||
laps: []
|
||||
};
|
||||
var actual = this.sw.toSerializable();
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
39
gaia/apps/clock/test/unit/tabs_test.js
Executable file
39
gaia/apps/clock/test/unit/tabs_test.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
suite('Tabs', function() {
|
||||
var Tabs;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['tabs'], function(tabs) {
|
||||
Tabs = tabs;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.element = document.createElement('div');
|
||||
this.element.innerHTML =
|
||||
'<li aria-selected="true"><a href="#link-0"></a></li>' +
|
||||
'<li><a href="#link-1"></a></li>' +
|
||||
'<li><a href="#link-2"></a></li>' +
|
||||
'<li><a href="#link-3"></a></li>';
|
||||
this.links = this.element.querySelectorAll('a');
|
||||
this.tabs = new Tabs(this.element);
|
||||
});
|
||||
|
||||
suite('test click link 1', function() {
|
||||
setup(function() {
|
||||
this.links[0].click();
|
||||
});
|
||||
});
|
||||
suite('test click link 2', function() {
|
||||
setup(function() {
|
||||
this.links[1].click();
|
||||
});
|
||||
test('test aria-selected set', function() {
|
||||
['false', 'true', 'false'].forEach(function(value, index) {
|
||||
assert.equal(this.links[index].getAttribute('aria-selected'), value);
|
||||
}, this);
|
||||
});
|
||||
});
|
||||
});
|
||||
201
gaia/apps/clock/test/unit/timer_test.js
Executable file
201
gaia/apps/clock/test/unit/timer_test.js
Executable file
|
|
@ -0,0 +1,201 @@
|
|||
'use strict';
|
||||
suite('Timer', function() {
|
||||
|
||||
/**** TEST DISABLED for intermittent failure; see bug 1153495.
|
||||
|
||||
var Timer;
|
||||
var now, duration;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['timer'], function(timer) {
|
||||
Timer = timer;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
now = Date.now();
|
||||
duration = 5000;
|
||||
this.clock = this.sinon.useFakeTimers(Date.now());
|
||||
});
|
||||
|
||||
test('shape:static ', function() {
|
||||
assert.equal(Timer.INITIAL, 0);
|
||||
assert.equal(Timer.STARTED, 1);
|
||||
assert.equal(Timer.PAUSED, 2);
|
||||
});
|
||||
|
||||
function getTimer() {
|
||||
return new Timer({
|
||||
configuredDuration: duration
|
||||
});
|
||||
}
|
||||
|
||||
test('start new timer ', function() {
|
||||
var timer = getTimer();
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
assert.equal(timer.state, Timer.STARTED);
|
||||
assert.equal(timer.remaining, duration - 200);
|
||||
});
|
||||
|
||||
test('reactivate an un-paused timer ', function() {
|
||||
var timer = getTimer();
|
||||
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
timer.start();
|
||||
|
||||
assert.equal(timer.state, Timer.STARTED);
|
||||
assert.equal(timer.remaining, duration - 400);
|
||||
});
|
||||
|
||||
test('pause timer', function() {
|
||||
var timer = getTimer();
|
||||
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
|
||||
timer.pause();
|
||||
assert.equal(timer.state, Timer.PAUSED);
|
||||
});
|
||||
|
||||
test('reactivate a paused timer ', function() {
|
||||
var timer = getTimer();
|
||||
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
|
||||
timer.pause();
|
||||
assert.equal(timer.state, Timer.PAUSED);
|
||||
|
||||
this.clock.tick(200);
|
||||
timer.start();
|
||||
|
||||
assert.equal(timer.state, Timer.STARTED);
|
||||
assert.equal(timer.remaining, duration - 200);
|
||||
});
|
||||
|
||||
|
||||
test('cancel ', function() {
|
||||
var timer = getTimer();
|
||||
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
|
||||
timer.cancel();
|
||||
assert.equal(timer.state, Timer.INITIAL);
|
||||
});
|
||||
|
||||
test('plus ', function() {
|
||||
var timer = getTimer();
|
||||
var originalDuration = timer.duration;
|
||||
|
||||
timer.plus(60);
|
||||
|
||||
assert.equal(timer.duration, originalDuration + (60 * 1000));
|
||||
});
|
||||
|
||||
suite('commit', function() {
|
||||
setup(function() {
|
||||
this.addSpy = this.sinon.spy(navigator.mozAlarms, 'add');
|
||||
this.removeSpy = this.sinon.spy(navigator.mozAlarms, 'remove');
|
||||
this.timer = getTimer();
|
||||
});
|
||||
suite('timers without alarms', function() {
|
||||
test('when in initial state', function(done) {
|
||||
this.timer.commit(function() {
|
||||
assert.equal(
|
||||
this.addSpy.callCount, 0, 'does not create a new alarm'
|
||||
);
|
||||
assert.equal(this.removeSpy.callCount, 0, 'does not remove alarm');
|
||||
done();
|
||||
}.bind(this));
|
||||
this.clock.tick(10);
|
||||
});
|
||||
test('when started', function(done) {
|
||||
this.timer.start();
|
||||
this.timer.commit(function() {
|
||||
assert.equal(this.addSpy.callCount, 1, 'creates a new alarm');
|
||||
assert.equal(this.removeSpy.callCount, 0, 'does not remove alarm');
|
||||
done();
|
||||
}.bind(this));
|
||||
this.clock.tick(10);
|
||||
});
|
||||
});
|
||||
|
||||
suite('timers with previously-created alarms', function() {
|
||||
setup(function(done) {
|
||||
// Ensure that the timer is in a saved state and the spies are reset
|
||||
this.timer.start();
|
||||
this.timer.commit(function() {
|
||||
this.addSpy.reset();
|
||||
this.removeSpy.reset();
|
||||
this.origTimerId = this.timer.id;
|
||||
|
||||
// Return timer to initial state
|
||||
this.timer.cancel();
|
||||
|
||||
done();
|
||||
}.bind(this));
|
||||
this.clock.tick(10);
|
||||
});
|
||||
|
||||
test('when in initial state', function(done) {
|
||||
this.timer.commit(function() {
|
||||
assert.equal(
|
||||
this.addSpy.callCount, 0, 'does not create a new alarm'
|
||||
);
|
||||
assert.equal(
|
||||
this.removeSpy.args[0][0], this.origTimerId, 'removes old alarm'
|
||||
);
|
||||
done();
|
||||
}.bind(this));
|
||||
this.clock.tick(10);
|
||||
});
|
||||
test('when started', function(done) {
|
||||
this.timer.start();
|
||||
this.timer.commit(function() {
|
||||
assert.equal(
|
||||
this.addSpy.callCount, 1, 'creates a new alarm'
|
||||
);
|
||||
assert.equal(
|
||||
this.removeSpy.args[0][0], this.origTimerId, 'removes old alarm'
|
||||
);
|
||||
done();
|
||||
}.bind(this));
|
||||
this.clock.tick(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Timer.remaining ', function() {
|
||||
test('Initial time', function() {
|
||||
var timer = getTimer();
|
||||
timer.configuredDuration = 7200;
|
||||
assert.equal(timer.remaining, 7200,
|
||||
'Expected remaining time to equal configured time.');
|
||||
});
|
||||
test('Paused time', function() {
|
||||
var timer = getTimer();
|
||||
timer.start();
|
||||
this.clock.tick(200);
|
||||
timer.pause();
|
||||
this.clock.tick(200);
|
||||
assert.equal(timer.remaining, 4800,
|
||||
'Expected paused time to be 4.8 seconds');
|
||||
});
|
||||
test('Started time', function() {
|
||||
var timer = getTimer();
|
||||
timer.start();
|
||||
this.clock.tick(300);
|
||||
assert.equal(timer.remaining, 4700,
|
||||
'Expected started time to be 4.7 seconds');
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
});
|
||||
610
gaia/apps/clock/test/unit/utils_test.js
Executable file
610
gaia/apps/clock/test/unit/utils_test.js
Executable file
|
|
@ -0,0 +1,610 @@
|
|||
'use strict';
|
||||
/* global MockIntlHelper, MockMozIntl */
|
||||
|
||||
suite('Time functions', function() {
|
||||
var Utils;
|
||||
var MockRequestWakeLock;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
window.IntlHelper = MockIntlHelper;
|
||||
window.mozIntl = MockMozIntl;
|
||||
require(['utils', 'mocks/mock_request_wake_lock'],
|
||||
function(utils, _MockRequestWakeLock, l10n) {
|
||||
Utils = utils;
|
||||
MockRequestWakeLock = _MockRequestWakeLock;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('#changeSelectByValue', function() {
|
||||
|
||||
var changeSelectByValue, selectDOM;
|
||||
|
||||
setup(function() {
|
||||
changeSelectByValue = Utils.changeSelectByValue;
|
||||
selectDOM = document.createElement('select');
|
||||
selectDOM.innerHTML = ['<option value="a">A</option>',
|
||||
'<option value="b">B</option>',
|
||||
'<option value="c" selected>C</option>'
|
||||
].join('');
|
||||
});
|
||||
|
||||
test('correctly selects the specified element', function() {
|
||||
changeSelectByValue(selectDOM, 'b');
|
||||
assert.equal(selectDOM.selectedIndex, 1);
|
||||
});
|
||||
|
||||
test('has no effect when specified element does not exist', function() {
|
||||
changeSelectByValue(selectDOM, 'g');
|
||||
assert.equal(selectDOM.selectedIndex, 2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('#dateMath', function() {
|
||||
suiteSetup(function() {
|
||||
// The timestamp for 'Tue Jul 16 2013 06:00:00' GMT
|
||||
this.sixAm = new Date(2013, 5, 16, 6).getTime();
|
||||
// Set clock so calls to new Date() and Date.now() will not vary
|
||||
// across test locales
|
||||
this.dat = new Date(this.sixAm + 15120000);
|
||||
this.allNeg = {
|
||||
hours: -4,
|
||||
minutes: -15,
|
||||
seconds: -2
|
||||
};
|
||||
this.someNeg = {
|
||||
hours: 4,
|
||||
minutes: -15,
|
||||
seconds: 2
|
||||
};
|
||||
this.overflow = {
|
||||
hours: 4,
|
||||
minutes: 0,
|
||||
seconds: 902
|
||||
};
|
||||
this.skip = {
|
||||
hours: 4,
|
||||
seconds: 902
|
||||
};
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.clock = this.sinon.useFakeTimers(this.sixAm);
|
||||
});
|
||||
|
||||
suite('toMS', function() {
|
||||
test('returns correct millisecond value ', function() {
|
||||
assert.equal(
|
||||
Utils.dateMath.toMS(this.skip),
|
||||
15302000
|
||||
);
|
||||
assert.equal(
|
||||
Utils.dateMath.toMS(this.overflow),
|
||||
15302000
|
||||
);
|
||||
});
|
||||
test('any/all negative input result in negative output ', function() {
|
||||
assert.ok(Utils.dateMath.toMS(this.someNeg) < 0);
|
||||
assert.ok(Utils.dateMath.toMS(this.allNeg) < 0);
|
||||
});
|
||||
test('all positive input result in positive output ', function() {
|
||||
assert.ok(Utils.dateMath.toMS(this.dat) > 0);
|
||||
assert.ok(Utils.dateMath.toMS(this.skip) > 0);
|
||||
});
|
||||
test('accepts plural or singular unit names ', function() {
|
||||
assert.equal(
|
||||
Utils.dateMath.toMS(this.skip, {unitsPartial: ['hours', 'second']}),
|
||||
15302000
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('fromMS', function() {
|
||||
suiteSetup(function() {
|
||||
this.negtime = -140000;
|
||||
this.hour = 1000 * 60 * 60;
|
||||
this.fourish = 15120000;
|
||||
});
|
||||
test('returns a correctly formed object ', function() {
|
||||
assert.deepEqual(
|
||||
Utils.dateMath.fromMS(this.fourish, {
|
||||
unitsPartial: ['hours', 'minutes']
|
||||
}),
|
||||
{
|
||||
hours: 4,
|
||||
minutes: 12
|
||||
}
|
||||
);
|
||||
});
|
||||
test('returns correct signs ', function() {
|
||||
var pos, neg;
|
||||
pos = Utils.dateMath.fromMS(this.hour);
|
||||
neg = Utils.dateMath.fromMS(this.negtime);
|
||||
assert.ok(Object.keys(pos).every(function(unit) {
|
||||
return pos[unit] >= 0;
|
||||
}));
|
||||
assert.ok(Object.keys(neg).every(function(unit) {
|
||||
return neg[unit] <= 0;
|
||||
}));
|
||||
});
|
||||
test('returns desired granularity ', function() {
|
||||
assert.deepEqual(
|
||||
Utils.dateMath.fromMS(this.hour, {unitsPartial: ['minutes']}),
|
||||
{
|
||||
minutes: 60
|
||||
}
|
||||
);
|
||||
assert.deepEqual(
|
||||
Utils.dateMath.fromMS(this.fourish, {unitsPartial: ['minutes']}),
|
||||
{
|
||||
minutes: 252
|
||||
}
|
||||
);
|
||||
});
|
||||
test('accepts plural or singular unit names ', function() {
|
||||
assert.deepEqual(
|
||||
Utils.dateMath.fromMS(this.fourish, {
|
||||
unitsPartial: ['hour', 'minute']
|
||||
}),
|
||||
{
|
||||
hours: 4,
|
||||
minutes: 12
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('data tests', function() {
|
||||
suite('compare tests', function() {
|
||||
test('numbers', function() {
|
||||
assert.equal(Utils.data.defaultCompare(42, 42), 0);
|
||||
assert.equal(Utils.data.defaultCompare(0, 0), 0);
|
||||
assert.equal(Utils.data.defaultCompare(0, 1), -1);
|
||||
assert.equal(Utils.data.defaultCompare(1, 0), 1);
|
||||
var e1, e2;
|
||||
try {
|
||||
Utils.data.defaultCompare(1, {});
|
||||
} catch (err) {
|
||||
e1 = err;
|
||||
}
|
||||
assert.ok(e1);
|
||||
try {
|
||||
Utils.data.defaultCompare({}, 1);
|
||||
} catch (err) {
|
||||
e2 = err;
|
||||
}
|
||||
assert.ok(e2);
|
||||
});
|
||||
test('strings', function() {
|
||||
assert.equal(Utils.data.defaultCompare('abc', 'abc'), 0);
|
||||
assert.equal(Utils.data.defaultCompare('abc', 'abd'), -1);
|
||||
assert.equal(Utils.data.defaultCompare('abc', 'abb'), 1);
|
||||
assert.equal(Utils.data.defaultCompare('abc', 'abcd'), -1);
|
||||
assert.equal(Utils.data.defaultCompare('abc', 'ab'), 1);
|
||||
var e;
|
||||
try {
|
||||
Utils.data.defaultCompare('abc', 1);
|
||||
} catch (err) {
|
||||
e = err;
|
||||
}
|
||||
assert.ok(e);
|
||||
});
|
||||
test('arrays', function() {
|
||||
assert.equal(Utils.data.defaultCompare([1, 2, 3], [1, 2, 3]), 0);
|
||||
assert.equal(Utils.data.defaultCompare([1, 2, 3], [1, 2, 4]), -1);
|
||||
assert.equal(Utils.data.defaultCompare([1, 2, 3], [1, 2, 2]), 1);
|
||||
var e;
|
||||
try {
|
||||
Utils.data.defaultCompare('abc', 1);
|
||||
} catch (err) {
|
||||
e = err;
|
||||
}
|
||||
assert.ok(e);
|
||||
});
|
||||
});
|
||||
|
||||
suite('binarySearch', function() {
|
||||
|
||||
var genMatchTest = function(arr, search, idx) {
|
||||
test('length ' + arr.length + ', match for ' + JSON.stringify(search),
|
||||
function() {
|
||||
var res = Utils.data.binarySearch({ value: search }, arr,
|
||||
Utils.data.keyedCompare('value'));
|
||||
assert.deepEqual(res, {
|
||||
match: true,
|
||||
index: idx,
|
||||
value: arr[idx]
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var genNoMatchTest = function(arr, search, idx) {
|
||||
test('length ' + arr.length + ', no match for ' +
|
||||
JSON.stringify(search),
|
||||
function() {
|
||||
var res = Utils.data.binarySearch({ value: search }, arr,
|
||||
Utils.data.keyedCompare('value'));
|
||||
assert.deepEqual(res, {
|
||||
match: false,
|
||||
index: idx
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var array14 = [
|
||||
{value: 1}, {value: 11}, {value: 25},
|
||||
{value: 28}, {value: 30}, {value: 36},
|
||||
{value: 48}, {value: 54}, {value: 54},
|
||||
{value: 59}, {value: 61}, {value: 82},
|
||||
{value: 82}, {value: 85}
|
||||
];
|
||||
|
||||
var array15 = [
|
||||
{value: 1}, {value: 11}, {value: 25},
|
||||
{value: 28}, {value: 30}, {value: 36},
|
||||
{value: 48}, {value: 54}, {value: 54},
|
||||
{value: 59}, {value: 61}, {value: 82},
|
||||
{value: 82}, {value: 85}, {value: 98}
|
||||
];
|
||||
|
||||
test('zero length', function() {
|
||||
var res = Utils.data.binarySearch(34, []);
|
||||
assert.deepEqual(res, { match: false, index: 0 });
|
||||
});
|
||||
|
||||
test('length one, no match', function() {
|
||||
var res = Utils.data.binarySearch(34, [5]);
|
||||
assert.deepEqual(res, { match: false, index: 1 });
|
||||
});
|
||||
|
||||
test('length one, match', function() {
|
||||
var res = Utils.data.binarySearch(34, [34]);
|
||||
assert.deepEqual(res, {
|
||||
match: true,
|
||||
index: 0,
|
||||
value: 34
|
||||
});
|
||||
});
|
||||
|
||||
genMatchTest(array14, 1, 0);
|
||||
genMatchTest(array14, 85, 13);
|
||||
genNoMatchTest(array14, -1, 0);
|
||||
genNoMatchTest(array14, 52, 7);
|
||||
genNoMatchTest(array14, 101, 14);
|
||||
|
||||
genMatchTest(array15, 1, 0);
|
||||
genMatchTest(array15, 98, 14);
|
||||
genNoMatchTest(array15, -1, 0);
|
||||
genNoMatchTest(array15, 52, 7);
|
||||
genNoMatchTest(array15, 101, 15);
|
||||
});
|
||||
|
||||
suite('sortedInsert', function() {
|
||||
var array = [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82, 91];
|
||||
test('zero length insert', function() {
|
||||
var x = [];
|
||||
var res = Utils.data.sortedInsert(3, x);
|
||||
assert.deepEqual(x, [3]);
|
||||
assert.equal(res, 0);
|
||||
});
|
||||
test('beginning insert', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedInsert(-10, x);
|
||||
assert.equal(x[0], -10);
|
||||
assert.equal(x.length, array.length + 1);
|
||||
assert.equal(res, 0);
|
||||
});
|
||||
test('middle insert', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedInsert(43, x);
|
||||
assert.equal(x[6], 43);
|
||||
assert.equal(x.length, array.length + 1);
|
||||
assert.equal(res, 6);
|
||||
});
|
||||
test('end insert', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedInsert(99, x);
|
||||
assert.equal(x[x.length - 1], 99);
|
||||
assert.equal(x.length, array.length + 1);
|
||||
assert.equal(res, array.length);
|
||||
});
|
||||
});
|
||||
|
||||
suite('sortedRemove', function() {
|
||||
var array = [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82, 91];
|
||||
test('zero length remove', function() {
|
||||
var x = [];
|
||||
var res = Utils.data.sortedRemove(3, x);
|
||||
assert.equal(res, false);
|
||||
});
|
||||
test('length one remove', function() {
|
||||
var x = [3];
|
||||
var res = Utils.data.sortedRemove(3, x);
|
||||
assert.deepEqual(x, []);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('beginning remove', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedRemove(1, x);
|
||||
assert.deepEqual(x, [8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82, 91]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('middle remove', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedRemove(58, x);
|
||||
assert.deepEqual(x, [1, 8, 9, 19, 38, 42, 44, 56, 64, 74, 82, 91]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('end remove', function() {
|
||||
var x = array.slice();
|
||||
var res = Utils.data.sortedRemove(91, x);
|
||||
assert.deepEqual(x, [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('beginning multi remove', function() {
|
||||
var x = [3, 3, 3, 3, 3, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82];
|
||||
var res = Utils.data.sortedRemove(3, x, undefined, true);
|
||||
assert.deepEqual(x, [8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('middle multi remove', function() {
|
||||
var x = [1, 8, 9, 19, 21, 21, 21, 21, 38, 42, 44, 56, 58, 64, 74, 82];
|
||||
var res = Utils.data.sortedRemove(21, x, undefined, true);
|
||||
assert.deepEqual(x, [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
test('end multi remove', function() {
|
||||
var x = [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82, 99, 99, 99];
|
||||
var res = Utils.data.sortedRemove(99, x, undefined, true);
|
||||
assert.deepEqual(x, [1, 8, 9, 19, 38, 42, 44, 56, 58, 64, 74, 82]);
|
||||
assert.equal(res, true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
suite('extend tests', function() {
|
||||
|
||||
function hasOwn(obj, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
}
|
||||
|
||||
function hasAny(obj, prop) {
|
||||
return hasOwn(obj, prop) || (
|
||||
obj !== Object.prototype && hasAny(obj.__proto__, prop));
|
||||
}
|
||||
|
||||
function testObject() {
|
||||
return Object.create(
|
||||
Object.create({ grandparents: 4 },
|
||||
{ parents: { value: 2 } }), {
|
||||
me: { value: 1 }
|
||||
});
|
||||
}
|
||||
|
||||
test('basic extend', function() {
|
||||
var x = {};
|
||||
var y = Utils.extend(x, {a: 1, b: 2, c: 3});
|
||||
assert.ok(x === y, 'x === y');
|
||||
assert.ok(hasOwn(x, 'a') && x.a === 1);
|
||||
assert.ok(hasOwn(x, 'b') && x.b === 2);
|
||||
assert.ok(hasOwn(x, 'c') && x.c === 3);
|
||||
});
|
||||
|
||||
test('extend only affects child instance', function() {
|
||||
var x = testObject(), y;
|
||||
y = Utils.extend(x, {favoriteColor: 'green'});
|
||||
assert.ok(x === y, 'x === y');
|
||||
assert.equal(x.favoriteColor, 'green');
|
||||
assert.ok(hasOwn(x, 'favoriteColor'));
|
||||
assert.ok(!hasAny(x.__proto__, 'favoriteColor'));
|
||||
});
|
||||
|
||||
test('multiple extend', function() {
|
||||
var x = Utils.extend({}, {
|
||||
a: 42
|
||||
}, {
|
||||
a: 1,
|
||||
b: 19,
|
||||
c: 45
|
||||
}, {
|
||||
b: 199
|
||||
});
|
||||
assert.equal(x.a, 1);
|
||||
assert.equal(x.b, 199);
|
||||
assert.equal(x.c, 45);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('safeWakeLock tests', function() {
|
||||
|
||||
setup(function() {
|
||||
this.mocklock = new MockRequestWakeLock();
|
||||
this.sinon.stub(navigator, 'requestWakeLock',
|
||||
this.mocklock.requestWakeLock.bind(this.mocklock));
|
||||
this.sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
this.sinon.restore(navigator, 'requestWakeLock');
|
||||
});
|
||||
|
||||
test('locks CPU', function() {
|
||||
var callback = this.sinon.spy(function(unlock) {
|
||||
assert.ok(navigator.requestWakeLock.calledOnce);
|
||||
});
|
||||
Utils.safeWakeLock({timeoutMs: 15000}, callback);
|
||||
this.sinon.clock.tick(16000);
|
||||
assert.ok(callback.calledOnce);
|
||||
});
|
||||
|
||||
test('single unlock', function() {
|
||||
Utils.safeWakeLock({timeoutMs: 15000}, function(unlock) {
|
||||
unlock();
|
||||
});
|
||||
this.sinon.clock.tick(16000);
|
||||
var locks = this.mocklock.getissued();
|
||||
assert.equal(locks.length, 1);
|
||||
assert.equal(locks[0].unlocks, 1);
|
||||
});
|
||||
|
||||
test('no duplicate unlock', function() {
|
||||
var here = 0;
|
||||
Utils.safeWakeLock({timeoutMs: 15000}, function(unlock) {
|
||||
setTimeout(function() {
|
||||
here++;
|
||||
unlock();
|
||||
}, 16000);
|
||||
here++;
|
||||
});
|
||||
this.sinon.clock.tick(17000);
|
||||
var locks = this.mocklock.getissued();
|
||||
assert.equal(locks.length, 1);
|
||||
assert.equal(locks[0].unlocks, 1);
|
||||
assert.equal(here, 2);
|
||||
});
|
||||
|
||||
test('timeout unlock', function() {
|
||||
var here = false;
|
||||
Utils.safeWakeLock({timeoutMs: 15000}, function(unlock) {
|
||||
here = true;
|
||||
});
|
||||
this.sinon.clock.tick(16000);
|
||||
var locks = this.mocklock.getissued();
|
||||
assert.equal(locks.length, 1);
|
||||
assert.equal(locks[0].unlocks, 1);
|
||||
assert.ok(here);
|
||||
});
|
||||
|
||||
test('exception in callback still unlocks CPU', function() {
|
||||
var here = false;
|
||||
try {
|
||||
Utils.safeWakeLock({timeoutMs: 15000}, function(unlock) {
|
||||
here = true;
|
||||
throw new Error('gotcha');
|
||||
});
|
||||
this.sinon.clock.tick(16000);
|
||||
} catch (err) {
|
||||
assert.equal(err.message, 'gotcha');
|
||||
}
|
||||
var locks = this.mocklock.getissued();
|
||||
assert.equal(locks.length, 1);
|
||||
assert.equal(locks[0].unlocks, 1);
|
||||
assert.ok(here);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('async', function() {
|
||||
test('generators', function() {
|
||||
var spy = this.sinon.spy(function() {
|
||||
assert.equal(arguments[0], null);
|
||||
assert.equal(arguments.length, 1);
|
||||
});
|
||||
var gen = Utils.async.generator(spy);
|
||||
var collection = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
collection.push(gen());
|
||||
}
|
||||
assert.ok(!spy.called);
|
||||
for (var k of collection) {
|
||||
k();
|
||||
}
|
||||
assert.ok(spy.calledOnce);
|
||||
});
|
||||
|
||||
test('generators error', function() {
|
||||
var spy = this.sinon.spy(function() {
|
||||
assert.equal(arguments[0], 1);
|
||||
assert.equal(arguments.length, 1);
|
||||
});
|
||||
var gen = Utils.async.generator(spy);
|
||||
var collection = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
collection.push(gen());
|
||||
}
|
||||
assert.ok(!spy.called);
|
||||
var j = 1;
|
||||
for (var k of collection) {
|
||||
k(j++);
|
||||
}
|
||||
assert.ok(spy.calledOnce);
|
||||
});
|
||||
|
||||
test('namedParallel', function() {
|
||||
var spy = this.sinon.spy(function() {
|
||||
assert.equal(arguments[0], null);
|
||||
assert.equal(arguments.length, 1);
|
||||
});
|
||||
var names = Utils.async.namedParallel([
|
||||
'testa', 'testb', 'testc'
|
||||
], spy);
|
||||
assert.ok(!spy.called);
|
||||
names.testa();
|
||||
assert.ok(!spy.called);
|
||||
names.testb();
|
||||
assert.ok(!spy.called);
|
||||
names.testc();
|
||||
assert.ok(spy.calledOnce);
|
||||
});
|
||||
|
||||
test('namedParallel error', function() {
|
||||
var spy = this.sinon.spy(function() {
|
||||
assert.equal(arguments[0], 'testing b');
|
||||
assert.equal(arguments.length, 1);
|
||||
});
|
||||
var names = Utils.async.namedParallel([
|
||||
'testa', 'testb', 'testc'
|
||||
], spy);
|
||||
assert.ok(!spy.called);
|
||||
names.testa();
|
||||
assert.ok(!spy.called);
|
||||
names.testb('testing b');
|
||||
assert.ok(spy.calledOnce);
|
||||
names.testc();
|
||||
assert.ok(spy.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
suite('summarizeDaysOfWeek', function() {
|
||||
test('weekends', function(done) {
|
||||
Utils.summarizeDaysOfWeek({
|
||||
'0': true, '6': true
|
||||
}).then(result => {
|
||||
assert.equal(result, 'weekends');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('never', function(done) {
|
||||
Utils.summarizeDaysOfWeek({
|
||||
}).then(result => {
|
||||
assert.equal(result, 'never');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('weekdays', function(done) {
|
||||
Utils.summarizeDaysOfWeek({
|
||||
'1': true, '2': true, '3': true, '4': true, '5': true
|
||||
}).then(result => {
|
||||
assert.equal(result, 'weekdays');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('everyday', function(done) {
|
||||
Utils.summarizeDaysOfWeek({
|
||||
'0': true, '1': true, '2': true, '3': true,
|
||||
'4': true, '5': true, '6': true
|
||||
}).then(result => {
|
||||
assert.equal(result, 'everyday');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('mon, wed, fri', function(done) {
|
||||
Utils.summarizeDaysOfWeek({
|
||||
'1': true, '3': true, '5': true
|
||||
}).then(result => {
|
||||
assert.deepEqual(result, {raw: 'Mon, Wed, Fri'});
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
106
gaia/apps/clock/test/unit/view_test.js
Executable file
106
gaia/apps/clock/test/unit/view_test.js
Executable file
|
|
@ -0,0 +1,106 @@
|
|||
'use strict';
|
||||
suite('View', function() {
|
||||
var View;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
require(['view'], function(view) {
|
||||
View = view;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.element = document.createElement('div');
|
||||
});
|
||||
|
||||
suite('View.instance', function() {
|
||||
test('creates new View', function() {
|
||||
var view = View.instance(this.element);
|
||||
assert.ok(view instanceof View);
|
||||
});
|
||||
test('returns same View on second run', function() {
|
||||
var view = View.instance(this.element);
|
||||
assert.equal(View.instance(this.element), view);
|
||||
});
|
||||
|
||||
suite('using subclass', function() {
|
||||
setup(function() {
|
||||
this.ctor = this.sinon.spy(function(element) {
|
||||
View.call(this, element);
|
||||
});
|
||||
this.ctor.prototype = Object.create(View.prototype);
|
||||
this.view = View.instance(this.element, this.ctor);
|
||||
});
|
||||
test('returns instance of ctor', function() {
|
||||
assert.ok(this.view instanceof this.ctor);
|
||||
});
|
||||
test('called ctor', function() {
|
||||
assert.ok(this.ctor.calledWith(this.element));
|
||||
});
|
||||
test('returns same view on second run', function() {
|
||||
assert.equal(this.view, View.instance(this.element));
|
||||
assert.equal(this.ctor.callCount, 1,
|
||||
'did not call ctor again');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('tests on instances', function() {
|
||||
setup(function() {
|
||||
this.element.id = 'test-element';
|
||||
this.view = new View(this.element);
|
||||
this.visibleSpy = this.sinon.spy();
|
||||
this.view.element.addEventListener('panel-visibilitychange',
|
||||
this.visibleSpy);
|
||||
});
|
||||
test('has element', function() {
|
||||
assert.equal(this.view.element, this.element);
|
||||
});
|
||||
test('has id', function() {
|
||||
assert.equal(this.view.id, this.element.id);
|
||||
});
|
||||
test('starts visible', function() {
|
||||
assert.isTrue(this.view.visible);
|
||||
});
|
||||
suite('set visible true', function() {
|
||||
setup(function() {
|
||||
this.view.visible = true;
|
||||
});
|
||||
test('event not called(changed to same state)', function() {
|
||||
assert.equal(this.visibleSpy.callCount, 0);
|
||||
});
|
||||
});
|
||||
suite('set visible false', function() {
|
||||
setup(function() {
|
||||
this.view.visible = false;
|
||||
});
|
||||
test('event called', function() {
|
||||
assert.ok(this.visibleSpy.calledOnce);
|
||||
});
|
||||
test('added hidden class', function() {
|
||||
assert.ok(this.element.classList.contains('hidden'));
|
||||
});
|
||||
suite('set visible false', function() {
|
||||
setup(function() {
|
||||
this.visibleSpy.reset();
|
||||
this.view.visible = false;
|
||||
});
|
||||
test('event not called(changed to same state)', function() {
|
||||
assert.equal(this.visibleSpy.callCount, 0);
|
||||
});
|
||||
});
|
||||
suite('set visible true', function() {
|
||||
setup(function() {
|
||||
this.visibleSpy.reset();
|
||||
this.view.visible = true;
|
||||
});
|
||||
test('event called', function() {
|
||||
assert.ok(this.visibleSpy.calledOnce);
|
||||
});
|
||||
test('removed hidden class', function() {
|
||||
assert.isFalse(this.element.classList.contains('hidden'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue