'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 = ['',
'',
''
].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);
});
});
});