justReturn(true); Functions\when('wp_doing_ajax')->justReturn(false); $this->renderer = new BlockRenderer(); } protected function tearDown(): void { Monkey\tearDown(); parent::tearDown(); } // ── core/table ────────────────────────────────────────────── public function testRenderTableAddsTableClass(): void { $html = '
A
1
'; $block = ['attrs' => []]; $result = $this->renderer->renderTable($html, $block); $this->assertStringContainsString('table', $this->classesOf('table', $result)); } public function testRenderTableWithStripesAddsStripedClass(): void { $html = '
1
'; $block = ['attrs' => ['className' => 'is-style-stripes']]; $result = $this->renderer->renderTable($html, $block); $classes = $this->classesOf('table', $result); $this->assertStringContainsString('table', $classes); $this->assertStringContainsString('table-striped', $classes); } public function testRenderTableEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderTable('', [])); } public function testRenderTableWithoutTableTagReturnsOriginal(): void { $html = '

No table here

'; $this->assertSame($html, $this->renderer->renderTable($html, [])); } // ── core/button ───────────────────────────────────────────── public function testRenderButtonAddsBtnPrimaryByDefault(): void { $html = '
Click
'; $block = ['attrs' => []]; $result = $this->renderer->renderButton($html, $block); $classes = $this->classesOf('a', $result); $this->assertStringContainsString('btn', $classes); $this->assertStringContainsString('btn-primary', $classes); } public function testRenderButtonWithBackgroundColor(): void { $html = '
Click
'; $block = ['attrs' => ['backgroundColor' => 'danger']]; $result = $this->renderer->renderButton($html, $block); $classes = $this->classesOf('a', $result); $this->assertStringContainsString('btn', $classes); $this->assertStringContainsString('btn-danger', $classes); } public function testRenderButtonOutlineStyle(): void { $html = '
Click
'; $block = ['attrs' => ['className' => 'is-style-outline', 'textColor' => 'success']]; $result = $this->renderer->renderButton($html, $block); $classes = $this->classesOf('a', $result); $this->assertStringContainsString('btn', $classes); $this->assertStringContainsString('btn-outline-success', $classes); } public function testRenderButtonOutlineDefaultsToPrimary(): void { $html = '
Click
'; $block = ['attrs' => ['className' => 'is-style-outline']]; $result = $this->renderer->renderButton($html, $block); $this->assertStringContainsString('btn-outline-primary', $this->classesOf('a', $result)); } public function testRenderButtonWithGradientOnlyAddsBtnBase(): void { $html = '
Click
'; $block = ['attrs' => ['gradient' => 'vivid-cyan-blue']]; $result = $this->renderer->renderButton($html, $block); $classes = $this->classesOf('a', $result); $this->assertStringContainsString('btn', $classes); $this->assertStringNotContainsString('btn-primary', $classes); } public function testRenderButtonWithUnknownColorDefaultsToPrimary(): void { $html = '
Click
'; $block = ['attrs' => ['backgroundColor' => 'custom-teal']]; $result = $this->renderer->renderButton($html, $block); $this->assertStringContainsString('btn-primary', $this->classesOf('a', $result)); } public function testRenderButtonEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderButton('', [])); } // ── core/buttons ──────────────────────────────────────────── public function testRenderButtonsAddsFlexClasses(): void { $html = '
A
'; $block = ['attrs' => []]; $result = $this->renderer->renderButtons($html, $block); $classes = $this->classesOfFirst('.wp-block-buttons', $result); $this->assertStringContainsString('d-flex', $classes); $this->assertStringContainsString('flex-wrap', $classes); $this->assertStringContainsString('gap-2', $classes); } public function testRenderButtonsEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderButtons('', [])); } // ── core/image ────────────────────────────────────────────── public function testRenderImageAddsImgFluid(): void { $html = '
Photo
'; $block = ['attrs' => []]; $result = $this->renderer->renderImage($html, $block); $this->assertStringContainsString('img-fluid', $this->classesOf('img', $result)); } public function testRenderImageEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderImage('', [])); } // ── core/search ───────────────────────────────────────────── public function testRenderSearchAddsBootstrapClasses(): void { $html = ''; $block = ['attrs' => []]; $result = $this->renderer->renderSearch($html, $block); $this->assertStringContainsString('input-group', $result); $this->assertStringContainsString('form-control', $result); $this->assertStringContainsString('btn-primary', $result); } public function testRenderSearchEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderSearch('', [])); } // ── core/quote ────────────────────────────────────────────── public function testRenderQuoteAddsBlockquoteClass(): void { $html = '

Quote text

'; $block = ['attrs' => []]; $result = $this->renderer->renderQuote($html, $block); $this->assertStringContainsString('blockquote', $this->classesOf('blockquote', $result)); } public function testRenderQuoteWithCiteAddsFooterClass(): void { $html = '

Quote

Author
'; $block = ['attrs' => []]; $result = $this->renderer->renderQuote($html, $block); $this->assertStringContainsString('blockquote-footer', $result); } public function testRenderQuoteWithoutCite(): void { $html = '

Quote only

'; $block = ['attrs' => []]; $result = $this->renderer->renderQuote($html, $block); $this->assertStringContainsString('blockquote', $this->classesOf('blockquote', $result)); $this->assertStringNotContainsString('blockquote-footer', $result); } public function testRenderQuoteEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderQuote('', [])); } // ── core/pullquote ────────────────────────────────────────── public function testRenderPullquoteAddsBlockquoteClass(): void { $html = '

Pull

'; $block = ['attrs' => []]; $result = $this->renderer->renderPullquote($html, $block); $this->assertStringContainsString('blockquote', $this->classesOf('blockquote', $result)); } public function testRenderPullquoteWithCiteAddsFooterClass(): void { $html = '

Pull

Source
'; $block = ['attrs' => []]; $result = $this->renderer->renderPullquote($html, $block); $this->assertStringContainsString('blockquote-footer', $result); } public function testRenderPullquoteEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderPullquote('', [])); } // ── core/list ─────────────────────────────────────────────── public function testRenderListGroupAddsClasses(): void { $html = ''; $block = ['attrs' => ['className' => 'is-style-list-group']]; $result = $this->renderer->renderList($html, $block); $this->assertStringContainsString('list-group', $this->classesOf('ul', $result)); $this->assertStringContainsString('list-group-item', $result); } public function testRenderListGroupOrderedList(): void { $html = '
  1. 1
  2. 2
'; $block = ['attrs' => ['className' => 'is-style-list-group', 'ordered' => true]]; $result = $this->renderer->renderList($html, $block); $this->assertStringContainsString('list-group', $this->classesOf('ol', $result)); } public function testRenderListWithoutGroupStyleReturnsUnchanged(): void { $html = ''; $block = ['attrs' => []]; $result = $this->renderer->renderList($html, $block); $this->assertStringNotContainsString('list-group', $result); } public function testRenderListEmptyContentReturnsEmpty(): void { $this->assertSame('', $this->renderer->renderList('', [])); } // ── helpers ───────────────────────────────────────────────── /** * Extract the class attribute value of the first matching tag. */ private function classesOf(string $tagName, string $html): string { $doc = new \DOMDocument(); @$doc->loadHTML('' . $html . '', LIBXML_HTML_NODEFDTD); $elements = $doc->getElementsByTagName($tagName); return $elements->length > 0 ? ($elements->item(0)->getAttribute('class') ?? '') : ''; } /** * Extract classes from the first element matching a CSS selector-style class. */ private function classesOfFirst(string $selector, string $html): string { $className = ltrim($selector, '.'); $doc = new \DOMDocument(); @$doc->loadHTML('' . $html . '', LIBXML_HTML_NODEFDTD); $xpath = new \DOMXPath($doc); $body = $doc->getElementsByTagName('body')->item(0); if (!$body) { return ''; } $nodes = $xpath->query(sprintf( ".//*[contains(concat(' ', normalize-space(@class), ' '), ' %s ')]", $className ), $body); return $nodes->length > 0 ? ($nodes->item(0)->getAttribute('class') ?? '') : ''; } }