examples() as $ex) {
$reason = $skip[$ex['number']] ?? null;
$label = sprintf('#%d %s', $ex['number'], $ex['section']);
yield $label => [$ex['markdown'], $ex['html'], $reason];
}
}
/**
* @dataProvider specProvider
*/
public function testExample(string $md, string $expected, ?string $skipReason): void
{
if ($skipReason !== null) {
$this->markTestSkipped($skipReason);
}
$actual = $this->renderMarkdown($md);
$this->assertHtmlEquals($expected, $actual);
}
public function tearDown(): void
{
ModeRegistry::reset();
parent::tearDown();
}
/**
* Render markdown text through DokuWiki's full parser + XHTML renderer
* pipeline under the `markdown` syntax setting.
*/
private function renderMarkdown(string $text): string
{
global $conf;
$conf['syntax'] = 'markdown';
ModeRegistry::reset();
$instructions = p_get_instructions($text);
$info = [];
return p_render('xhtml', $instructions, $info);
}
/**
* Assert two HTML strings are equivalent after whitespace normalization.
*
* DokuWiki's XHTML renderer emits extra whitespace around block tags
* that the spec's reference HTML omits. The comparator strips whitespace
* only around **block-level** tags (p, div, h1-h6, ul/ol/li, table/tr/td,
* blockquote, pre, hr). Whitespace around **inline** tags (em, strong,
* a, code, span, img, br, etc.) is preserved, because `x y`
* and `xy` render differently.
*/
private function assertHtmlEquals(string $expected, string $actual): void
{
$this->assertEquals(
$this->normalizeHtml($expected),
$this->normalizeHtml($actual)
);
}
/**
* Strip whitespace adjacent to block-level tags; leave inline tags alone.
*/
private function normalizeHtml(string $html): string
{
$block = 'p|div|h[1-6]|hr|ul|ol|li|blockquote|pre|table|thead|tbody|tfoot|tr|th|td';
// Whitespace before/after an opening block tag (including attributes)
$html = preg_replace('#\s*<(' . $block . ')((?:\s[^>]*)?)>\s*#', '<$1$2>', $html);
// Whitespace before/after a closing block tag
$html = preg_replace('#\s*(' . $block . ')>\s*#', '$1>', $html);
return trim($html);
}
}