Plugin API

Karma является гибким инструментом для тестирования JavaScript, а его Plugin API предоставляет механизмы расширения функциональности, позволяя интегрировать новые браузеры, фреймворки, препроцессоры и репортеры. Понимание этой системы критически важно для построения кастомизированных сред тестирования и создания собственных плагинов.


Структура плагина

Плагин в Karma — это модуль Node.js, который регистрируется в системе с использованием dependency injection. Основная структура плагина включает:

  1. Экспорт объекта или функции, возвращающей объект.
  2. Ключ name, указывающий на тип плагина, например, framework, launcher, reporter, preprocessor.
  3. Массив dependencies, описывающий сервисы, которые будут автоматически внедрены в плагин.
  4. Методы, реализующие функциональность.

Пример минимального плагина:

const MyReporter = function(baseReporterDecorator, config) {
  baseReporterDecorator(this);
  this.onRunCompl ete = function(browsers, results) {
    console.log('Тестирование завершено', results.success);
  };
};

MyReporter.$inject = ['baseReporterDecorator', 'config'];

module.exports = {
  'reporter:my-reporter': ['type', MyReporter]
};

Ключевой момент: имя плагина должно содержать префикс типа (reporter:, launcher:, framework:, preprocessor:), за которым следует уникальное имя.


Типы плагинов

1. Framework

Фреймворки интегрируются в процесс тестирования, добавляя специфические функции. Пример: подключение Mocha или Jasmine.

  • Реализация основывается на добавлении скриптов в браузер.
  • Используется метод framework с массивом зависимостей:
module.exports = {
  'framework:custom-framework': ['factory', function() {
    return {
      'framework:custom-framework': function(files) {
        files.unshift('path/to/custom-framework.js');
      }
    };
  }]
};
  • files.unshift гарантирует, что фреймворк будет подключён до тестов.

2. Launcher

Отвечает за запуск браузеров. Плагин должен реализовать метод launch:

function CustomLauncher(baseLauncherDecorator, args, logger) {
  baseLauncherDecorator(this);
  this.launch = function(url) {
    console.log('Запуск браузера с URL:', url);
    // Код запуска браузера
  };
}

CustomLauncher.$inject = ['baseLauncherDecorator', 'args', 'logger'];

module.exports = {
  'launcher:custom-browser': ['type', CustomLauncher]
};
  • baseLauncherDecorator обеспечивает стандартные методы: start, kill, onKill.
  • args позволяет передавать параметры из конфигурации Karma.
  • logger нужен для логирования работы плагина.

3. Preprocessor

Обрабатывает файлы перед отправкой в браузер. Обычно используется для транспиляции, минификации или внедрения шаблонов.

  • Обязательный метод: функция, принимающая содержимое файла:
function MyPreprocessor() {
  return function(content, file, done) {
    const transformed = content.toUpperCase(); // пример обработки
    done(transformed);
  };
}

module.exports = {
  'preprocessor:uppercase': ['factory', MyPreprocessor]
};
  • Аргументы функции:

    • content — исходный код файла.
    • file — объект с информацией о файле.
    • done — callback для передачи результата.

4. Reporter

Служит для вывода результатов тестов. Интерфейс определяется набором методов жизненного цикла:

  • onRunStart(browsers): начало запуска.
  • onBrowserStart(browser): запуск конкретного браузера.
  • onSpecComplete(browser, result): завершение одного теста.
  • onRunComplete(browsers, results): завершение всей серии тестов.
function SimpleReporter(baseReporterDecorator) {
  baseReporterDecorator(this);
  this.onSpecCompl ete = function(browser, result) {
    console.log(`[${browser.name}] ${result.description}: ${result.success ? 'OK' : 'FAIL'}`);
  };
}

SimpleReporter.$inject = ['baseReporterDecorator'];

module.exports = {
  'reporter:simple': ['type', SimpleReporter]
};

Dependency Injection

Karma использует систему DI, которая позволяет внедрять в плагины:

  • config — объект конфигурации Karma.
  • logger — логгер.
  • emitter — события тестирования.
  • helper — вспомогательные функции (например, formatError, isDefined).
  • fileList — список файлов проекта.

Пример внедрения:

function CustomReporter(baseReporterDecorator, config, logger) {
  baseReporterDecorator(this);
  const log = logger.create('CustomReporter');
  this.onRunSt art = function() {
    log.info('Начало тестирования');
  };
}
CustomReporter.$inject = ['baseReporterDecorator', 'config', 'logger'];

Регистрация и загрузка плагинов

  • Плагины можно указывать в karma.conf.js через массив plugins:
module.exports = function(config) {
  config.set({
    plugins: [
      'karma-jasmine',
      'karma-chrome-launcher',
      require('./my-custom-reporter')
    ],
    reporters: ['progress', 'my-reporter']
  });
};
  • Karma автоматически сканирует node_modules и подключает плагины с префиксом karma-.
  • Пользовательские плагины можно подключать напрямую через require.

Важные рекомендации по разработке

  • Всегда использовать декораторы базовых компонентов (baseReporterDecorator, baseLauncherDecorator) для совместимости.
  • Использовать $inject для явного указания зависимостей, чтобы избежать проблем при минификации.
  • Проверять, что методы плагина вызываются асинхронно, если операция долгосрочная.
  • Сохранять консистентность именования: тип:имя, чтобы Karma могла корректно распознать плагин.
  • Логирование через logger предпочтительнее console.log в продакшене.

События плагинов и жизненный цикл

Karma предоставляет множество событий, на которые плагин может подписаться:

  • browser_register — регистрация браузера.
  • file_list_modified — изменения в списке файлов.
  • run_start, run_complete — начало и конец тестового прогона.
  • browser_error, browser_complete — обработка ошибок браузера.

Пример подписки:

function EventReporter(emitter) {
  emitter.on('run_start', function() {
    console.log('Тесты стартовали');
  });
}

EventReporter.$inject = ['emitter'];

module.exports = {
  'reporter:event': ['type', EventReporter]
};

Эти события дают полный контроль над процессом тестирования и позволяют интегрировать дополнительные механизмы мониторинга или логирования.


Особенности асинхронных плагинов

  • Preprocessor и Reporter могут работать с асинхронными операциями.
  • Асинхронные функции должны использовать done callback или возвращать промис:
function AsyncPreprocessor() {
  return function(content, file, done) {
    setTimeout(() => done(content.split('').reverse().join('')), 100);
  };
}

module.exports = {
  'preprocessor:reverse': ['factory', AsyncPreprocessor]
};
  • Karma корректно ожидает завершения всех асинхронных операций перед продолжением тестового прогона.

Эта архитектура делает Plugin API Karma мощным инструментом для интеграции с любыми средами тестирования и позволяет создавать высоко кастомизированные решения без модификации ядра.