Files
michaelschiemer/tests/Framework/Design/Service/DesignSystemAnalyzerTest.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

347 lines
13 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\Service\ColorAnalyzer;
use App\Framework\Design\Service\ComponentDetector;
use App\Framework\Design\Service\ConventionChecker;
use App\Framework\Design\Service\DesignSystemAnalyzer;
use App\Framework\Design\Service\TokenAnalyzer;
describe('DesignSystemAnalyzer', function () {
beforeEach(function () {
$this->customPropertyParser = new CustomPropertyParser();
$this->classNameParser = new ClassNameParser();
$this->cssParser = new CssParser($this->customPropertyParser, $this->classNameParser);
$this->colorAnalyzer = new ColorAnalyzer();
$this->tokenAnalyzer = new TokenAnalyzer();
$this->componentDetector = new ComponentDetector();
$this->conventionChecker = new ConventionChecker();
$this->analyzer = new DesignSystemAnalyzer(
$this->cssParser,
$this->colorAnalyzer,
$this->tokenAnalyzer,
$this->componentDetector,
$this->conventionChecker
);
});
it('analyzes complete design system from CSS files', function () {
$cssFiles = [
'/test/tokens.css' => '
:root {
--color-primary-500: #3b82f6;
--color-secondary-500: #6b7280;
--spacing-md: 1rem;
--font-size-base: 16px;
--border-radius-md: 0.375rem;
}
',
'/test/components.css' => '
.button {
padding: var(--spacing-md);
border-radius: var(--border-radius-md);
background-color: var(--color-primary-500);
}
.button--secondary {
background-color: var(--color-secondary-500);
}
.card {
border-radius: var(--border-radius-md);
padding: calc(var(--spacing-md) * 2);
}
.card__header {
margin-bottom: var(--spacing-md);
}
',
];
$analysis = $this->analyzer->analyze($cssFiles);
expect($analysis->designTokens)->not->toBeEmpty();
expect($analysis->components)->not->toBeEmpty();
expect($analysis->colorPalette)->not->toBeEmpty();
expect($analysis->maturityScore)->toBeGreaterThan(0);
expect($analysis->recommendations)->not->toBeEmpty();
// Check token analysis
expect($analysis->tokenAnalysis['categories'])->toHaveKey('color');
expect($analysis->tokenAnalysis['categories'])->toHaveKey('spacing');
expect($analysis->tokenAnalysis['categories'])->toHaveKey('typography');
// Check component analysis
expect($analysis->componentAnalysis['bem_components'])->toHaveCount(2); // button, card
expect($analysis->componentAnalysis['bem_components'][0]['block'])->toBe('button');
expect($analysis->componentAnalysis['bem_components'][1]['block'])->toBe('card');
// Check color analysis
expect($analysis->colorAnalysis['total_colors'])->toBe(2);
expect($analysis->colorAnalysis['color_scheme'])->toBeIn(['light', 'dark', 'mixed']);
});
it('calculates design system maturity score', function () {
// Mature design system
$matureSystem = [
'/test/tokens.css' => '
:root {
/* Comprehensive color scale */
--color-primary-100: #dbeafe;
--color-primary-500: #3b82f6;
--color-primary-900: #1e3a8a;
/* Consistent spacing scale */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Typography scale */
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
}
',
'/test/components.css' => '
.btn { /* BEM naming */ }
.btn--primary { }
.btn--secondary { }
.btn__icon { }
.card { }
.card__header { }
.card__body { }
.card__footer { }
.form { }
.form__group { }
.form__label { }
.form__input { }
',
];
$matureAnalysis = $this->analyzer->analyze($matureSystem);
// Basic design system
$basicSystem = [
'/test/basic.css' => '
:root {
--main-color: red;
--bg-color: white;
}
.redButton { color: red; }
.blueDiv { background: blue; }
',
];
$basicAnalysis = $this->analyzer->analyze($basicSystem);
expect($matureAnalysis->maturityScore)->toBeGreaterThan($basicAnalysis->maturityScore);
expect($matureAnalysis->maturityLevel)->toBeIn(['Developing', 'Established', 'Mature']);
expect($basicAnalysis->maturityLevel)->toBeIn(['Basic', 'Emerging']);
});
it('identifies design system gaps and improvements', function () {
$incompleteSystem = [
'/test/gaps.css' => '
:root {
--primary: #3b82f6; /* Missing scale */
--big-space: 2rem; /* Inconsistent naming */
--tiny: 2px; /* Non-standard value */
}
.redButton { color: red; } /* Presentational naming */
.bigBox { size: large; } /* Presentational naming */
.card_header { } /* Wrong BEM separator */
.NAVIGATION { } /* Wrong case */
',
];
$analysis = $this->analyzer->analyze($incompleteSystem);
expect($analysis->gaps)->not->toBeEmpty();
expect($analysis->recommendations)->not->toBeEmpty();
// Check for specific gap types
$gapTypes = array_column($analysis->gaps, 'type');
expect($gapTypes)->toContain('incomplete_color_scale');
expect($gapTypes)->toContain('inconsistent_naming');
expect($gapTypes)->toContain('non_standard_values');
// Check recommendations
$recommendationTexts = array_column($analysis->recommendations, 'text');
expect($recommendationTexts)->toContainStrings([
'naming convention',
'color scale',
'BEM',
]);
});
it('analyzes design system consistency', function () {
$consistentSystem = [
'/test/consistent.css' => '
:root {
--color-primary-500: #3b82f6;
--color-secondary-500: #6b7280;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
}
.button { }
.button--primary { }
.button--secondary { }
.card { }
.card__header { }
.card__body { }
',
];
$inconsistentSystem = [
'/test/inconsistent.css' => '
:root {
--primaryColor: #3b82f6; /* camelCase */
--secondary_color: #6b7280; /* snake_case */
--SPACING_SM: 0.5rem; /* UPPER_CASE */
}
.btn { } /* Inconsistent with button */
.button { } /* Mixed naming */
.card_header { } /* Wrong separator */
.CardBody { } /* PascalCase */
',
];
$consistentAnalysis = $this->analyzer->analyze($consistentSystem);
$inconsistentAnalysis = $this->analyzer->analyze($inconsistentSystem);
expect($consistentAnalysis->consistencyScore)->toBeGreaterThan($inconsistentAnalysis->consistencyScore);
expect($consistentAnalysis->consistencyScore)->toBeGreaterThan(0.8);
expect($inconsistentAnalysis->consistencyScore)->toBeLessThan(0.5);
expect($inconsistentAnalysis->conventionViolations)->not->toBeEmpty();
});
it('generates comprehensive design system report', function () {
$system = [
'/test/comprehensive.css' => '
:root {
--color-primary-500: #3b82f6;
--color-secondary-500: #6b7280;
--spacing-md: 1rem;
--font-size-base: 16px;
--border-radius-md: 0.375rem;
}
.button {
padding: var(--spacing-md);
font-size: var(--font-size-base);
border-radius: var(--border-radius-md);
background-color: var(--color-primary-500);
}
.button--secondary {
background-color: var(--color-secondary-500);
}
.card {
border-radius: var(--border-radius-md);
padding: var(--spacing-md);
}
',
];
$analysis = $this->analyzer->analyze($system);
// Verify all sections are present
expect($analysis->overview)->toHaveKeys(['total_tokens', 'total_components', 'maturity_level']);
expect($analysis->tokenAnalysis)->toHaveKeys(['categories', 'naming_patterns', 'usage_analysis']);
expect($analysis->componentAnalysis)->toHaveKeys(['bem_components', 'utility_patterns', 'complexity_analysis']);
expect($analysis->colorAnalysis)->toHaveKeys(['total_colors', 'color_scheme', 'accessibility_issues']);
expect($analysis->conventionAnalysis)->toHaveKeys(['bem_compliance', 'naming_consistency', 'violations']);
// Verify metrics
expect($analysis->metrics)->toHaveKeys(['maturity_score', 'consistency_score', 'token_coverage']);
expect($analysis->metrics['maturity_score'])->toBeBetween(0, 1);
expect($analysis->metrics['consistency_score'])->toBeBetween(0, 1);
// Verify recommendations
expect($analysis->recommendations)->toBeArray();
expect($analysis->recommendations)->not->toBeEmpty();
// Verify gaps analysis
expect($analysis->gaps)->toBeArray();
});
it('handles empty or invalid CSS gracefully', function () {
$emptySystem = [
'/test/empty.css' => '',
];
$emptyAnalysis = $this->analyzer->analyze($emptySystem);
expect($emptyAnalysis->overview['total_tokens'])->toBe(0);
expect($emptyAnalysis->overview['total_components'])->toBe(0);
expect($emptyAnalysis->maturityLevel)->toBe('Basic');
expect($emptyAnalysis->recommendations)->toContain('Start by defining design tokens');
$invalidSystem = [
'/test/invalid.css' => 'invalid css content {',
];
$invalidAnalysis = $this->analyzer->analyze($invalidSystem);
expect($invalidAnalysis->errors)->not->toBeEmpty();
expect($invalidAnalysis->overview['total_tokens'])->toBe(0);
});
it('tracks design system evolution over time', function () {
$version1 = [
'/test/v1.css' => '
:root {
--primary: #3b82f6;
--secondary: #6b7280;
}
.btn { }
.btn--primary { }
',
];
$version2 = [
'/test/v2.css' => '
:root {
--color-primary-500: #3b82f6;
--color-secondary-500: #6b7280;
--spacing-md: 1rem;
}
.button { }
.button--primary { }
.button__icon { }
',
];
$v1Analysis = $this->analyzer->analyze($version1);
$v2Analysis = $this->analyzer->analyze($version2);
expect($v2Analysis->maturityScore)->toBeGreaterThan($v1Analysis->maturityScore);
expect($v2Analysis->overview['total_tokens'])->toBeGreaterThan($v1Analysis->overview['total_tokens']);
$evolution = $this->analyzer->compareVersions($v1Analysis, $v2Analysis);
expect($evolution['improvements'])->not->toBeEmpty();
expect($evolution['regressions'])->toBeArray();
expect($evolution['new_features'])->toContain('Enhanced token naming');
expect($evolution['new_features'])->toContain('Added spacing tokens');
});
});