Создание собственного препроцессора

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

Препроцессор в Karma — это функция, которая получает исходный код файла и возвращает его обработанную версию через callback или промис. Он регистрируется в конфигурации с помощью объекта preprocessors и связки с ключом-паттерном, указывающим на файлы, которые нужно обработать.

module.exports = function(config) {
  config.set({
    preprocessors: {
      '**/*.js': ['myCustomPreprocessor']
    },
    plugins: [
      'karma-*',
      require('./karma-my-preprocessor')
    ]
  });
};

Создание собственного препроцессора

Препроцессор представляет собой модуль, экспортирующий фабричную функцию. Фабрика принимает объект logger и возвращает функцию, обрабатывающую исходные файлы. Функция препроцессора должна соответствовать интерфейсу (content, file, done), где:

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

Пример минимального собственного препроцессора:

var createPreprocessor = function(logger) {
  var log = logger.create('preprocessor:myCustomPreprocessor');

  return function(content, file, done) {
    log.debug('Обрабатывается файл: ' + file.originalPath);
    
    // Простейшая замена текста в файле
    var transformed = content.replace(/console\.log/g, 'customLogger.log');

    done(transformed);
  };
};

createPreprocessor.$inject = ['logger'];

module.exports = {
  'preprocessor:myCustomPreprocessor': ['factory', createPreprocessor]
};

Регистрация и интеграция препроцессора

После написания модуля его необходимо зарегистрировать в конфигурации Karma. Это делается через параметр plugins и указание препроцессора в объекте preprocessors. Karma автоматически подхватывает плагин и применяет его к указанным файлам.

Пример расширенной конфигурации:

module.exports = function(config) {
  config.set({
    frameworks: ['jasmine'],
    files: [
      'src/**/*.js',
      'test/**/*.spec.js'
    ],
    preprocessors: {
      'src/**/*.js': ['myCustomPreprocessor'],
      'test/**/*.spec.js': ['myCustomPreprocessor']
    },
    plugins: [
      'karma-jasmine',
      'karma-chrome-launcher',
      require('./karma-my-preprocessor')
    ]
  });
};

Асинхронная обработка

Препроцессор может выполнять асинхронные операции, например, загрузку внешних данных, компиляцию TypeScript или чтение файлов. Для этого done можно вызывать после завершения промиса или асинхронного вызова.

return function(content, file, done) {
  someAsyncTransformation(content)
    .then(transformed => done(transformed))
    .catch(err => done(file.error(err)));
};

Тестирование и отладка

Логирование является ключевым инструментом для разработки собственного препроцессора. Karma предоставляет объект logger, который поддерживает уровни debug, info, warn и error. Использование log.debug позволяет отслеживать порядок обработки файлов и внутренние состояния без засорения консоли во время обычного запуска тестов.

Особенности работы с путями и зависимостями

Препроцессор получает полные пути файлов, что позволяет строить зависимости и определять структуру проекта. Объект file содержит метод originalPath, который указывает исходное расположение файла до любых изменений пути. Это особенно полезно при генерации исходного кода в отдельную директорию или внедрении source maps.

Использование source maps

Для препроцессоров, которые изменяют код, критично сохранять соответствие исходного и скомпилированного кода через source maps. Karma не генерирует их автоматически, но позволяет передавать объект sourceMap вместе с результатом:

done({
  code: transformedCode,
  map: sourceMapObject
});

Это обеспечивает корректную навигацию в инструментах разработчика и правильное отображение ошибок тестов.

Множественные препроцессоры

Karma поддерживает последовательное применение нескольких препроцессоров к одному файлу. Порядок обработки определяется порядком массива в конфигурации. Это позволяет комбинировать, например, компиляцию TypeScript с минификацией или внедрением mock-функций для тестирования.

preprocessors: {
  'src/**/*.ts': ['typescript', 'myCustomPreprocessor']
}

Практические рекомендации

  • Всегда проверять работу с пустыми и некорректными файлами.
  • Логирование на уровне debug помогает отследить внутренние изменения кода.
  • Для крупных проектов использовать source maps обязательно для сохранения читаемости ошибок.
  • Для асинхронных операций важно корректно обрабатывать ошибки через file.error() или reject промиса.
  • Собственные препроцессоры должны быть независимыми и не менять глобальное состояние, чтобы не влиять на другие тестовые файлы.

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