Юнит-тестирование рендереров

Основы работы рендереров

В библиотеке Marked рендерер отвечает за преобразование синтаксических элементов Markdown в HTML. Каждый тип элемента имеет соответствующий метод рендерера: heading, paragraph, link, list, listitem и другие. Понимание структуры рендерера важно для юнит-тестирования, так как тесты должны проверять корректность вывода HTML при различных входных данных.

Рендереры в Marked создаются через класс Renderer:

const { Renderer } = require('marked');
const renderer = new Renderer();

Методы рендерера можно переопределять:

renderer.heading = (text, level) => `<h${level}>${text.toUpperCase()}</h${level}>`;

Юнит-тестирование должно проверять, что при вызове метода с определёнными аргументами возвращается ожидаемый HTML.

Настройка тестового окружения

Для тестирования рендереров рекомендуется использовать фреймворк Jest или Mocha с Chai. Основная цель — изолированное тестирование методов рендерера без необходимости парсить весь Markdown.

Пример базовой конфигурации Jest:

// renderer.test.js
const { Renderer } = require('marked');

describe('Renderer', () => {
    let renderer;

    beforeEach(() => {
        renderer = new Renderer();
    });

    test('heading генерирует корректный HTML', () => {
        const result = renderer.heading('Test', 2);
        expect(result).toBe('<h2>Test</h2>');
    });
});

Тестирование всех типов элементов

Заголовки

Метод heading(text, level) получает текст и уровень заголовка. Тесты должны покрывать все уровни от 1 до 6 и проверять правильное формирование HTML.

test.each([1,2,3,4,5,6])('heading уровня %i', (level) => {
    const result = renderer.heading('Title', level);
    expect(result).toBe(`<h${level}>Title</h${level}>`);
});
Параграфы

Метод paragraph(text) преобразует текст в тег <p>.

test('paragraph возвращает текст в тег <p>', () => {
    const result = renderer.paragraph('Hello world');
    expect(result).toBe('<p>Hello world</p>\n');
});
Списки и элементы списка

Методы list(body, ordered, start) и listitem(text) отвечают за генерацию <ul>/<ol> и <li>.

test('listitem формирует <li>', () => {
    const result = renderer.listitem('Item');
    expect(result).toBe('<li>Item</li>\n');
});

test('ordered list формируется с атрибутом start', () => {
    const result = renderer.list('Item', true, 3);
    expect(result).toBe('<ol start="3">\nItem</ol>\n');
});
Ссылки и изображения

Методы link(href, title, text) и image(href, title, text) позволяют тестировать генерацию атрибутов HTML.

test('link формирует корректный <a>', () => {
    const result = renderer.link('https://example.com', 'Example', 'Click');
    expect(result).toBe('<a href="https://example.com" title="Example">Click</a>');
});

test('image формирует <img>', () => {
    const result = renderer.image('img.png', 'Alt text', 'Image');
    expect(result).toBe('<img src="img.png" alt="Alt text" title="Image">');
});

Тестирование кастомных рендереров

Marked позволяет полностью переопределять методы рендерера. Юнит-тесты должны проверять поведение кастомных методов.

renderer.paragraph = text => `<p class="custom">${text}</p>`;

test('кастомный paragraph добавляет класс', () => {
    const result = renderer.paragraph('Custom text');
    expect(result).toBe('<p class="custom">Custom text</p>');
});

Проверка экранирования и безопасности

Некорректные данные, содержащие HTML или скрипты, должны корректно обрабатываться. Тесты должны проверять экранирование и предотвращение XSS-уязвимостей.

test('paragraph экранирует HTML', () => {
    const result = renderer.paragraph('<script>alert(1)</script>');
    expect(result).toBe('<p>&lt;script&gt;alert(1)&lt;/script&gt;</p>\n');
});

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

  • Покрывать тестами все методы рендерера, включая нестандартные.
  • Использовать test.each для параметризованных сценариев (разные уровни заголовков, списков и т.д.).
  • Проверять как стандартные, так и кастомные реализации методов.
  • Следить за корректным экранированием опасного HTML и специальных символов.

Юнит-тестирование рендереров Marked обеспечивает надёжность генерации HTML, предсказуемость кастомных настроек и защиту от ошибок форматирования или XSS. Оно позволяет безопасно модифицировать рендереры и внедрять кастомные шаблоны без риска поломки базовой функциональности.