Разработка собственного плагина

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


Архитектура плагина

Плагин в Karma представляет собой обычный Node.js модуль, который экспортирует объект или функцию через module.exports. Karma использует систему dependency injection, поэтому любой плагин должен быть зарегистрирован через механизм module.exports = { 'name:type': factory }.

Структура объекта плагина:

module.exports = {
  'type:name': ['factory', factoryFunction]
};
  • type — тип расширения (например, reporter, preprocessor, launcher, framework, middleware).
  • name — уникальное имя плагина.
  • factoryFunction — функция, создающая экземпляр плагина. Может получать зависимости через DI (например, config, logger).

Пример создания репортера

Репортер отвечает за вывод информации о тестах. Минимальный пример:

const MyReporter = function(baseReporterDecorator, config, logger, helper) {
  baseReporterDecorator(this);

  this.onRunSt art = function(browsers) {
    this.write('Тестирование началось\n');
  };

  this.onSpecCompl ete = function(browser, result) {
    if (result.success) {
      this.write(`Тест ${result.description} пройден\n`);
    } else {
      this.write(`Тест ${result.description} не пройден\n`);
    }
  };

  this.onRunCompl ete = function(browsers, results) {
    this.write(`Тестирование завершено: ${results.success} успешных, ${results.failed} неудачных\n`);
  };
};

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

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

Ключевые моменты:

  • baseReporterDecorator позволяет использовать стандартные методы Karma для вывода и форматирования.
  • Методы onRunStart, onSpecComplete, onRunComplete вызываются автоматически в ходе тестирования.
  • $inject определяет зависимости для DI, чтобы Karma корректно передала необходимые объекты.

Препроцессоры

Препроцессоры выполняют трансформацию файлов перед их подачей в браузер. Пример простого препроцессора для добавления комментария в каждый файл:

const myPreprocessor = function(logger, config, helper) {
  const log = logger.create('preprocessor.my-preprocessor');

  return function(content, file, done) {
    log.debug(`Обработка файла: ${file.originalPath}`);
    const processedContent = `// Добавлен комментарий\n${content}`;
    done(processedContent);
  };
};

myPreprocessor.$inject = ['logger', 'config', 'helper'];

module.exports = {
  'preprocessor:my-preprocessor': ['factory', myPreprocessor]
};

Особенности:

  • Функция препроцессора возвращает callback с обработанным содержимым.
  • Можно использовать сторонние библиотеки (например, Babel или TypeScript) для сложной трансформации.
  • Поддерживается асинхронная обработка файлов.

Создание собственного launchera

Launcher отвечает за запуск браузеров. Пример простого headless-старта Chrome:

const MyLauncher = function(baseLauncherDecorator, args, logger) {
  baseLauncherDecorator(this);

  this._start = function() {
    this._execCommand('google-chrome-headless', [
      '--no-sandbox',
      '--disable-gpu',
      '--remote-debugging-port=9222'
    ]);
  };
};

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

module.exports = {
  'launcher:my-chrome': ['type', MyLauncher]
};

Особенности:

  • baseLauncherDecorator предоставляет базовый функционал для управления процессом.
  • _execCommand запускает процесс браузера с указанными аргументами.
  • Можно реализовать сложные сценарии запуска, включая виртуальные окружения и контейнеры.

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

  1. Плагин помещается в отдельную папку и указывается в package.json как обычный модуль Node.js.
  2. В конфигурации Karma подключение плагина выполняется через plugins:
module.exports = function(config) {
  config.set({
    frameworks: ['jasmine'],
    files: ['test/**/*.spec.js'],
    reporters: ['my-reporter'],
    preprocessors: {
      'src/**/*.js': ['my-preprocessor']
    },
    plugins: [
      'karma-jasmine',
      'karma-chrome-launcher',
      require('./karma-my-plugin')
    ]
  });
};

Особенности подключения:

  • Можно комбинировать стандартные плагины и кастомные.
  • Karma автоматически ищет типы плагинов (reporter, launcher, preprocessor) и регистрирует их по имени.
  • Возможна динамическая конфигурация через объект config.

Работа с зависимостями и логированием

  • DI (Dependency Injection) — основной механизм Karma для передачи зависимостей в плагины.
  • Logger — предоставляет логирование с уровнями debug, info, warn, error.
  • Helper — набор утилит для работы с путями, шаблонами и асинхронными операциями.
  • Использование DI делает плагин тестируемым и независимым от глобальных переменных.

Тестирование собственного плагина

Для проверки плагина создается отдельный тестовый проект с минимальной конфигурацией Karma. Рекомендуется использовать стандартные фреймворки тестирования (Jasmine, Mocha) для написания unit-тестов, проверяющих:

  • Корректность регистрации плагина.
  • Вызовы ключевых методов (onRunStart, _start, препроцессор).
  • Совместимость с различными версиями браузеров.

Советы по оптимизации

  • Разделять функционал на маленькие фабрики и утилиты.
  • Использовать кеширование результатов препроцессоров для ускорения сборки.
  • Поддерживать строгие имена для типов плагинов, чтобы избежать конфликтов.
  • Обеспечивать асинхронную обработку, особенно для работы с сетью и файлами.

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