'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); }); }); });