Files
xamxam/tests/phpunit/MarkdownHelperTest.php
Pontoporeia 622aac5e58 fix(phpstan): resolve 6 pre-existing type errors + 2 cs-fixer nits
- AdminAuth: drop dead password_hash() === false check (PHP 8 returns string)
- ExportController: remove redundant !== null after isset() on duration fields
- SystemController: $def['json'] is always present, drop ?? false
- TfeController: simplify always-true type guard when sorting TFE files
- validate-file-fragment: AV 5GB override condition was always true, apply unconditionally
- tests: single-quote string, sort use statements (php-cs-fixer)
2026-09-18 16:41:26 +02:00

139 lines
4.8 KiB
PHP

<?php
use League\CommonMark\CommonMarkConverter;
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use PHPUnit\Framework\TestCase;
/**
* MarkdownHelperTest — TOC extraction from markdown headings.
*
* Regression: TOC labels used to show raw markdown (`*Licence*`, `**Gras**`,
* `` `code` ``), because the raw heading text was passed straight to the
* template. Labels must be plain text and hrefs must match the heading ids
* CommonMark generates on the rendered page.
*/
class MarkdownHelperTest extends TestCase
{
/**
* Render markdown exactly like the content controllers do, and return the
* heading ids in document order.
*/
private function renderedHeadingIds(string $markdown): array
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'heading_permalink' => [
'apply_id_to_heading' => true,
'id_prefix' => '',
'insert' => 'before',
'aria_hidden' => true,
],
]);
$converter->getEnvironment()->addExtension(new HeadingPermalinkExtension());
$html = $converter->convert($markdown)->getContent();
preg_match_all('/<h[1-3] id="([^"]+)"/', $html, $matches);
return $matches[1];
}
// ── Labels are plain text ─────────────────────────────────────────────────
public function testItalicMarkupIsStrippedFromLabel(): void
{
$toc = MarkdownHelper::extractToc('## *Licence* CC');
$this->assertSame('Licence CC', $toc[0]['label']);
}
public function testBoldMarkupIsStrippedFromLabel(): void
{
$toc = MarkdownHelper::extractToc('## **Gros** gras');
$this->assertSame('Gros gras', $toc[0]['label']);
}
public function testInlineCodeMarkupIsStrippedFromLabel(): void
{
$toc = MarkdownHelper::extractToc('## `code` inline');
$this->assertSame('code inline', $toc[0]['label']);
}
public function testLinkKeepsLabelTextNotUrl(): void
{
$toc = MarkdownHelper::extractToc('## [lien](http://x) ici');
$this->assertSame('lien ici', $toc[0]['label']);
}
public function testNestedMarkupIsFullyStripped(): void
{
$toc = MarkdownHelper::extractToc('## *[lien](http://x)* et `code`');
$this->assertSame('lien et code', $toc[0]['label']);
}
public function testNoMarkdownSyntaxLeaksIntoAnyLabel(): void
{
$markdown = "## *Italique*\n\n## **Gras**\n\n## `Code`\n\n## [Lien](http://x)\n";
$labels = array_column(MarkdownHelper::extractToc($markdown), 'label');
foreach ($labels as $label) {
$this->assertDoesNotMatchRegularExpression('/[*`]|\[|\]\(/', $label);
}
}
// ── Hrefs match rendered heading ids ──────────────────────────────────────
public function testHrefsMatchRenderedHeadingIdsForInlineMarkup(): void
{
$markdown = "## *Licence* CC\n\n## **Gros** gras\n\n## `code` inline\n\n## [lien](http://x) ici\n";
$toc = MarkdownHelper::extractToc($markdown);
$headingIds = $this->renderedHeadingIds($markdown);
foreach ($toc as $index => $item) {
$this->assertSame('#' . $headingIds[$index], $item['href'], "TOC href mismatch at index {$index}");
}
}
public function testHrefsMatchRenderedHeadingIdsForAccents(): void
{
$markdown = "## Été accents\n\n## Emoji 🎉 fin\n";
$toc = MarkdownHelper::extractToc($markdown);
$headingIds = $this->renderedHeadingIds($markdown);
foreach ($toc as $index => $item) {
$this->assertSame('#' . $headingIds[$index], $item['href'], "TOC href mismatch at index {$index}");
}
}
// ── Levels and structure ──────────────────────────────────────────────────
public function testLevelsAreCapturedFromHashes(): void
{
$markdown = "# Un\n\n## Deux\n\n### Trois\n";
$toc = MarkdownHelper::extractToc($markdown);
$this->assertSame([1, 2, 3], array_column($toc, 'level'));
}
public function testHeadingsDeeperThanThreeAreIgnored(): void
{
$toc = MarkdownHelper::extractToc("# Un\n\n#### Quatre\n");
$this->assertCount(1, $toc);
}
public function testContentWithoutHeadingsYieldsEmptyToc(): void
{
$this->assertSame([], MarkdownHelper::extractToc("Juste du texte.\n\nEt un paragraphe."));
}
public function testEmptyContentYieldsEmptyToc(): void
{
$this->assertSame([], MarkdownHelper::extractToc(''));
}
}