Тестирование обработки ошибок

Основы обработки ошибок

Remark и Rehype строят обработку Markdown и HTML через систему плагинов, где каждый плагин может изменять дерево синтаксиса (AST) или трансформировать контент. Ошибки в процессе могут возникать на нескольких уровнях: синтаксическом, семантическом или логическом.

  • Синтаксические ошибки появляются при разборе исходного текста, например, некорректно закрытые теги HTML или неправильное использование Markdown-разметки.
  • Семантические ошибки связаны с логикой работы плагинов, когда дерево AST корректно построено, но преобразование не соответствует ожиданиям.
  • Ошибки выполнения появляются во время работы плагина, когда код сталкивается с недопустимыми типами данных или отсутствующими свойствами.

Remark и Rehype предоставляют встроенные механизмы для обнаружения и передачи этих ошибок. Основным инструментом является объект VFile, который хранит как содержимое файла, так и метаданные, включая сообщения об ошибках (messages). Каждое сообщение содержит:

  • reason — текст ошибки,
  • position — диапазон в исходном тексте,
  • fatal — булево значение, определяющее критичность ошибки.

Обработка ошибок на уровне плагинов

Каждый плагин в Remark/Rehype реализуется как функция, принимающая AST и объект VFile. Для корректного тестирования важно, чтобы плагин возвращал ошибки через file.message:

function examplePlugin(options) {
  return (tree, file) => {
    visit(tree, 'text', (node) => {
      if (node.value.includes('TODO')) {
        file.message('Обнаружено TODO в тексте', node.position, 'example-plugin/todo');
      }
    });
  };
}
  • visit — утилита для обхода дерева AST.
  • file.message создает объект ошибки и добавляет его в массив сообщений.

Ошибки можно помечать как фатальные, если нужно прервать обработку:

file.fail('Критическая ошибка: недопустимый контент', node.position);

Метод fail автоматически завершает обработку и генерирует исключение, что полезно для строгой валидации.

Юнит-тесты обработки ошибок

Тестирование плагинов должно проверять:

  1. Появление ожидаемых ошибок Используется генерация фиктивных AST или исходного текста, на котором плагин должен сработать:
import {unified} from 'unified';
import remarkParse from 'remark-parse';
import {examplePlugin} from './plugins/example-plugin.js';
import {expect} from 'chai';

const processor = unified().use(remarkParse).use(examplePlugin);

it('должен сообщать об ошибке при наличии TODO', () => {
  const file = processor.processSync('Здесь есть TODO');
  expect(file.messages).to.have.lengthOf(1);
  expect(file.messages[0].reason).to.include('TODO');
});
  1. Обработка фатальных ошибок Проверяется, что использование file.fail корректно прерывает обработку:
it('должен прерывать обработку при критической ошибке', () => {
  expect(() => processor.processSync('Некорректный контент'))
    .to.throw('Критическая ошибка');
});
  1. Проверка позиции ошибки Тесты должны проверять, что диапазон position соответствует реальному месту в исходном тексте:
expect(file.messages[0].position.start.line).to.equal(1);
expect(file.messages[0].position.start.column).to.equal(11);

Интеграционное тестирование цепочек плагинов

Remark и Rehype позволяют строить сложные цепочки плагинов. Ошибки могут возникать на любом этапе:

  • Важно, чтобы каждый плагин корректно передавал сообщения об ошибках в объект VFile.
  • Обработчик ошибок на уровне процессора должен агрегировать сообщения:
const processor = unified()
  .use(remarkParse)
  .use(pluginA)
  .use(pluginB);

const file = await processor.process('Исходный текст');

file.messages.forEach(msg => {
  console.log(msg.reason, msg.position);
});
  • Для интеграционных тестов создаются сценарии с несколькими плагинами, проверяя, что ошибки одного плагина не затираются и корректно передаются в конце обработки.

Логирование и диагностика

Для анализа сложных цепочек рекомендуется использовать логирование ошибок:

file.messages.forEach(msg => {
  console.error(`[${msg.source}] ${msg.reason} (строка ${msg.position.start.line})`);
});
  • source помогает идентифицировать плагин, который сгенерировал ошибку.
  • Логирование полезно при разработке пользовательских плагинов или модификации чужих цепочек.

Проверка некорректного HTML в Rehype

Rehype использует аналогичную систему сообщений. Для тестирования HTML-парсинга можно проверять:

  • Отсутствие закрывающих тегов
  • Некорректное вложение элементов
  • Недопустимые атрибуты
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(rehypeParse, {fragment: true})
  .use(rehypeStringify);

const file = processor.processSync('<div><span>Текст</div>');

file.messages.forEach(msg => {
  console.log(msg.reason); // Сообщения о несоответствии структуры
});

Сообщения об ошибках позволяют автоматизировать проверку HTML-контента перед дальнейшей обработкой.

Автоматизация тестирования

Для систематического контроля рекомендуется:

  • Интегрировать тесты в CI/CD
  • Использовать фикстуры с известными ошибками
  • Проверять как наличие, так и точное содержание сообщений
  • Параллельно тестировать корректный контент, чтобы убедиться, что ошибки не генерируются без причины

Такая практика обеспечивает высокую надежность цепочек Remark/Rehype и позволяет безопасно добавлять новые плагины или модифицировать существующие.