Files
michaelschiemer/tests/Framework/Design/Parser/CssParserTest.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

213 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Design\Parser\ClassNameParser;
use App\Framework\Design\Parser\CssParser;
use App\Framework\Design\Parser\CustomPropertyParser;
use App\Framework\Design\ValueObjects\ColorFormat;
use App\Framework\Design\ValueObjects\CssColor;
use App\Framework\Filesystem\FilePath;
describe('CssParser', function () {
beforeEach(function () {
$this->customPropertyParser = new CustomPropertyParser();
$this->classNameParser = new ClassNameParser();
$this->parser = new CssParser($this->customPropertyParser, $this->classNameParser);
});
it('parses simple CSS content', function () {
$css = '
:root {
--primary-color: #3b82f6;
--text-size: 16px;
}
.button {
background-color: var(--primary-color);
padding: 0.5rem 1rem;
color: white;
}
';
$result = $this->parser->parseContent($css);
expect($result->rules)->toHaveCount(2);
expect($result->customProperties)->toHaveCount(2);
expect($result->classNames)->toHaveCount(1);
// Test custom properties
$primaryColor = $result->customProperties[0];
expect($primaryColor->name)->toBe('primary-color');
expect($primaryColor->value)->toBe('#3b82f6');
// Test class names
$buttonClass = $result->classNames[0];
expect($buttonClass->name)->toBe('button');
expect($buttonClass->isBemBlock())->toBeTrue();
});
it('parses BEM classes correctly', function () {
$css = '
.card { }
.card__header { }
.card__body { }
.card--featured { }
.card__header--large { }
';
$result = $this->parser->parseContent($css);
expect($result->classNames)->toHaveCount(5);
$classes = array_map(fn ($c) => $c->name, $result->classNames);
expect($classes)->toContain('card');
expect($classes)->toContain('card__header');
expect($classes)->toContain('card__body');
expect($classes)->toContain('card--featured');
expect($classes)->toContain('card__header--large');
// Test BEM detection
$cardClass = $result->classNames[0];
expect($cardClass->isBemBlock())->toBeTrue();
$headerClass = $result->classNames[1];
expect($headerClass->isBemElement())->toBeTrue();
$featuredClass = $result->classNames[3];
expect($featuredClass->isBemModifier())->toBeTrue();
});
it('handles OKLCH colors', function () {
$css = '
:root {
--modern-blue: oklch(0.7 0.15 260);
--vibrant-red: oklch(65% 0.2 20deg);
}
';
$result = $this->parser->parseContent($css);
expect($result->customProperties)->toHaveCount(2);
$blueToken = $result->customProperties[0];
expect($blueToken->name)->toBe('modern-blue');
expect($blueToken->hasValueType('color'))->toBeTrue();
$color = $blueToken->getValueAs('color');
expect($color)->toBeInstanceOf(CssColor::class);
expect($color->format)->toBe(ColorFormat::OKLCH);
});
it('parses complex selectors', function () {
$css = '
.nav-menu > .nav-item:hover .nav-link {
color: #333;
}
@media (min-width: 768px) {
.container {
max-width: 1200px;
}
}
';
$result = $this->parser->parseContent($css);
expect($result->rules)->toHaveCount(2);
// Complex selector
$complexRule = $result->rules[0];
expect($complexRule->selectors)->toHaveCount(1);
expect($complexRule->selectors[0]->value)->toContain('nav-menu');
expect($complexRule->selectors[0]->extractClasses())->toContain('nav-menu');
expect($complexRule->selectors[0]->extractClasses())->toContain('nav-item');
expect($complexRule->selectors[0]->extractClasses())->toContain('nav-link');
});
it('calculates selector specificity correctly', function () {
$css = '
.simple { }
#unique { }
div.class { }
.parent > .child:hover { }
#main .sidebar .widget { }
';
$result = $this->parser->parseContent($css);
expect($result->rules)->toHaveCount(5);
// .simple = 10
expect($result->rules[0]->selectors[0]->calculateSpecificity())->toBe(10);
// #unique = 100
expect($result->rules[1]->selectors[0]->calculateSpecificity())->toBe(100);
// div.class = 11 (1 element + 10 class)
expect($result->rules[2]->selectors[0]->calculateSpecificity())->toBe(11);
// .parent > .child:hover = 30 (20 classes + 10 pseudo-class)
expect($result->rules[3]->selectors[0]->calculateSpecificity())->toBe(30);
// #main .sidebar .widget = 120 (100 ID + 20 classes)
expect($result->rules[4]->selectors[0]->calculateSpecificity())->toBe(120);
});
it('parses file from filesystem', function () {
// Create temporary CSS file
$tempFile = sys_get_temp_dir() . '/test-' . uniqid() . '.css';
file_put_contents($tempFile, '
:root {
--test-color: #ff0000;
}
.test-class {
color: var(--test-color);
}
');
$filePath = new FilePath($tempFile);
$result = $this->parser->parseFile($filePath);
expect($result->customProperties)->toHaveCount(1);
expect($result->classNames)->toHaveCount(1);
expect($result->rules)->toHaveCount(2);
// Cleanup
unlink($tempFile);
});
it('handles empty CSS gracefully', function () {
$result = $this->parser->parseContent('');
expect($result->rules)->toHaveCount(0);
expect($result->customProperties)->toHaveCount(0);
expect($result->classNames)->toHaveCount(0);
expect($result->statistics['total_rules'])->toBe(0);
});
it('parses utility classes', function () {
$css = '
.text-center { text-align: center; }
.p-4 { padding: 1rem; }
.bg-red-500 { background-color: #ef4444; }
.hover\\:bg-blue-500:hover { background-color: #3b82f6; }
';
$result = $this->parser->parseContent($css);
expect($result->classNames)->toHaveCount(4);
$classes = array_map(fn ($c) => $c->name, $result->classNames);
expect($classes)->toContain('text-center');
expect($classes)->toContain('p-4');
expect($classes)->toContain('bg-red-500');
expect($classes)->toContain('hover:bg-blue-500');
// Test utility detection
$utilityClass = $result->classNames[1]; // p-4
expect($utilityClass->isUtilityClass())->toBeTrue();
});
});