Testing Object Utility Functions in Video.js
This test suite validates utility functions for object manipulation in Video.js, focusing on core object operations like iteration, merging, and property definition. The tests ensure reliable object handling across the Video.js codebase.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
videojs/videoJs
test/unit/utils/obj.test.js
/* eslint-env qunit */
import sinon from 'sinon';
import * as Obj from '../../../src/js/utils/obj';
class Foo {
constructor() {}
toString() {
return 'I am a Foo!';
}
}
const passFail = (assert, fn, descriptor, passes, failures) => {
Object.keys(passes).forEach(key => {
assert.ok(fn(passes[key]), `${key} IS ${descriptor}`);
});
Object.keys(failures).forEach(key => {
assert.notOk(fn(failures[key]), `${key} IS NOT ${descriptor}`);
});
};
QUnit.module('utils/obj', function() {
QUnit.test('each', function(assert) {
const spy = sinon.spy();
Obj.each({
a: 1,
b: 'foo',
c: null
}, spy);
assert.strictEqual(spy.callCount, 3);
assert.ok(spy.calledWith(1, 'a'));
assert.ok(spy.calledWith('foo', 'b'));
assert.ok(spy.calledWith(null, 'c'));
Obj.each({}, spy);
assert.strictEqual(spy.callCount, 3, 'an empty object was not iterated over');
});
QUnit.test('reduce', function(assert) {
const first = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value) => accum + value);
assert.strictEqual(first, 10);
const second = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value) => accum + value, 10);
assert.strictEqual(second, 20);
const third = Obj.reduce({
a: 1,
b: 2,
c: 3,
d: 4
}, (accum, value, key) => {
accum[key] = 0 - value;
return accum;
}, {});
assert.strictEqual(third.a, -1);
assert.strictEqual(third.b, -2);
assert.strictEqual(third.c, -3);
assert.strictEqual(third.d, -4);
});
QUnit.test('isObject', function(assert) {
passFail(assert, Obj.isObject, 'an object', {
'plain object': {},
'constructed object': new Foo(),
'array': [],
'regex': new RegExp('.'),
'date': new Date()
}, {
null: null,
function() {},
boolean: true,
number: 1,
string: 'xyz'
});
});
QUnit.test('isPlain', function(assert) {
passFail(assert, Obj.isPlain, 'a plain object', {
'plain object': {}
}, {
'constructed object': new Foo(),
'null': null,
'array': [],
'function'() {},
'regex': new RegExp('.'),
'date': new Date(),
'boolean': true,
'number': 1,
'string': 'xyz'
});
});
QUnit.module('merge');
QUnit.test('should merge objects', function(assert) {
const ob1 = {
a: true,
b: { b1: true, b2: true, b3: true },
c: true
};
const ob2 = {
// override value
a: false,
// merge sub-option values
b: { b1: true, b2: false, b4: true },
// add new option
d: true
};
const ob3 = Obj.merge(ob1, ob2);
assert.deepEqual(ob3, {
a: false,
b: { b1: true, b2: false, b3: true, b4: true },
c: true,
d: true
}, 'options objects merged correctly');
});
QUnit.test('should ignore non-objects', function(assert) {
const obj = { a: 1 };
assert.deepEqual(Obj.merge(obj, true), obj, 'ignored non-object input');
});
QUnit.module('defineLazyProperty');
QUnit.test('should define a "lazy" property', function(assert) {
assert.expect(12);
const obj = {a: 1};
const getValue = sinon.spy(() => {
return 2;
});
Obj.defineLazyProperty(obj, 'b', getValue);
let descriptor = Object.getOwnPropertyDescriptor(obj, 'b');
assert.ok(getValue.notCalled, 'getValue function was not called');
assert.strictEqual(typeof descriptor.get, 'function', 'descriptor has a getter');
assert.strictEqual(typeof descriptor.set, 'function', 'descriptor has a setter');
assert.strictEqual(typeof descriptor.value, 'undefined', 'descriptor has no value');
let b = obj.b;
descriptor = Object.getOwnPropertyDescriptor(obj, 'b');
assert.ok(getValue.calledOnce, 'getValue function was not called');
assert.strictEqual(b, 2, 'the value was retrieved correctly');
assert.strictEqual(typeof descriptor.get, 'undefined', 'descriptor has no getter');
assert.strictEqual(typeof descriptor.set, 'undefined', 'descriptor has no setter');
assert.strictEqual(descriptor.value, 2, 'descriptor has a value');
b = obj.b;
descriptor = Object.getOwnPropertyDescriptor(obj, 'b');
assert.ok(getValue.calledOnce, 'getValue function was still only called once');
assert.strictEqual(b, 2, 'the value was retrieved correctly');
assert.strictEqual(descriptor.value, 2, 'descriptor has a value');
});
QUnit.module('values', () => {
QUnit.test('returns an array of values for a given object', (assert) => {
const source = { a: 1, b: 2, c: 3 };
const expectedResult = [1, 2, 3];
assert.deepEqual(Obj.values(source), expectedResult, 'All values are extracted correctly');
});
QUnit.test('returns an empty array for an empty object', (assert) => {
const source = {};
const expectedResult = [];
assert.deepEqual(Obj.values(source), expectedResult, 'Empty array is returned for an empty object');
});
QUnit.test('ignores prototype properties', (assert) => {
const source = Object.create({ a: 1 });
source.b = 2;
const expectedResult = [2];
assert.deepEqual(Obj.values(source), expectedResult, 'Only own properties are included in the result');
});
});
});