componentId = ComponentId::generate('admin-block-editor');
$this->blockTypeRegistry = new BlockTypeRegistry();
$this->componentRegistry = Mockery::mock(ComponentRegistry::class);
// Mock ContentPreviewService
$this->previewService = Mockery::mock(ContentPreviewService::class);
$this->previewService
->shouldReceive('renderPreview')
->andReturn('
Preview HTML
');
// Mock BlockTemplateService
$this->templateService = Mockery::mock(BlockTemplateService::class);
$this->templateService
->shouldReceive('getAllTemplates')
->andReturn([
'landing-page' => [
'name' => 'Landing Page',
'description' => 'Test template',
'blocks' => [],
],
]);
$this->state = BlockEditorState::empty();
});
it('creates component with initial state', function () {
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
expect($component->id)->toBe($this->componentId);
expect($component->state)->toBe($this->state);
});
it('adds a new block', function () {
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$newState = $component->addBlock('hero');
expect($newState->blocks)->toHaveCount(1);
expect($newState->blocks[0]['type'])->toBe('hero');
});
it('does not add block for invalid block type', function () {
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$newState = $component->addBlock('invalid-type');
expect($newState->blocks)->toBeEmpty();
});
it('removes a block', function () {
$state = $this->state->withBlockAdded('hero', ['title' => 'Test']);
$component = new BlockEditorComponent(
id: $this->componentId,
state: $state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$blockId = $state->blocks[0]['id'];
$newState = $component->removeBlock($blockId);
expect($newState->blocks)->toBeEmpty();
});
it('updates block field', function () {
$state = $this->state->withBlockAdded('text', ['content' => 'Old content']);
$component = new BlockEditorComponent(
id: $this->componentId,
state: $state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$blockId = $state->blocks[0]['id'];
$newState = $component->updateBlockField($blockId, 'content', 'New content');
expect($newState->blocks[0]['data']['content'])->toBe('New content');
});
it('reorders blocks', function () {
$state = $this->state
->withBlockAdded('hero', ['title' => 'Hero'])
->withBlockAdded('text', ['content' => 'Text']);
$component = new BlockEditorComponent(
id: $this->componentId,
state: $state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$blockIds = array_map(fn ($block) => $block['id'], $state->blocks);
$reorderedIds = array_reverse($blockIds);
$newState = $component->reorderBlocks($reorderedIds);
expect($newState->blocks[0]['type'])->toBe('text');
expect($newState->blocks[1]['type'])->toBe('hero');
});
it('toggles preview mode', function () {
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$newState = $component->togglePreview();
expect($newState->isPreviewMode)->toBeTrue();
expect($newState->previewHtml)->not->toBeNull();
});
it('applies template', function () {
$this->templateService
->shouldReceive('hasTemplate')
->with('landing-page')
->andReturn(true);
$this->templateService
->shouldReceive('getTemplate')
->with('landing-page')
->andReturn([
'name' => 'Landing Page',
'description' => 'Test',
'blocks' => [
[
'type' => 'hero',
'data' => ['title' => 'Hero Title'],
],
],
]);
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$newState = $component->applyTemplate('landing-page');
expect($newState->blocks)->not->toBeEmpty();
expect($newState->blocks[0]['type'])->toBe('hero');
});
it('does not apply invalid template', function () {
$this->templateService
->shouldReceive('hasTemplate')
->with('invalid')
->andReturn(false);
$component = new BlockEditorComponent(
id: $this->componentId,
state: $this->state,
blockTypeRegistry: $this->blockTypeRegistry,
previewService: $this->previewService,
componentRegistry: $this->componentRegistry,
templateService: $this->templateService
);
$newState = $component->applyTemplate('invalid');
expect($newState->blocks)->toBeEmpty();
});
});