Back to Repositories

Validating Plugin System Static Methods in video.js

This test suite validates the static methods and functionality of the Video.js plugin system. It ensures proper plugin registration, management, and integration with the Player class while maintaining version control and plugin type verification.

Test Coverage Overview

The test suite provides comprehensive coverage of Video.js plugin system’s core functionality.

Key areas tested include:
  • Plugin registration and deregistration
  • Basic and class-based plugin handling
  • Plugin version management
  • Plugin retrieval and validation
  • Error handling for illegal plugin operations

Implementation Analysis

The testing approach utilizes QUnit’s module-based structure with beforeEach and afterEach hooks for consistent test isolation. Tests employ both function-based and class-based plugin implementations to verify the system’s flexibility in handling different plugin architectures.

Notable patterns include:
  • Mock plugin class implementation
  • Sinon integration for stub testing
  • Systematic error case validation

Technical Details

Testing infrastructure includes:
  • QUnit as the primary testing framework
  • Sinon.js for stub/mock functionality
  • ESLint configuration for code quality
  • Custom Plugin base class implementation
  • Player class integration testing

Best Practices Demonstrated

The test suite exemplifies strong testing practices through thorough coverage and organized structure.

Notable practices include:
  • Systematic test cleanup after each case
  • Comprehensive error condition testing
  • Isolated test cases with clear assertions
  • Proper mock object implementation
  • Consistent validation patterns across test cases

videojs/videoJs

test/unit/plugin-static.test.js

            
/* eslint-env qunit */
import sinon from 'sinon';
import log from '../../src/js/utils/log';
import Player from '../../src/js/player';
import Plugin from '../../src/js/plugin';

class MockPlugin extends Plugin {}

MockPlugin.VERSION = 'v1.2.3';

QUnit.module('Plugin: static methods', {

  beforeEach() {
    this.basic = () => {};

    Plugin.registerPlugin('basic', this.basic);
    Plugin.registerPlugin('mock', MockPlugin);
  },

  afterEach() {
    Object.keys(Plugin.getPlugins()).forEach(key => {
      if (key !== Plugin.BASE_PLUGIN_NAME) {
        Plugin.deregisterPlugin(key);
      }
    });
  }
});

QUnit.test('registerPlugin() works with basic plugins', function(assert) {
  const foo = () => {};

  assert.strictEqual(Plugin.registerPlugin('foo', foo), foo, 'the plugin is returned');
  assert.strictEqual(Plugin.getPlugin('foo'), foo, 'the plugin can be retrieved');
  assert.strictEqual(Object.prototype.toString.call(Player.prototype.foo), '[object Function]', 'the plugin has a wrapper function');
  assert.notStrictEqual(Player.prototype.foo, foo, 'the function on the player prototype is a wrapper');

  Plugin.deregisterPlugin('foo');
});

QUnit.test('registerPlugin() works with class-based plugins', function(assert) {
  class Foo extends Plugin {}

  assert.strictEqual(Plugin.registerPlugin('foo', Foo), Foo, 'the plugin is returned');
  assert.strictEqual(Plugin.getPlugin('foo'), Foo, 'the plugin can be retrieved');
  assert.strictEqual(Object.prototype.toString.call(Player.prototype.foo), '[object Function]', 'the plugin has a factory function');
  assert.notStrictEqual(Player.prototype.foo, Foo, 'the function on the player prototype is a factory');

  Plugin.deregisterPlugin('foo');
});

QUnit.test('registerPlugin() illegal arguments', function(assert) {
  assert.throws(
    () => Plugin.registerPlugin(),
    new Error('Illegal plugin name, "undefined", must be a string, was undefined.'),
    'plugins must have a name'
  );

  assert.throws(
    () => Plugin.registerPlugin('foo'),
    new Error('Illegal plugin for "foo", must be a function, was undefined.'),
    'plugins require both arguments'
  );

  assert.throws(
    () => Plugin.registerPlugin('foo', {}),
    new Error('Illegal plugin for "foo", must be a function, was object.'),
    'plugins must be functions'
  );

  assert.throws(
    () => Plugin.registerPlugin('play', function() {}),
    new Error('Illegal plugin name, "play", cannot share a name with an existing player method!'),
    'plugins must be functions'
  );

  sinon.stub(log, 'warn');
  Plugin.registerPlugin('foo', function() {});
  Plugin.registerPlugin('foo', function() {});
  assert.strictEqual(log.warn.callCount, 1, 'warn on re-registering a plugin');
  log.warn.restore();
});

QUnit.test('getPlugin()', function(assert) {
  assert.ok(Plugin.getPlugin('basic'), 'the "basic" plugin exists');
  assert.ok(Plugin.getPlugin('mock'), 'the "mock" plugin exists');
  assert.strictEqual(Plugin.getPlugin(), undefined, 'returns undefined with no arguments');
  assert.strictEqual(Plugin.getPlugin('nonExistent'), undefined, 'returns undefined with non-existent plugin');
  assert.strictEqual(Plugin.getPlugin(123), undefined, 'returns undefined with an invalid type');
});

QUnit.test('getPluginVersion()', function(assert) {
  assert.strictEqual(Plugin.getPluginVersion('basic'), '', 'the basic plugin has no version');
  assert.strictEqual(Plugin.getPluginVersion('mock'), 'v1.2.3', 'a plugin with a version returns its version');
});

QUnit.test('getPlugins()', function(assert) {
  assert.strictEqual(Object.keys(Plugin.getPlugins()).length, 3, 'all plugins are returned by default');
  assert.strictEqual(Plugin.getPlugins().basic, this.basic, 'the "basic" plugin is included');
  assert.strictEqual(Plugin.getPlugins().mock, MockPlugin, 'the "mock" plugin is included');
  assert.strictEqual(Plugin.getPlugins().plugin, Plugin, 'the "plugin" plugin is included');
  assert.strictEqual(Object.keys(Plugin.getPlugins(['basic'])).length, 1, 'a subset of plugins can be requested');
  assert.strictEqual(Plugin.getPlugins(['basic']).basic, this.basic, 'the correct subset of plugins is returned');
});

QUnit.test('deregisterPlugin()', function(assert) {
  const foo = () => {};

  Plugin.registerPlugin('foo', foo);
  Plugin.deregisterPlugin('foo');

  assert.strictEqual(Player.prototype.foo, undefined, 'the player prototype method is removed');
  assert.strictEqual(Plugin.getPlugin('foo'), undefined, 'the plugin can no longer be retrieved');

  assert.throws(
    () => Plugin.deregisterPlugin('plugin'),
    new Error('Cannot de-register base plugin.'),
    'the base plugin cannot be de-registered'
  );
});

QUnit.test('isBasic()', function(assert) {
  assert.ok(Plugin.isBasic(this.basic), 'the "basic" plugin is a basic plugin (by reference)');
  assert.ok(Plugin.isBasic('basic'), 'the "basic" plugin is a basic plugin (by name)');
  assert.notOk(Plugin.isBasic(MockPlugin), 'the "mock" plugin is NOT a basic plugin (by reference)');
  assert.notOk(Plugin.isBasic('mock'), 'the "mock" plugin is NOT a basic plugin (by name)');
});