templateRenderer = Mockery::mock(TemplateRenderer::class);
$this->fragmentRenderer = new FragmentRenderer($this->templateRenderer);
});
afterEach(function () {
Mockery::close();
});
it('renders single fragment from template', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['count' => 5]));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');
$fullHtml = <<
Count: 5
HTML;
$this->templateRenderer
->shouldReceive('render')
->once()
->with('livecomponent-counter', ['count' => 5])
->andReturn($fullHtml);
$fragments = $this->fragmentRenderer->renderFragments(
$component,
['counter-display']
);
expect($fragments->isEmpty())->toBeFalse();
expect($fragments->has('counter-display'))->toBeTrue();
$displayFragment = $fragments->get('counter-display');
expect($displayFragment)->toContain('Count: 5');
expect($displayFragment)->toContain('span');
});
it('renders multiple fragments from template', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['count' => 10]));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');
$fullHtml = <<
Count: 10
HTML;
$this->templateRenderer
->shouldReceive('render')
->once()
->andReturn($fullHtml);
$fragments = $this->fragmentRenderer->renderFragments(
$component,
['counter-display', 'counter-controls']
);
expect($fragments->count())->toBe(2);
expect($fragments->has('counter-display'))->toBeTrue();
expect($fragments->has('counter-controls'))->toBeTrue();
expect($fragments->get('counter-display'))->toContain('Count: 10');
expect($fragments->get('counter-controls'))->toContain('Increment');
expect($fragments->get('counter-controls'))->toContain('Decrement');
});
it('returns empty collection when fragments not found', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('counter:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-counter');
$fullHtml = 'No fragments
';
$this->templateRenderer
->shouldReceive('render')
->once()
->andReturn($fullHtml);
$fragments = $this->fragmentRenderer->renderFragments(
$component,
['non-existent-fragment']
);
expect($fragments->isEmpty())->toBeTrue();
});
it('handles nested fragments correctly', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('form:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-form');
$fullHtml = <<
HTML;
$this->templateRenderer
->shouldReceive('render')
->once()
->andReturn($fullHtml);
$fragments = $this->fragmentRenderer->renderFragments(
$component,
['form-fields', 'form-errors']
);
expect($fragments->count())->toBe(2);
expect($fragments->has('form-fields'))->toBeTrue();
expect($fragments->has('form-errors'))->toBeTrue();
// Nested fragment should still be extractable
expect($fragments->get('form-errors'))->toContain('Invalid email');
});
it('preserves HTML structure in fragments', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('card:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray(['title' => 'Test Card']));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-card');
$fullHtml = <<
HTML;
$this->templateRenderer
->shouldReceive('render')
->once()
->andReturn($fullHtml);
$fragments = $this->fragmentRenderer->renderFragments(
$component,
['card-header']
);
$header = $fragments->get('card-header');
// Should preserve attributes and nested structure
expect($header)->toContain('class="card-header"');
expect($header)->toContain('Test Card
');
expect($header)->toContain('class="badge"');
});
it('handles empty fragment list', function () {
$component = Mockery::mock(LiveComponent::class);
$component->shouldReceive('getId')->andReturn(ComponentId::fromString('test:demo'));
$component->shouldReceive('getData')->andReturn(ComponentData::fromArray([]));
$component->shouldReceive('getTemplateName')->andReturn('livecomponent-test');
// Should not call renderer if no fragments requested
$this->templateRenderer->shouldNotReceive('render');
$fragments = $this->fragmentRenderer->renderFragments($component, []);
expect($fragments->isEmpty())->toBeTrue();
});
});