You've already forked wc-bootstrap
191 lines
6.0 KiB
PHP
191 lines
6.0 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace WcBootstrap\Tests\Unit;
|
||
|
|
|
||
|
|
use PHPUnit\Framework\TestCase;
|
||
|
|
use Brain\Monkey;
|
||
|
|
use Brain\Monkey\Functions;
|
||
|
|
use WcBootstrap\TemplateOverride;
|
||
|
|
use WPBootstrap\Twig\TwigService;
|
||
|
|
|
||
|
|
class TemplateOverrideTest extends TestCase
|
||
|
|
{
|
||
|
|
private TemplateOverride $override;
|
||
|
|
private string $templatePath;
|
||
|
|
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
Monkey\setUp();
|
||
|
|
|
||
|
|
TwigService::reset();
|
||
|
|
|
||
|
|
// WC_BOOTSTRAP_PATH must point to the theme root so
|
||
|
|
// resolveTwigTemplate() can locate files under templates/.
|
||
|
|
$this->templatePath = dirname(__DIR__, 2) . '/';
|
||
|
|
if (!defined('WC_BOOTSTRAP_PATH')) {
|
||
|
|
define('WC_BOOTSTRAP_PATH', $this->templatePath);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->override = new TemplateOverride();
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void
|
||
|
|
{
|
||
|
|
TwigService::reset();
|
||
|
|
Monkey\tearDown();
|
||
|
|
parent::tearDown();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── register() ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
public function testRegisterAddsHooksWhenTwigServiceExists(): void
|
||
|
|
{
|
||
|
|
$calls = [];
|
||
|
|
Functions\when('add_action')->alias(function () use (&$calls): void {
|
||
|
|
$calls[] = func_get_args();
|
||
|
|
});
|
||
|
|
|
||
|
|
$this->override->register();
|
||
|
|
|
||
|
|
$this->assertCount(2, $calls);
|
||
|
|
$this->assertSame('woocommerce_before_template_part', $calls[0][0]);
|
||
|
|
$this->assertSame('woocommerce_after_template_part', $calls[1][0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── beforeTemplatePart / afterTemplatePart ───────────────────
|
||
|
|
|
||
|
|
public function testBeforeTemplatePartRendersAndBuffersWhenTwigTemplateExists(): void
|
||
|
|
{
|
||
|
|
// Use a real template file that exists in the theme.
|
||
|
|
// cart/cart.php -> cart/cart.html.twig
|
||
|
|
$templateName = 'cart/cart.php';
|
||
|
|
|
||
|
|
TwigService::setRenderCallback(function (string $tpl, array $ctx): string {
|
||
|
|
return '<div>twig-rendered</div>';
|
||
|
|
});
|
||
|
|
|
||
|
|
$this->override->beforeTemplatePart($templateName, '', '', []);
|
||
|
|
|
||
|
|
// Output buffer should be active — the PHP template output is being captured.
|
||
|
|
$level = ob_get_level();
|
||
|
|
$this->assertGreaterThan(0, $level);
|
||
|
|
|
||
|
|
// Now simulate the PHP template echoing something.
|
||
|
|
echo 'php-output-should-be-discarded';
|
||
|
|
|
||
|
|
$this->override->afterTemplatePart($templateName, '', '', []);
|
||
|
|
|
||
|
|
// The Twig output was already echoed before the buffer, so we
|
||
|
|
// just verify the buffer was cleaned (level decreased).
|
||
|
|
$this->assertSame($level - 1, ob_get_level());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testBeforeTemplatePartSkipsWhenNoTwigTemplate(): void
|
||
|
|
{
|
||
|
|
$levelBefore = ob_get_level();
|
||
|
|
|
||
|
|
// Use a template name that has no Twig override.
|
||
|
|
$this->override->beforeTemplatePart('nonexistent/template.php', '', '', []);
|
||
|
|
|
||
|
|
// No buffer should have been started.
|
||
|
|
$this->assertSame($levelBefore, ob_get_level());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testAfterTemplatePartIgnoresUnbufferedTemplate(): void
|
||
|
|
{
|
||
|
|
$levelBefore = ob_get_level();
|
||
|
|
|
||
|
|
// Calling after without a matching before should be safe.
|
||
|
|
$this->override->afterTemplatePart('cart/cart.php', '', '', []);
|
||
|
|
|
||
|
|
$this->assertSame($levelBefore, ob_get_level());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testNestedTemplatesHandledCorrectly(): void
|
||
|
|
{
|
||
|
|
// Both templates must exist as Twig files.
|
||
|
|
$outer = 'cart/cart.php';
|
||
|
|
$inner = 'cart/cart-empty.php';
|
||
|
|
|
||
|
|
TwigService::setRenderCallback(fn() => '<div>rendered</div>');
|
||
|
|
|
||
|
|
$levelBefore = ob_get_level();
|
||
|
|
|
||
|
|
$this->override->beforeTemplatePart($outer, '', '', []);
|
||
|
|
$outerLevel = ob_get_level();
|
||
|
|
|
||
|
|
$this->override->beforeTemplatePart($inner, '', '', []);
|
||
|
|
$innerLevel = ob_get_level();
|
||
|
|
|
||
|
|
$this->assertSame($outerLevel + 1, $innerLevel);
|
||
|
|
|
||
|
|
// Close inner first (stack order).
|
||
|
|
$this->override->afterTemplatePart($inner, '', '', []);
|
||
|
|
$this->assertSame($outerLevel, ob_get_level());
|
||
|
|
|
||
|
|
$this->override->afterTemplatePart($outer, '', '', []);
|
||
|
|
$this->assertSame($levelBefore, ob_get_level());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testBeforeTemplatePartPassesArgsAndProductToTwig(): void
|
||
|
|
{
|
||
|
|
$templateName = 'cart/cart.php';
|
||
|
|
$captured = null;
|
||
|
|
|
||
|
|
TwigService::setRenderCallback(function (string $tpl, array $ctx) use (&$captured): string {
|
||
|
|
$captured = $ctx;
|
||
|
|
return '';
|
||
|
|
});
|
||
|
|
|
||
|
|
$args = ['foo' => 'bar'];
|
||
|
|
$this->override->beforeTemplatePart($templateName, '', '', $args);
|
||
|
|
|
||
|
|
// Clean up the buffer.
|
||
|
|
$this->override->afterTemplatePart($templateName, '', '', $args);
|
||
|
|
|
||
|
|
$this->assertIsArray($captured);
|
||
|
|
$this->assertSame('bar', $captured['foo']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testBeforeTemplatePartInjectsGlobalProduct(): void
|
||
|
|
{
|
||
|
|
$templateName = 'cart/cart.php';
|
||
|
|
$captured = null;
|
||
|
|
|
||
|
|
$product = new \WC_Product(42);
|
||
|
|
$GLOBALS['product'] = $product;
|
||
|
|
|
||
|
|
TwigService::setRenderCallback(function (string $tpl, array $ctx) use (&$captured): string {
|
||
|
|
$captured = $ctx;
|
||
|
|
return '';
|
||
|
|
});
|
||
|
|
|
||
|
|
$this->override->beforeTemplatePart($templateName, '', '', []);
|
||
|
|
$this->override->afterTemplatePart($templateName, '', '', []);
|
||
|
|
|
||
|
|
$this->assertSame($product, $captured['product']);
|
||
|
|
|
||
|
|
unset($GLOBALS['product']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function testBeforeTemplatePartFallsBackOnRenderException(): void
|
||
|
|
{
|
||
|
|
$templateName = 'cart/cart.php';
|
||
|
|
|
||
|
|
TwigService::setRenderCallback(function (): string {
|
||
|
|
throw new \RuntimeException('Twig error');
|
||
|
|
});
|
||
|
|
|
||
|
|
$levelBefore = ob_get_level();
|
||
|
|
|
||
|
|
// Should not throw — falls back silently and lets PHP render.
|
||
|
|
@$this->override->beforeTemplatePart($templateName, '', '', []);
|
||
|
|
|
||
|
|
// No buffer started on failure.
|
||
|
|
$this->assertSame($levelBefore, ob_get_level());
|
||
|
|
}
|
||
|
|
}
|