fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Application\Admin\Cms\Services\ContentPreviewService;
|
||||
use App\Domain\Cms\Services\ContentRenderer;
|
||||
use App\Domain\Cms\ValueObjects\BlockData;
|
||||
use App\Domain\Cms\ValueObjects\BlockId;
|
||||
use App\Domain\Cms\ValueObjects\BlockType;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlock;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlocks;
|
||||
use App\Framework\DateTime\Clock;
|
||||
|
||||
describe('ContentPreviewService', function () {
|
||||
beforeEach(function () {
|
||||
$this->clock = new Clock();
|
||||
|
||||
// Create a mock ContentRenderer
|
||||
$this->contentRenderer = Mockery::mock(ContentRenderer::class);
|
||||
$this->service = new ContentPreviewService(
|
||||
$this->contentRenderer,
|
||||
$this->clock
|
||||
);
|
||||
});
|
||||
|
||||
it('renders preview HTML from ContentBlocks', function () {
|
||||
$blocks = ContentBlocks::fromArray([
|
||||
[
|
||||
'id' => 'block-1',
|
||||
'type' => 'text',
|
||||
'data' => ['content' => '<p>Test content</p>'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->contentRenderer
|
||||
->shouldReceive('render')
|
||||
->once()
|
||||
->andReturn('<div class="cms-text"><p>Test content</p></div>');
|
||||
|
||||
$html = $this->service->renderPreview($blocks);
|
||||
|
||||
expect($html)->toBeString();
|
||||
expect($html)->not->toBeEmpty();
|
||||
expect($html)->toContain('Test content');
|
||||
});
|
||||
|
||||
it('renders multiple blocks in preview', function () {
|
||||
$blocks = ContentBlocks::fromArray([
|
||||
[
|
||||
'id' => 'block-1',
|
||||
'type' => 'hero',
|
||||
'data' => ['title' => 'Hero Title'],
|
||||
],
|
||||
[
|
||||
'id' => 'block-2',
|
||||
'type' => 'text',
|
||||
'data' => ['content' => '<p>Text content</p>'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->contentRenderer
|
||||
->shouldReceive('render')
|
||||
->once()
|
||||
->andReturn('<div class="cms-hero">Hero Title</div><div class="cms-text"><p>Text content</p></div>');
|
||||
|
||||
$html = $this->service->renderPreview($blocks);
|
||||
|
||||
expect($html)->toBeString();
|
||||
expect($html)->toContain('Hero Title');
|
||||
expect($html)->toContain('Text content');
|
||||
});
|
||||
|
||||
it('renders empty blocks as empty string', function () {
|
||||
$blocks = ContentBlocks::fromArray([]);
|
||||
|
||||
$this->contentRenderer
|
||||
->shouldReceive('render')
|
||||
->once()
|
||||
->andReturn('');
|
||||
|
||||
$html = $this->service->renderPreview($blocks);
|
||||
|
||||
expect($html)->toBe('');
|
||||
});
|
||||
|
||||
it('does not use caching for preview', function () {
|
||||
$blocks = ContentBlocks::fromArray([
|
||||
[
|
||||
'id' => 'block-1',
|
||||
'type' => 'text',
|
||||
'data' => ['content' => '<p>Test</p>'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->contentRenderer
|
||||
->shouldReceive('render')
|
||||
->once()
|
||||
->andReturn('<div>Test</div>');
|
||||
|
||||
// Call twice - should render both times (no caching)
|
||||
$html1 = $this->service->renderPreview($blocks);
|
||||
$html2 = $this->service->renderPreview($blocks);
|
||||
|
||||
expect($html1)->toBe($html2);
|
||||
// Verify render was called twice (no caching)
|
||||
$this->contentRenderer->shouldHaveReceived('render')->twice();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -199,3 +199,8 @@ describe('AdminLayoutProcessor', function () {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Application\Admin\Cms\Services\ContentPreviewService;
|
||||
use App\Application\LiveComponents\Admin\BlockEditor\BlockEditorComponent;
|
||||
use App\Application\LiveComponents\Admin\BlockEditor\BlockEditorState;
|
||||
use App\Domain\Cms\Services\BlockTemplateService;
|
||||
use App\Domain\Cms\Services\BlockTypeRegistry;
|
||||
use App\Framework\LiveComponents\ComponentRegistry;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
|
||||
describe('BlockEditorComponent', function () {
|
||||
beforeEach(function () {
|
||||
$this->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('<div>Preview HTML</div>');
|
||||
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Application\LiveComponents\Drawer;
|
||||
|
||||
use App\Application\LiveComponents\Drawer\DrawerComponent;
|
||||
use App\Application\LiveComponents\Drawer\DrawerState;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentData;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class DrawerComponentTest extends TestCase
|
||||
{
|
||||
public function testDrawerComponentCanBeCreated(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
isOpen: false,
|
||||
position: 'left',
|
||||
width: '400px'
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(DrawerComponent::class, $component);
|
||||
$this->assertFalse($component->state->isOpen);
|
||||
$this->assertEquals('left', $component->state->position);
|
||||
}
|
||||
|
||||
public function testOpenActionOpensDrawer(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test')
|
||||
);
|
||||
|
||||
$newState = $component->open('Test Title', 'Test Content', 'right', '500px');
|
||||
|
||||
$this->assertTrue($newState->isOpen);
|
||||
$this->assertEquals('Test Title', $newState->title);
|
||||
$this->assertEquals('Test Content', $newState->content);
|
||||
$this->assertEquals('right', $newState->position);
|
||||
$this->assertEquals('500px', $newState->width);
|
||||
}
|
||||
|
||||
public function testCloseActionClosesDrawer(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
isOpen: true
|
||||
);
|
||||
|
||||
$newState = $component->close();
|
||||
|
||||
$this->assertFalse($newState->isOpen);
|
||||
}
|
||||
|
||||
public function testToggleActionTogglesDrawer(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
isOpen: false
|
||||
);
|
||||
|
||||
$openedState = $component->toggle();
|
||||
$this->assertTrue($openedState->isOpen);
|
||||
|
||||
// Simulate state update
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
initialData: ComponentData::fromArray($openedState->toArray())
|
||||
);
|
||||
|
||||
$closedState = $component->toggle();
|
||||
$this->assertFalse($closedState->isOpen);
|
||||
}
|
||||
|
||||
public function testUpdateContentActionUpdatesContent(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
isOpen: true,
|
||||
content: 'Old Content'
|
||||
);
|
||||
|
||||
$newState = $component->updateContent('New Content');
|
||||
|
||||
$this->assertEquals('New Content', $newState->content);
|
||||
$this->assertTrue($newState->isOpen); // State should remain open
|
||||
}
|
||||
|
||||
public function testChangePositionActionChangesPosition(): void
|
||||
{
|
||||
$component = new DrawerComponent(
|
||||
id: ComponentId::fromString('drawer:test'),
|
||||
position: 'left'
|
||||
);
|
||||
|
||||
$newState = $component->changePosition('right');
|
||||
|
||||
$this->assertEquals('right', $newState->position);
|
||||
}
|
||||
|
||||
public function testDrawerStateFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'is_open' => true,
|
||||
'position' => 'right',
|
||||
'width' => '500px',
|
||||
'show_overlay' => true,
|
||||
'title' => 'Test',
|
||||
'content' => 'Content',
|
||||
'close_on_overlay' => true,
|
||||
'close_on_escape' => true,
|
||||
'animation' => 'slide',
|
||||
'z_index' => 1050,
|
||||
];
|
||||
|
||||
$state = DrawerState::fromArray($data);
|
||||
|
||||
$this->assertTrue($state->isOpen);
|
||||
$this->assertEquals('right', $state->position);
|
||||
$this->assertEquals('500px', $state->width);
|
||||
}
|
||||
|
||||
public function testDrawerStateToArray(): void
|
||||
{
|
||||
$state = new DrawerState(
|
||||
isOpen: true,
|
||||
position: 'left',
|
||||
width: '400px'
|
||||
);
|
||||
|
||||
$array = $state->toArray();
|
||||
|
||||
$this->assertTrue($array['is_open']);
|
||||
$this->assertEquals('left', $array['position']);
|
||||
$this->assertEquals('400px', $array['width']);
|
||||
}
|
||||
|
||||
public function testDrawerStatePositionHelpers(): void
|
||||
{
|
||||
$leftState = new DrawerState(position: 'left');
|
||||
$rightState = new DrawerState(position: 'right');
|
||||
|
||||
$this->assertTrue($leftState->isLeft());
|
||||
$this->assertFalse($leftState->isRight());
|
||||
$this->assertTrue($rightState->isRight());
|
||||
$this->assertFalse($rightState->isLeft());
|
||||
}
|
||||
|
||||
public function testDrawerStateGetPositionClass(): void
|
||||
{
|
||||
$state = new DrawerState(position: 'left');
|
||||
$this->assertEquals('drawer--left', $state->getPositionClass());
|
||||
|
||||
$state = new DrawerState(position: 'right');
|
||||
$this->assertEquals('drawer--right', $state->getPositionClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Application\LiveComponents\Popover;
|
||||
|
||||
use App\Application\LiveComponents\Popover\PopoverComponent;
|
||||
use App\Application\LiveComponents\Popover\PopoverState;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentData;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class PopoverComponentTest extends TestCase
|
||||
{
|
||||
public function testPopoverComponentCanBeCreated(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
isVisible: false,
|
||||
position: 'top',
|
||||
anchorId: 'test-anchor'
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(PopoverComponent::class, $component);
|
||||
$this->assertFalse($component->state->isVisible);
|
||||
$this->assertEquals('top', $component->state->position);
|
||||
$this->assertEquals('test-anchor', $component->state->anchorId);
|
||||
}
|
||||
|
||||
public function testShowActionShowsPopover(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test')
|
||||
);
|
||||
|
||||
$newState = $component->show('Test Content', 'Test Title', 'test-anchor', 'bottom');
|
||||
|
||||
$this->assertTrue($newState->isVisible);
|
||||
$this->assertEquals('Test Content', $newState->content);
|
||||
$this->assertEquals('Test Title', $newState->title);
|
||||
$this->assertEquals('test-anchor', $newState->anchorId);
|
||||
$this->assertEquals('bottom', $newState->position);
|
||||
}
|
||||
|
||||
public function testHideActionHidesPopover(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
isVisible: true
|
||||
);
|
||||
|
||||
$newState = $component->hide();
|
||||
|
||||
$this->assertFalse($newState->isVisible);
|
||||
}
|
||||
|
||||
public function testToggleActionTogglesPopover(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
isVisible: false
|
||||
);
|
||||
|
||||
$visibleState = $component->toggle();
|
||||
$this->assertTrue($visibleState->isVisible);
|
||||
|
||||
// Simulate state update
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
initialData: ComponentData::fromArray($visibleState->toArray())
|
||||
);
|
||||
|
||||
$hiddenState = $component->toggle();
|
||||
$this->assertFalse($hiddenState->isVisible);
|
||||
}
|
||||
|
||||
public function testUpdatePositionActionUpdatesPosition(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
position: 'top'
|
||||
);
|
||||
|
||||
$newState = $component->updatePosition('right');
|
||||
|
||||
$this->assertEquals('right', $newState->position);
|
||||
}
|
||||
|
||||
public function testUpdateAnchorActionUpdatesAnchor(): void
|
||||
{
|
||||
$component = new PopoverComponent(
|
||||
id: ComponentId::fromString('popover:test'),
|
||||
anchorId: 'old-anchor'
|
||||
);
|
||||
|
||||
$newState = $component->updateAnchor('new-anchor');
|
||||
|
||||
$this->assertEquals('new-anchor', $newState->anchorId);
|
||||
}
|
||||
|
||||
public function testPopoverStateFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'is_visible' => true,
|
||||
'position' => 'bottom',
|
||||
'anchor_id' => 'test-anchor',
|
||||
'content' => 'Test Content',
|
||||
'title' => 'Test Title',
|
||||
'show_arrow' => true,
|
||||
'offset' => 10,
|
||||
'close_on_outside_click' => true,
|
||||
'z_index' => 1060,
|
||||
];
|
||||
|
||||
$state = PopoverState::fromArray($data);
|
||||
|
||||
$this->assertTrue($state->isVisible);
|
||||
$this->assertEquals('bottom', $state->position);
|
||||
$this->assertEquals('test-anchor', $state->anchorId);
|
||||
}
|
||||
|
||||
public function testPopoverStateToArray(): void
|
||||
{
|
||||
$state = new PopoverState(
|
||||
isVisible: true,
|
||||
position: 'top',
|
||||
anchorId: 'test-anchor'
|
||||
);
|
||||
|
||||
$array = $state->toArray();
|
||||
|
||||
$this->assertTrue($array['is_visible']);
|
||||
$this->assertEquals('top', $array['position']);
|
||||
$this->assertEquals('test-anchor', $array['anchor_id']);
|
||||
}
|
||||
|
||||
public function testPopoverStateIsAutoPosition(): void
|
||||
{
|
||||
$autoState = new PopoverState(position: 'auto');
|
||||
$topState = new PopoverState(position: 'top');
|
||||
|
||||
$this->assertTrue($autoState->isAutoPosition());
|
||||
$this->assertFalse($topState->isAutoPosition());
|
||||
}
|
||||
|
||||
public function testPopoverStateGetPositionClass(): void
|
||||
{
|
||||
$state = new PopoverState(position: 'top');
|
||||
$this->assertEquals('popover--top', $state->getPositionClass());
|
||||
|
||||
$state = new PopoverState(position: 'bottom');
|
||||
$this->assertEquals('popover--bottom', $state->getPositionClass());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user