- 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
175 lines
5.2 KiB
PHP
175 lines
5.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\DI\DefaultContainer;
|
|
use App\Framework\Template\Parser\DomTemplateParser;
|
|
use App\Framework\View\ComponentRenderer;
|
|
use App\Framework\View\DomComponentService;
|
|
use App\Framework\View\Processors\ComponentProcessor;
|
|
use App\Framework\View\Processors\PlaceholderReplacer;
|
|
use App\Framework\View\RenderContext;
|
|
|
|
beforeEach(function () {
|
|
$this->container = new DefaultContainer();
|
|
|
|
// Setup dependencies
|
|
$this->container->singleton(
|
|
PlaceholderReplacer::class,
|
|
new PlaceholderReplacer($this->container)
|
|
);
|
|
|
|
$this->componentService = new DomComponentService();
|
|
$this->componentRenderer = new ComponentRenderer(
|
|
__DIR__ . '/../../../src/Framework/View/templates/components'
|
|
);
|
|
|
|
$this->processor = new ComponentProcessor(
|
|
$this->componentService,
|
|
$this->componentRenderer,
|
|
$this->container
|
|
);
|
|
|
|
$this->parser = new DomTemplateParser();
|
|
});
|
|
|
|
describe('ComponentProcessor', function () {
|
|
it('processes simple component without attributes', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="alert" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(
|
|
template: 'test',
|
|
data: [
|
|
'type' => 'info',
|
|
'message' => 'Test message',
|
|
]
|
|
);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
expect($output)->toContain('alert alert-info');
|
|
expect($output)->toContain('Test message');
|
|
});
|
|
|
|
it('processes component with attributes', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="alert" type="warning" message="Custom message" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(template: 'test', data: []);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
expect($output)->toContain('alert alert-warning');
|
|
expect($output)->toContain('Custom message');
|
|
});
|
|
|
|
it('processes nested components', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="card" title="Test Card" message="Card content" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(template: 'test', data: []);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
expect($output)->toContain('card');
|
|
expect($output)->toContain('Test Card');
|
|
expect($output)->toContain('Card content');
|
|
});
|
|
|
|
it('handles components with placeholder variables', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="alert" type="{{ alertType }}" message="{{ alertMessage }}" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(
|
|
template: 'test',
|
|
data: [
|
|
'alertType' => 'success',
|
|
'alertMessage' => 'Operation successful',
|
|
]
|
|
);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
expect($output)->toContain('alert alert-success');
|
|
expect($output)->toContain('Operation successful');
|
|
});
|
|
|
|
it('gracefully handles missing component files', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="non-existent-component" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(template: 'test', data: []);
|
|
|
|
// Should not throw exception
|
|
$result = $this->processor->process($dom, $context);
|
|
|
|
expect($result)->toBeInstanceOf(App\Framework\View\DomWrapper::class);
|
|
});
|
|
|
|
it('processes multiple components in sequence', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component name="alert" type="info" message="First alert" />
|
|
<component name="alert" type="warning" message="Second alert" />
|
|
<component name="alert" type="danger" message="Third alert" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(template: 'test', data: []);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
expect($output)->toContain('First alert');
|
|
expect($output)->toContain('Second alert');
|
|
expect($output)->toContain('Third alert');
|
|
expect($output)->toContain('alert-info');
|
|
expect($output)->toContain('alert-warning');
|
|
expect($output)->toContain('alert-danger');
|
|
});
|
|
|
|
it('skips components without name attribute', function () {
|
|
$html = <<<HTML
|
|
<div>
|
|
<component />
|
|
<component name="alert" type="info" message="Valid component" />
|
|
</div>
|
|
HTML;
|
|
|
|
$dom = $this->parser->parseToWrapper($html);
|
|
$context = new RenderContext(template: 'test', data: []);
|
|
|
|
$result = $this->processor->process($dom, $context);
|
|
$output = $result->document->saveHTML();
|
|
|
|
// Should only process the valid component
|
|
expect($output)->toContain('Valid component');
|
|
});
|
|
});
|