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,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\View\Dom\Transformer;
|
||||
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\View\Dom\Parser\HtmlParser;
|
||||
use App\Framework\View\Dom\Renderer\HtmlRenderer;
|
||||
use App\Framework\View\Dom\Transformer\PlaceholderTransformer;
|
||||
use App\Framework\View\RenderContext;
|
||||
use App\Framework\Meta\MetaData;
|
||||
|
||||
describe('PlaceholderTransformer', function () {
|
||||
beforeEach(function () {
|
||||
$this->container = new DefaultContainer();
|
||||
$this->parser = new HtmlParser();
|
||||
$this->renderer = new HtmlRenderer();
|
||||
$this->transformer = new PlaceholderTransformer($this->container);
|
||||
});
|
||||
|
||||
describe('fallback mechanism', function () {
|
||||
it('supports null coalescing operator', function () {
|
||||
$html = '<div>{{ $name ?? "Default Name" }}</div>';
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: []
|
||||
);
|
||||
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->transformer->transform($document, $context);
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
expect($output)->toContain('Default Name');
|
||||
});
|
||||
|
||||
it('uses actual value when variable exists', function () {
|
||||
$html = '<div>{{ $name ?? "Default Name" }}</div>';
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: ['name' => 'Actual Name']
|
||||
);
|
||||
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->transformer->transform($document, $context);
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
expect($output)->toContain('Actual Name');
|
||||
expect($output)->not->toContain('Default Name');
|
||||
});
|
||||
|
||||
it('handles null values with fallback', function () {
|
||||
$html = '<div>{{ $name ?? "Default" }}</div>';
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: ['name' => null]
|
||||
);
|
||||
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->transformer->transform($document, $context);
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
expect($output)->toContain('Default');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nested array access', function () {
|
||||
it('supports nested array access in placeholders', function () {
|
||||
$html = '<div>{{ $item["user"]["name"] }}</div>';
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'item' => [
|
||||
'user' => [
|
||||
'name' => 'John',
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->transformer->transform($document, $context);
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
expect($output)->toContain('John');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', function () {
|
||||
it('returns empty string for missing variables in production', function () {
|
||||
putenv('APP_DEBUG=false');
|
||||
$html = '<div>{{ $missing }}</div>';
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: []
|
||||
);
|
||||
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->transformer->transform($document, $context);
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Should not throw, but return empty string
|
||||
expect($output)->toContain('<div></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Framework\View\Dom\Transformer;
|
||||
|
||||
use App\Framework\Config\AppConfig;
|
||||
use App\Framework\DI\DefaultContainer;
|
||||
use App\Framework\LiveComponents\ComponentRegistry;
|
||||
use App\Framework\LiveComponents\Performance\ComponentMetadataCache;
|
||||
use App\Framework\LiveComponents\Performance\ComponentMetadataCacheInterface;
|
||||
use App\Framework\LiveComponents\Contracts\ComponentRegistryInterface;
|
||||
use App\Framework\Meta\MetaData;
|
||||
use App\Framework\View\Components\Button;
|
||||
use App\Framework\View\Dom\Parser\HtmlParser;
|
||||
use App\Framework\View\Dom\Renderer\HtmlRenderer;
|
||||
use App\Framework\View\Dom\Transformer\ForTransformer;
|
||||
use App\Framework\View\Dom\Transformer\PlaceholderTransformer;
|
||||
use App\Framework\View\Dom\Transformer\XComponentTransformer;
|
||||
use App\Framework\View\Processing\AstProcessingPipeline;
|
||||
use App\Framework\View\RenderContext;
|
||||
use App\Framework\View\StaticComponentRenderer;
|
||||
|
||||
describe('XComponentTransformer + ForTransformer Integration', function () {
|
||||
beforeEach(function () {
|
||||
$this->container = new DefaultContainer();
|
||||
$this->parser = new HtmlParser();
|
||||
$this->renderer = new HtmlRenderer();
|
||||
|
||||
// Create component registry and dependencies
|
||||
$discoveryRegistry = $this->container->get('App\Framework\Discovery\Results\DiscoveryRegistry');
|
||||
$liveComponentRenderer = $this->container->get('App\Framework\View\LiveComponentRenderer');
|
||||
$cacheManager = $this->container->get('App\Framework\LiveComponents\Cache\ComponentCacheManager');
|
||||
$handler = $this->container->get('App\Framework\LiveComponents\LiveComponentHandler');
|
||||
$metadataCache = new ComponentMetadataCache($this->container);
|
||||
$performanceTracker = $this->container->get('App\Framework\LiveComponents\Performance\NestedPerformanceTracker');
|
||||
|
||||
$this->componentRegistry = new ComponentRegistry(
|
||||
$this->container,
|
||||
$discoveryRegistry,
|
||||
$liveComponentRenderer,
|
||||
$cacheManager,
|
||||
$handler,
|
||||
$metadataCache,
|
||||
$performanceTracker
|
||||
);
|
||||
|
||||
$this->staticComponentRenderer = new StaticComponentRenderer();
|
||||
$this->metadataCache = $metadataCache;
|
||||
$this->appConfig = new AppConfig(['debug' => true]);
|
||||
|
||||
// Create transformers
|
||||
$this->forTransformer = new ForTransformer($this->container);
|
||||
$this->placeholderTransformer = new PlaceholderTransformer($this->container);
|
||||
$this->xComponentTransformer = new XComponentTransformer(
|
||||
$this->componentRegistry,
|
||||
$this->staticComponentRenderer,
|
||||
$this->metadataCache,
|
||||
$this->parser,
|
||||
$this->appConfig
|
||||
);
|
||||
});
|
||||
|
||||
it('transforms x-button components within for loops', function () {
|
||||
$html = <<<HTML
|
||||
<ul>
|
||||
<for items="{{pages}}" as="page">
|
||||
<li>
|
||||
<x-button variant="secondary" size="sm" href="{{page.url}}" class="page-link">{{page.number}}</x-button>
|
||||
</li>
|
||||
</for>
|
||||
</ul>
|
||||
HTML;
|
||||
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'pages' => [
|
||||
['number' => 1, 'url' => '/page/1'],
|
||||
['number' => 2, 'url' => '/page/2'],
|
||||
['number' => 3, 'url' => '/page/3'],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Process through pipeline: ForTransformer -> PlaceholderTransformer -> XComponentTransformer
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->forTransformer->transform($document, $context);
|
||||
$document = $this->placeholderTransformer->transform($document, $context);
|
||||
$document = $this->xComponentTransformer->transform($document, $context);
|
||||
|
||||
// Render to HTML
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Assert: No x-button tags should remain
|
||||
expect($output)->not->toContain('<x-button');
|
||||
expect($output)->not->toContain('</x-button>');
|
||||
|
||||
// Assert: Buttons should be rendered as <a> or <button> elements
|
||||
expect($output)->toContain('<a');
|
||||
expect($output)->toContain('href="/page/1"');
|
||||
expect($output)->toContain('href="/page/2"');
|
||||
expect($output)->toContain('href="/page/3"');
|
||||
expect($output)->toContain('1');
|
||||
expect($output)->toContain('2');
|
||||
expect($output)->toContain('3');
|
||||
});
|
||||
|
||||
it('transforms x-button components in pagination-like structure', function () {
|
||||
$html = <<<HTML
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
<li if="{{has_previous}}">
|
||||
<x-button variant="secondary" size="sm" href="{{previous_url}}" class="page-link">Previous</x-button>
|
||||
</li>
|
||||
<for items="{{pages}}" as="page">
|
||||
<li if="{{!page.ellipsis}}">
|
||||
<x-button variant="{{page.active ? 'primary' : 'secondary'}}" size="sm" href="{{page.url}}" class="page-link">{{page.number}}</x-button>
|
||||
</li>
|
||||
</for>
|
||||
<li if="{{has_next}}">
|
||||
<x-button variant="secondary" size="sm" href="{{next_url}}" class="page-link">Next</x-button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
HTML;
|
||||
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'has_previous' => true,
|
||||
'has_next' => true,
|
||||
'previous_url' => '/page/1',
|
||||
'next_url' => '/page/3',
|
||||
'pages' => [
|
||||
['number' => 1, 'url' => '/page/1', 'active' => true, 'ellipsis' => false],
|
||||
['number' => 2, 'url' => '/page/2', 'active' => false, 'ellipsis' => false],
|
||||
['number' => 3, 'url' => '/page/3', 'active' => false, 'ellipsis' => false],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Process through pipeline
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->forTransformer->transform($document, $context);
|
||||
$document = $this->placeholderTransformer->transform($document, $context);
|
||||
$document = $this->xComponentTransformer->transform($document, $context);
|
||||
|
||||
// Render to HTML
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Assert: No x-button tags should remain
|
||||
expect($output)->not->toContain('<x-button');
|
||||
expect($output)->not->toContain('</x-button>');
|
||||
|
||||
// Assert: All buttons should be rendered
|
||||
expect($output)->toContain('Previous');
|
||||
expect($output)->toContain('Next');
|
||||
expect($output)->toContain('href="/page/1"');
|
||||
expect($output)->toContain('href="/page/2"');
|
||||
expect($output)->toContain('href="/page/3"');
|
||||
});
|
||||
|
||||
it('handles nested for loops with x-button components', function () {
|
||||
$html = <<<HTML
|
||||
<div>
|
||||
<for items="{{sections}}" as="section">
|
||||
<h2>{{section.title}}</h2>
|
||||
<ul>
|
||||
<for items="{{section.items}}" as="item">
|
||||
<li>
|
||||
<x-button variant="secondary" href="{{item.url}}">{{item.name}}</x-button>
|
||||
</li>
|
||||
</for>
|
||||
</ul>
|
||||
</for>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'sections' => [
|
||||
[
|
||||
'title' => 'Section 1',
|
||||
'items' => [
|
||||
['name' => 'Item 1', 'url' => '/item/1'],
|
||||
['name' => 'Item 2', 'url' => '/item/2'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'title' => 'Section 2',
|
||||
'items' => [
|
||||
['name' => 'Item 3', 'url' => '/item/3'],
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Process through pipeline
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->forTransformer->transform($document, $context);
|
||||
$document = $this->placeholderTransformer->transform($document, $context);
|
||||
$document = $this->xComponentTransformer->transform($document, $context);
|
||||
|
||||
// Render to HTML
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Assert: No x-button tags should remain
|
||||
expect($output)->not->toContain('<x-button');
|
||||
expect($output)->not->toContain('</x-button>');
|
||||
|
||||
// Assert: All buttons should be rendered
|
||||
expect($output)->toContain('Item 1');
|
||||
expect($output)->toContain('Item 2');
|
||||
expect($output)->toContain('Item 3');
|
||||
expect($output)->toContain('href="/item/1"');
|
||||
expect($output)->toContain('href="/item/2"');
|
||||
expect($output)->toContain('href="/item/3"');
|
||||
});
|
||||
|
||||
it('handles empty for loops gracefully', function () {
|
||||
$html = <<<HTML
|
||||
<ul>
|
||||
<for items="{{pages}}" as="page">
|
||||
<li>
|
||||
<x-button variant="secondary" href="{{page.url}}">{{page.number}}</x-button>
|
||||
</li>
|
||||
</for>
|
||||
</ul>
|
||||
HTML;
|
||||
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'pages' => [],
|
||||
]
|
||||
);
|
||||
|
||||
// Process through pipeline
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->forTransformer->transform($document, $context);
|
||||
$document = $this->placeholderTransformer->transform($document, $context);
|
||||
$document = $this->xComponentTransformer->transform($document, $context);
|
||||
|
||||
// Render to HTML
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Assert: No x-button tags should remain
|
||||
expect($output)->not->toContain('<x-button');
|
||||
expect($output)->not->toContain('</x-button>');
|
||||
expect($output)->not->toContain('<for');
|
||||
});
|
||||
|
||||
it('processes x-button components outside for loops correctly', function () {
|
||||
$html = <<<HTML
|
||||
<div>
|
||||
<x-button variant="primary" href="/outside">Outside Button</x-button>
|
||||
<ul>
|
||||
<for items="{{pages}}" as="page">
|
||||
<li>
|
||||
<x-button variant="secondary" href="{{page.url}}">{{page.number}}</x-button>
|
||||
</li>
|
||||
</for>
|
||||
</ul>
|
||||
<x-button variant="primary" href="/after">After Button</x-button>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$context = new RenderContext(
|
||||
template: 'test',
|
||||
metaData: new MetaData('Test'),
|
||||
data: [
|
||||
'pages' => [
|
||||
['number' => 1, 'url' => '/page/1'],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
// Process through pipeline
|
||||
$document = $this->parser->parse($html);
|
||||
$document = $this->forTransformer->transform($document, $context);
|
||||
$document = $this->placeholderTransformer->transform($document, $context);
|
||||
$document = $this->xComponentTransformer->transform($document, $context);
|
||||
|
||||
// Render to HTML
|
||||
$output = $this->renderer->render($document);
|
||||
|
||||
// Assert: No x-button tags should remain
|
||||
expect($output)->not->toContain('<x-button');
|
||||
expect($output)->not->toContain('</x-button>');
|
||||
|
||||
// Assert: All buttons should be rendered
|
||||
expect($output)->toContain('Outside Button');
|
||||
expect($output)->toContain('After Button');
|
||||
expect($output)->toContain('href="/outside"');
|
||||
expect($output)->toContain('href="/after"');
|
||||
expect($output)->toContain('href="/page/1"');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user