- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
253 lines
8.1 KiB
PHP
253 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Application\Admin\Service;
|
|
|
|
use App\Application\Admin\Service\AdminLayoutProcessor;
|
|
use App\Application\Admin\Service\AdminNavigationService;
|
|
use App\Application\Admin\ValueObjects\AdminLayoutData;
|
|
use App\Application\Admin\ValueObjects\BreadcrumbCollection;
|
|
use App\Application\Admin\ValueObjects\NavigationMenu;
|
|
use App\Framework\Http\HttpRequest;
|
|
|
|
// Test stub for AdminNavigationService since it's final
|
|
class TestAdminNavigationService
|
|
{
|
|
private array $menuData = [];
|
|
|
|
private array $breadcrumbsData = [];
|
|
|
|
private bool $shouldFailMenu = false;
|
|
|
|
private bool $shouldFailBreadcrumbs = false;
|
|
|
|
public function setMenuData(array $menuData): void
|
|
{
|
|
$this->menuData = $menuData;
|
|
}
|
|
|
|
public function setBreadcrumbsData(array $breadcrumbsData): void
|
|
{
|
|
$this->breadcrumbsData = $breadcrumbsData;
|
|
}
|
|
|
|
public function setShouldFailMenu(bool $shouldFail): void
|
|
{
|
|
$this->shouldFailMenu = $shouldFail;
|
|
}
|
|
|
|
public function setShouldFailBreadcrumbs(bool $shouldFail): void
|
|
{
|
|
$this->shouldFailBreadcrumbs = $shouldFail;
|
|
}
|
|
|
|
public function getNavigationMenu(): array
|
|
{
|
|
if ($this->shouldFailMenu) {
|
|
throw new \Exception('Navigation service failed');
|
|
}
|
|
|
|
return $this->menuData;
|
|
}
|
|
|
|
public function getBreadcrumbs(string $currentPath): array
|
|
{
|
|
if ($this->shouldFailBreadcrumbs) {
|
|
throw new \Exception('Breadcrumbs service failed');
|
|
}
|
|
|
|
return $this->breadcrumbsData;
|
|
}
|
|
}
|
|
|
|
// Test stub for HttpRequest since it's readonly
|
|
class TestHttpRequest
|
|
{
|
|
public string $path = '/admin';
|
|
}
|
|
|
|
describe('AdminLayoutProcessor', function () {
|
|
beforeEach(function () {
|
|
$this->navigationService = new TestAdminNavigationService();
|
|
$this->request = new TestHttpRequest();
|
|
|
|
// Use reflection to create AdminLayoutProcessor with our test doubles
|
|
$reflection = new \ReflectionClass(AdminLayoutProcessor::class);
|
|
$constructor = $reflection->getConstructor();
|
|
|
|
// Create instance using reflection to bypass readonly constraints
|
|
$this->processor = $reflection->newInstanceWithoutConstructor();
|
|
|
|
// Set the private properties
|
|
$navProperty = $reflection->getProperty('navigationService');
|
|
$navProperty->setAccessible(true);
|
|
$navProperty->setValue($this->processor, $this->navigationService);
|
|
|
|
$requestProperty = $reflection->getProperty('request');
|
|
$requestProperty->setAccessible(true);
|
|
$requestProperty->setValue($this->processor, $this->request);
|
|
});
|
|
|
|
it('processes admin layout data with navigation and breadcrumbs', function () {
|
|
$this->request->path = '/admin/dashboard';
|
|
|
|
$menuData = [
|
|
'System' => [
|
|
'icon' => 'server',
|
|
'items' => [
|
|
'Dashboard' => '/admin',
|
|
'Health Check' => '/admin/system/health',
|
|
],
|
|
],
|
|
];
|
|
|
|
$breadcrumbsData = [
|
|
['name' => 'Admin', 'url' => '/admin'],
|
|
['name' => 'Dashboard', 'url' => '/admin/dashboard'],
|
|
];
|
|
|
|
$this->navigationService->setMenuData($menuData);
|
|
$this->navigationService->setBreadcrumbsData($breadcrumbsData);
|
|
|
|
$inputData = new AdminLayoutData(
|
|
title: 'Test Page',
|
|
navigationMenu: new NavigationMenu([]),
|
|
breadcrumbs: new BreadcrumbCollection([]),
|
|
currentPath: '/admin'
|
|
);
|
|
|
|
$result = $this->processor->processAdminLayout($inputData);
|
|
|
|
expect($result)->toBeInstanceOf(AdminLayoutData::class);
|
|
expect($result->navigationMenu->sections)->toHaveCount(1);
|
|
expect($result->navigationMenu->sections[0]->name)->toBe('System');
|
|
expect($result->navigationMenu->sections[0]->items)->toHaveCount(2);
|
|
expect($result->breadcrumbs->breadcrumbs)->toHaveCount(2);
|
|
});
|
|
|
|
it('handles navigation service failure gracefully', function () {
|
|
$this->request->path = '/admin/test';
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getNavigationMenu')
|
|
->once()
|
|
->andThrow(new \Exception('Navigation service failed'));
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getBreadcrumbs')
|
|
->with('/admin/test')
|
|
->once()
|
|
->andReturn([['name' => 'Admin', 'url' => '/admin']]);
|
|
|
|
$inputData = new AdminLayoutData(
|
|
title: 'Test Page',
|
|
navigationMenu: new NavigationMenu([]),
|
|
breadcrumbs: new BreadcrumbCollection([]),
|
|
currentPath: '/admin'
|
|
);
|
|
|
|
$result = $this->processor->processAdminLayout($inputData);
|
|
|
|
// Should have fallback menu
|
|
expect($result->navigationMenu->sections)->toHaveCount(1);
|
|
expect($result->navigationMenu->sections[0]->name)->toBe('System');
|
|
expect($result->navigationMenu->sections[0]->items)->toHaveCount(2);
|
|
expect($result->navigationMenu->sections[0]->items[0]->name)->toBe('Dashboard');
|
|
});
|
|
|
|
it('handles breadcrumbs service failure gracefully', function () {
|
|
$this->request->path = '/admin/test';
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getNavigationMenu')
|
|
->once()
|
|
->andReturn([]);
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getBreadcrumbs')
|
|
->with('/admin/test')
|
|
->once()
|
|
->andThrow(new \Exception('Breadcrumbs service failed'));
|
|
|
|
$inputData = new AdminLayoutData(
|
|
title: 'Test Page',
|
|
navigationMenu: new NavigationMenu([]),
|
|
breadcrumbs: new BreadcrumbCollection([]),
|
|
currentPath: '/admin'
|
|
);
|
|
|
|
$result = $this->processor->processAdminLayout($inputData);
|
|
|
|
// Should have fallback breadcrumbs
|
|
expect($result->breadcrumbs->breadcrumbs)->toHaveCount(1);
|
|
expect($result->breadcrumbs->breadcrumbs[0]->name)->toBe('Admin');
|
|
expect($result->breadcrumbs->breadcrumbs[0]->url)->toBe('/admin');
|
|
});
|
|
|
|
it('sets active state for navigation items based on current path', function () {
|
|
$this->request->path = '/admin/system/health';
|
|
|
|
$menuData = [
|
|
'System' => [
|
|
'items' => [
|
|
'Dashboard' => '/admin',
|
|
'Health Check' => '/admin/system/health',
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getNavigationMenu')
|
|
->once()
|
|
->andReturn($menuData);
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getBreadcrumbs')
|
|
->once()
|
|
->andReturn([]);
|
|
|
|
$inputData = new AdminLayoutData(
|
|
title: 'Health Check',
|
|
navigationMenu: new NavigationMenu([]),
|
|
breadcrumbs: new BreadcrumbCollection([]),
|
|
currentPath: '/admin'
|
|
);
|
|
|
|
$result = $this->processor->processAdminLayout($inputData);
|
|
|
|
$items = $result->navigationMenu->sections[0]->items;
|
|
expect($items[0]->isActive)->toBeFalse(); // Dashboard
|
|
expect($items[1]->isActive)->toBeTrue(); // Health Check (current path)
|
|
});
|
|
|
|
it('preserves original layout data properties', function () {
|
|
$this->request->path = '/admin';
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getNavigationMenu')
|
|
->once()
|
|
->andReturn([]);
|
|
|
|
$this->navigationService
|
|
->shouldReceive('getBreadcrumbs')
|
|
->once()
|
|
->andReturn([]);
|
|
|
|
$inputData = new AdminLayoutData(
|
|
title: 'Original Title',
|
|
navigationMenu: new NavigationMenu([]),
|
|
breadcrumbs: new BreadcrumbCollection([]),
|
|
currentPath: '/admin',
|
|
metaDescription: 'Original description',
|
|
pageClass: 'original-class'
|
|
);
|
|
|
|
$result = $this->processor->processAdminLayout($inputData);
|
|
|
|
expect($result->title)->toBe('Original Title');
|
|
expect($result->metaDescription)->toBe('Original description');
|
|
expect($result->pageClass)->toBe('original-class');
|
|
});
|
|
});
|