Testing Function Utility Implementation in Video.js
This test suite validates core function utilities in Video.js, focusing on context binding, throttling, and debouncing implementations. The tests ensure reliable function behavior control and timing mechanisms essential for video player performance.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
videojs/videoJs
test/unit/utils/fn.test.js
/* eslint-env qunit */
import sinon from 'sinon';
import * as Fn from '../../../src/js/utils/fn';
QUnit.module('utils/fn', {
beforeEach() {
this.clock = sinon.useFakeTimers();
},
afterEach() {
this.clock.restore();
}
});
QUnit.test('should add context to a function', function(assert) {
assert.expect(1);
const newContext = { test: 'obj'};
const asdf = function() {
assert.ok(this === newContext);
};
const fdsa = Fn.bind_(newContext, asdf);
fdsa();
});
QUnit.test('should throttle functions properly', function(assert) {
assert.expect(3);
const tester = sinon.spy();
const throttled = Fn.throttle(tester, 100);
// We must wait a full wait period before the function can be called.
this.clock.tick(100);
throttled();
throttled();
this.clock.tick(50);
throttled();
assert.strictEqual(tester.callCount, 1, 'the throttled function has been called the correct number of times');
this.clock.tick(50);
throttled();
assert.strictEqual(tester.callCount, 2, 'the throttled function has been called the correct number of times');
throttled();
this.clock.tick(100);
throttled();
assert.strictEqual(tester.callCount, 3, 'the throttled function has been called the correct number of times');
});
QUnit.test('should debounce functions properly', function(assert) {
assert.expect(6);
const tester = sinon.spy();
const debounced = Fn.debounce(tester, 100);
// Called twice on each assertion to ensure that multiple calls only result
// in a call to the inner function.
debounced();
debounced();
assert.strictEqual(tester.callCount, 0, 'the debounced function was NOT called because no time has elapsed');
this.clock.tick(100);
assert.strictEqual(tester.callCount, 1, 'the debounced function was called because enough time elapsed');
this.clock.tick(100);
assert.strictEqual(tester.callCount, 1, 'the debounced function was NOT called again even though time elapsed');
debounced();
debounced();
assert.strictEqual(tester.callCount, 1, 'the debounced function was NOT called because no time has elapsed since invocation');
this.clock.tick(50);
assert.strictEqual(tester.callCount, 1, 'the debounced function was NOT called because the clock has NOT ticket forward enough');
this.clock.tick(50);
assert.strictEqual(tester.callCount, 2, 'the debounced function was called because the clock ticked forward enough');
});
QUnit.test('may immediately invoke debounced functions', function(assert) {
assert.expect(2);
const tester = sinon.spy();
const debounced = Fn.debounce(tester, 100, true);
// Called twice on each assertion to ensure that multiple calls only result
// in a call to the inner function.
debounced();
debounced();
assert.strictEqual(tester.callCount, 1, 'the debounced function was called because true was passed');
this.clock.tick(100);
assert.strictEqual(tester.callCount, 1, 'the debounced function was NOT called because it has only been invoked once');
});
QUnit.test('may cancel debounced functions', function(assert) {
assert.expect(2);
const tester = sinon.spy();
const debounced = Fn.debounce(tester, 100);
debounced();
this.clock.tick(50);
debounced.cancel();
this.clock.tick(50);
assert.strictEqual(tester.callCount, 0, 'the debounced function was NOT called because it was cancelled');
debounced();
this.clock.tick(100);
assert.strictEqual(tester.callCount, 1, 'the debounced function was called because it was NOT cancelled');
});