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
This commit is contained in:
279
tests/Framework/Design/Service/ComponentDetectorTest.php
Normal file
279
tests/Framework/Design/Service/ComponentDetectorTest.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\Design\Service\ComponentDetector;
|
||||
use App\Framework\Design\ValueObjects\CssClass;
|
||||
|
||||
describe('ComponentDetector', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
$this->detector = new ComponentDetector();
|
||||
});
|
||||
|
||||
it('detects BEM components correctly', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('card'),
|
||||
new CssClass('card__header'),
|
||||
new CssClass('card__body'),
|
||||
new CssClass('card__footer'),
|
||||
new CssClass('card--featured'),
|
||||
new CssClass('card__header--large'),
|
||||
new CssClass('button'),
|
||||
new CssClass('button--primary'),
|
||||
];
|
||||
|
||||
$components = $this->detector->detectBemComponents($cssClasses);
|
||||
|
||||
expect($components)->toHaveCount(2); // card and button
|
||||
|
||||
$cardComponent = $components[0];
|
||||
expect($cardComponent['block'])->toBe('card');
|
||||
expect($cardComponent['elements'])->toContain('header');
|
||||
expect($cardComponent['elements'])->toContain('body');
|
||||
expect($cardComponent['elements'])->toContain('footer');
|
||||
expect($cardComponent['modifiers'])->toContain('featured');
|
||||
expect($cardComponent['element_modifiers'])->toHaveKey('header');
|
||||
expect($cardComponent['element_modifiers']['header'])->toContain('large');
|
||||
});
|
||||
|
||||
it('identifies utility class patterns', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('text-center'),
|
||||
new CssClass('text-left'),
|
||||
new CssClass('text-right'),
|
||||
new CssClass('p-4'),
|
||||
new CssClass('p-8'),
|
||||
new CssClass('m-2'),
|
||||
new CssClass('bg-blue-500'),
|
||||
new CssClass('bg-red-300'),
|
||||
new CssClass('hover:bg-blue-600'),
|
||||
new CssClass('card'), // Not a utility
|
||||
];
|
||||
|
||||
$utilityPatterns = $this->detector->detectUtilityPatterns($cssClasses);
|
||||
|
||||
expect($utilityPatterns)->toHaveKey('text-alignment');
|
||||
expect($utilityPatterns)->toHaveKey('padding');
|
||||
expect($utilityPatterns)->toHaveKey('margin');
|
||||
expect($utilityPatterns)->toHaveKey('background-color');
|
||||
expect($utilityPatterns)->toHaveKey('hover-states');
|
||||
|
||||
expect($utilityPatterns['text-alignment'])->toHaveCount(3);
|
||||
expect($utilityPatterns['padding'])->toHaveCount(2);
|
||||
expect($utilityPatterns['background-color'])->toHaveCount(2);
|
||||
expect($utilityPatterns['hover-states'])->toHaveCount(1);
|
||||
});
|
||||
|
||||
it('detects component structure patterns', function () {
|
||||
$cssClasses = [
|
||||
// Layout components
|
||||
new CssClass('container'),
|
||||
new CssClass('row'),
|
||||
new CssClass('col'),
|
||||
new CssClass('col-md-6'),
|
||||
|
||||
// Form components
|
||||
new CssClass('form'),
|
||||
new CssClass('form-group'),
|
||||
new CssClass('form-control'),
|
||||
new CssClass('form-label'),
|
||||
|
||||
// Navigation components
|
||||
new CssClass('nav'),
|
||||
new CssClass('nav-item'),
|
||||
new CssClass('nav-link'),
|
||||
];
|
||||
|
||||
$patterns = $this->detector->detectStructurePatterns($cssClasses);
|
||||
|
||||
expect($patterns)->toHaveKey('layout');
|
||||
expect($patterns)->toHaveKey('form');
|
||||
expect($patterns)->toHaveKey('navigation');
|
||||
|
||||
expect($patterns['layout']['components'])->toContain('container');
|
||||
expect($patterns['layout']['components'])->toContain('row');
|
||||
expect($patterns['form']['components'])->toContain('form');
|
||||
expect($patterns['form']['components'])->toContain('form-group');
|
||||
expect($patterns['navigation']['components'])->toContain('nav');
|
||||
});
|
||||
|
||||
it('analyzes responsive design patterns', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('hidden-xs'),
|
||||
new CssClass('visible-md'),
|
||||
new CssClass('col-sm-12'),
|
||||
new CssClass('col-md-6'),
|
||||
new CssClass('col-lg-4'),
|
||||
new CssClass('text-sm-center'),
|
||||
new CssClass('text-md-left'),
|
||||
];
|
||||
|
||||
$responsive = $this->detector->analyzeResponsivePatterns($cssClasses);
|
||||
|
||||
expect($responsive['breakpoints'])->toContain('xs');
|
||||
expect($responsive['breakpoints'])->toContain('sm');
|
||||
expect($responsive['breakpoints'])->toContain('md');
|
||||
expect($responsive['breakpoints'])->toContain('lg');
|
||||
|
||||
expect($responsive['patterns']['visibility'])->toHaveCount(2);
|
||||
expect($responsive['patterns']['grid'])->toHaveCount(3);
|
||||
expect($responsive['patterns']['typography'])->toHaveCount(2);
|
||||
});
|
||||
|
||||
it('identifies component complexity levels', function () {
|
||||
$simpleComponent = [
|
||||
new CssClass('button'),
|
||||
new CssClass('button--primary'),
|
||||
];
|
||||
|
||||
$complexComponent = [
|
||||
new CssClass('card'),
|
||||
new CssClass('card__header'),
|
||||
new CssClass('card__title'),
|
||||
new CssClass('card__subtitle'),
|
||||
new CssClass('card__body'),
|
||||
new CssClass('card__content'),
|
||||
new CssClass('card__actions'),
|
||||
new CssClass('card__footer'),
|
||||
new CssClass('card--featured'),
|
||||
new CssClass('card--compact'),
|
||||
new CssClass('card__header--large'),
|
||||
new CssClass('card__actions--centered'),
|
||||
];
|
||||
|
||||
$simpleComplexity = $this->detector->analyzeComponentComplexity($simpleComponent);
|
||||
$complexComplexity = $this->detector->analyzeComponentComplexity($complexComponent);
|
||||
|
||||
expect($simpleComplexity['level'])->toBe('simple');
|
||||
expect($simpleComplexity['score'])->toBeLessThan(3);
|
||||
|
||||
expect($complexComplexity['level'])->toBe('complex');
|
||||
expect($complexComplexity['score'])->toBeGreaterThan(8);
|
||||
expect($complexComplexity['recommendations'])->toContain('Consider splitting into smaller components');
|
||||
});
|
||||
|
||||
it('detects atomic design patterns', function () {
|
||||
$cssClasses = [
|
||||
// Atoms
|
||||
new CssClass('btn'),
|
||||
new CssClass('input'),
|
||||
new CssClass('label'),
|
||||
new CssClass('icon'),
|
||||
|
||||
// Molecules
|
||||
new CssClass('search-form'),
|
||||
new CssClass('form-group'),
|
||||
new CssClass('nav-item'),
|
||||
|
||||
// Organisms
|
||||
new CssClass('header'),
|
||||
new CssClass('sidebar'),
|
||||
new CssClass('footer'),
|
||||
new CssClass('product-grid'),
|
||||
];
|
||||
|
||||
$atomicAnalysis = $this->detector->analyzeAtomicDesignPatterns($cssClasses);
|
||||
|
||||
expect($atomicAnalysis['atoms'])->toHaveCount(4);
|
||||
expect($atomicAnalysis['molecules'])->toHaveCount(3);
|
||||
expect($atomicAnalysis['organisms'])->toHaveCount(4);
|
||||
|
||||
expect($atomicAnalysis['atoms'])->toContain('btn');
|
||||
expect($atomicAnalysis['molecules'])->toContain('search-form');
|
||||
expect($atomicAnalysis['organisms'])->toContain('header');
|
||||
});
|
||||
|
||||
it('validates component naming conventions', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('button'), // Good: semantic
|
||||
new CssClass('btn'), // Good: abbreviation
|
||||
new CssClass('redButton'), // Bad: camelCase
|
||||
new CssClass('button_primary'), // Bad: underscore instead of dash
|
||||
new CssClass('Button'), // Bad: PascalCase
|
||||
new CssClass('my-custom-btn-2'), // Good: kebab-case
|
||||
];
|
||||
|
||||
$validation = $this->detector->validateNamingConventions($cssClasses);
|
||||
|
||||
expect($validation['valid'])->toHaveCount(3);
|
||||
expect($validation['invalid'])->toHaveCount(3);
|
||||
|
||||
$invalidClasses = array_map(fn ($v) => $v['class'], $validation['invalid']);
|
||||
expect($invalidClasses)->toContain('redButton');
|
||||
expect($invalidClasses)->toContain('button_primary');
|
||||
expect($invalidClasses)->toContain('Button');
|
||||
});
|
||||
|
||||
it('detects component relationships', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('modal'),
|
||||
new CssClass('modal__backdrop'),
|
||||
new CssClass('modal__dialog'),
|
||||
new CssClass('modal__header'),
|
||||
new CssClass('modal__title'),
|
||||
new CssClass('modal__close'),
|
||||
new CssClass('modal__body'),
|
||||
new CssClass('modal__footer'),
|
||||
new CssClass('modal__actions'),
|
||||
];
|
||||
|
||||
$relationships = $this->detector->detectComponentRelationships($cssClasses);
|
||||
|
||||
expect($relationships)->toHaveKey('modal');
|
||||
|
||||
$modalRelationships = $relationships['modal'];
|
||||
expect($modalRelationships['children'])->toContain('backdrop');
|
||||
expect($modalRelationships['children'])->toContain('dialog');
|
||||
expect($modalRelationships['children'])->toContain('header');
|
||||
expect($modalRelationships['depth'])->toBe(2); // modal -> header -> title
|
||||
expect($modalRelationships['complexity_score'])->toBeGreaterThan(5);
|
||||
});
|
||||
|
||||
it('suggests component improvements', function () {
|
||||
$cssClasses = [
|
||||
// Inconsistent button pattern
|
||||
new CssClass('button'),
|
||||
new CssClass('btn'), // Inconsistent naming
|
||||
new CssClass('submit-btn'), // Another variation
|
||||
|
||||
// Missing BEM structure
|
||||
new CssClass('card-header'), // Should be card__header
|
||||
new CssClass('card-body'), // Should be card__body
|
||||
|
||||
// Overly specific
|
||||
new CssClass('red-submit-button-large'),
|
||||
];
|
||||
|
||||
$improvements = $this->detector->suggestImprovements($cssClasses);
|
||||
|
||||
expect($improvements['naming_inconsistencies'])->not->toBeEmpty();
|
||||
expect($improvements['bem_violations'])->not->toBeEmpty();
|
||||
expect($improvements['overly_specific'])->not->toBeEmpty();
|
||||
|
||||
expect($improvements['suggestions'])->toContain('Standardize button naming (choose: button, btn)');
|
||||
expect($improvements['suggestions'])->toContain('Convert card-header to card__header for BEM compliance');
|
||||
});
|
||||
|
||||
it('analyzes component reusability', function () {
|
||||
$cssClasses = [
|
||||
new CssClass('btn'),
|
||||
new CssClass('btn--primary'),
|
||||
new CssClass('btn--secondary'),
|
||||
new CssClass('btn--large'),
|
||||
new CssClass('btn--small'),
|
||||
new CssClass('very-specific-page-button'), // Low reusability
|
||||
];
|
||||
|
||||
$reusability = $this->detector->analyzeComponentReusability($cssClasses);
|
||||
|
||||
$btnReusability = $reusability['btn'];
|
||||
expect($btnReusability['score'])->toBeGreaterThan(0.8);
|
||||
expect($btnReusability['variants'])->toBe(4);
|
||||
expect($btnReusability['reusability_level'])->toBe('high');
|
||||
|
||||
$specificReusability = $reusability['very-specific-page-button'];
|
||||
expect($specificReusability['score'])->toBeLessThan(0.3);
|
||||
expect($specificReusability['reusability_level'])->toBe('low');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user