'; } } class Renderer { public function render(string $xml): string { return '

ok

'; } } class Configurator { public static bool $returnEmpty = false; public object $plugins; public object $tags; public function __construct() { $this->plugins = new class { public function load(string $name): object { return new class { public function addFromRepository(string $name): self { return $this; } }; } }; $this->tags = new class implements \ArrayAccess { public array $store = []; public function add($name) { $obj = new \stdClass(); $this->store[$name] = $obj; return $obj; } public function offsetExists($offset): bool { return array_key_exists($offset, $this->store); } public function offsetGet($offset): mixed { return $this->store[$offset] ?? null; } public function offsetSet($offset, $value): void { $this->store[$offset] = $value; } public function offsetUnset($offset): void { unset($this->store[$offset]); } }; $this->tags['QUOTE'] = new \stdClass(); } public function finalize(): array { if (self::$returnEmpty) { return []; } return [ 'parser' => new Parser(), 'renderer' => new Renderer(), ]; } } } namespace { use App\Actions\BbcodeFormatter; it('returns empty string for null and empty input', function (): void { expect(BbcodeFormatter::format(null))->toBe(''); expect(BbcodeFormatter::format(''))->toBe(''); }); it('formats bbcode content', function (): void { $parserProp = new ReflectionProperty(BbcodeFormatter::class, 'parser'); $parserProp->setAccessible(true); $parserProp->setValue( \Mockery::mock(\s9e\TextFormatter\Parser::class) ->shouldReceive('parse') ->andReturn('') ->getMock() ); $rendererProp = new ReflectionProperty(BbcodeFormatter::class, 'renderer'); $rendererProp->setAccessible(true); $rendererProp->setValue( \Mockery::mock(\s9e\TextFormatter\Renderer::class) ->shouldReceive('render') ->andReturn('Bold') ->getMock() ); $html = BbcodeFormatter::format('[b]Bold[/b]'); expect($html)->toContain(''); }); it('initializes parser and renderer when not set', function (): void { $parserProp = new ReflectionProperty(BbcodeFormatter::class, 'parser'); $parserProp->setAccessible(true); $parserProp->setValue(null); $rendererProp = new ReflectionProperty(BbcodeFormatter::class, 'renderer'); $rendererProp->setAccessible(true); $rendererProp->setValue(null); $html = BbcodeFormatter::format('[b]Bold[/b]'); expect($html)->toBeString(); expect($parserProp->getValue())->not->toBeNull(); expect($rendererProp->getValue())->not->toBeNull(); }); it('throws when bbcode formatter cannot initialize', function (): void { \s9e\TextFormatter\Configurator::$returnEmpty = true; $parserProp = new ReflectionProperty(BbcodeFormatter::class, 'parser'); $parserProp->setAccessible(true); $parserProp->setValue(null); $rendererProp = new ReflectionProperty(BbcodeFormatter::class, 'renderer'); $rendererProp->setAccessible(true); $rendererProp->setValue(null); try { BbcodeFormatter::format('test'); $this->fail('Expected exception not thrown.'); } catch (Throwable $e) { expect($e)->toBeInstanceOf(RuntimeException::class); } finally { \s9e\TextFormatter\Configurator::$returnEmpty = false; } }); afterEach(function (): void { \Mockery::close(); }); }