utilikit-1.0.0/modules/utilikit_test/src/Service/TestGenerator.php
modules/utilikit_test/src/Service/TestGenerator.php
<?php
declare(strict_types=1);
namespace Drupal\utilikit_test\Service;
use Drupal\utilikit\Service\UtilikitRules;
/**
* Generates comprehensive test cases for UtiliKit utility class validation.
*
* This service provides systematic test case generation for all UtiliKit
* utility rules, ensuring comprehensive coverage of value types, responsive
* breakpoints, and edge cases. The generator creates both positive test
* cases (expected to work) and boundary condition tests to validate the
* complete UtiliKit system behavior.
*
* Test Case Generation Strategy:
* - Covers all rule types defined in UtilikitRules
* - Generates responsive variants for all applicable utilities
* - Tests boundary values and edge cases
* - Includes both basic and complex value patterns
* - Validates CSS property mapping accuracy
* - Ensures consistent behavior across breakpoints
*
* Generated test cases include metadata for automated testing frameworks
* and can be used for CSS validation, performance testing, and regression
* testing during development.
*/
class TestGenerator {
/**
* Predefined test values organized by category and use case.
*
* This array contains carefully selected test values that cover common
* use cases, edge cases, and boundary conditions for different value
* types used throughout the UtiliKit system.
*
* @var array
*/
private array $testValues = [
'numeric' => [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 100],
'percentages' => ['25pr', '50pr', '75pr', '100pr'],
'viewport' => ['50vh', '100vh', '50vw', '100vw'],
'em' => ['0.5em', '1em', '1.5em', '2em'],
'rem' => ['0.5rem', '1rem', '1.5rem', '2rem'],
'colors' => ['000000', 'ffffff', '3b82f6', 'ef4444', '10b981', 'f59e0b'],
'alphas' => ['50', '75', '90'],
'transforms' => [
'rotate' => [0, 45, 90, 180, 270, 360],
'scale' => [50, 75, 100, 110, 125, 150, 200, 250],
],
'breakpoints' => ['', 'sm', 'md', 'lg', 'xl', 'xxl'],
'directions' => ['t', 'r', 'b', 'l'],
];
/**
* CSS keyword values mapped by property type.
*
* Contains comprehensive lists of valid CSS keyword values for each
* property type that accepts keyword values in the UtiliKit system.
* These mappings ensure test coverage for all supported keyword
* combinations and help validate keyword-based utility classes.
*
* @var array
*/
private array $keywordMap = [
'display' => ['none', 'block', 'inline', 'inline-block', 'flex', 'inline-flex', 'grid', 'inline-grid'],
'position' => ['static', 'relative', 'absolute', 'fixed', 'sticky'],
'textAlign' => ['left', 'center', 'right', 'justify'],
'overflow' => ['visible', 'hidden', 'auto', 'scroll'],
'cursor' => ['auto', 'pointer', 'move', 'text', 'wait', 'help', 'not-allowed'],
'float' => ['left', 'right', 'none'],
'clear' => ['left', 'right', 'both', 'none'],
'userSelect' => ['none', 'auto', 'text', 'all'],
'borderStyle' => ['none', 'solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset'],
'backgroundSize' => ['auto', 'cover', 'contain'],
'flexDirection' => ['row', 'row-reverse', 'column', 'column-reverse'],
'justifyContent' => ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'],
'alignItems' => ['flex-start', 'flex-end', 'center', 'baseline', 'stretch'],
'alignContent' => ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'stretch'],
'flexWrap' => ['nowrap', 'wrap', 'wrap-reverse'],
];
/**
* Generates comprehensive test cases for all UtiliKit rules.
*
* Creates a complete test suite covering every rule defined in the
* UtiliKit system, including all value types, responsive variants,
* and edge cases. The generated test cases include metadata for
* automated testing and validation frameworks.
*
* @return array
* Array of test case groups, each containing:
* - prefix: The UtiliKit prefix being tested
* - rule: Complete rule definition from UtilikitRules
* - tests: Array of individual test cases
* - group: Rule category for organization
*
* @see UtilikitRules::getRules()
*/
public function generateAllTests(): array {
$rules = UtilikitRules::getRules();
$allTests = [];
foreach ($rules as $prefix => $rule) {
$testCases = $this->generateTestCasesForRule($prefix, $rule);
if (!empty($testCases)) {
$allTests[] = [
'prefix' => $prefix,
'rule' => $rule,
'tests' => $testCases,
'group' => $rule['group'] ?? 'Other',
];
}
}
return $allTests;
}
/**
* Generates test cases for a specific UtiliKit rule.
*
* Analyzes the rule definition to determine appropriate test case
* generation strategies and creates comprehensive test coverage
* based on the rule's value types and characteristics.
*
* @param string $prefix
* The UtiliKit prefix identifier (e.g., 'pd', 'mg', 'bg').
* @param array $rule
* The complete rule definition array containing CSS property
* and behavior flags.
*
* @return array
* Array of test case objects for the specified rule.
*/
private function generateTestCasesForRule(string $prefix, array $rule): array {
$testCases = [];
// Numeric flexible properties.
if (!empty($rule['isNumericFlexible'])) {
$testCases = array_merge($testCases, $this->generateNumericTests($prefix, $rule));
}
// Keyword properties.
if (!empty($rule['isKeyword'])) {
$testCases = array_merge($testCases, $this->generateKeywordTests($prefix, $rule));
}
// Color properties.
if (!empty($rule['isColor'])) {
$testCases = array_merge($testCases, $this->generateColorTests($prefix, $rule));
}
// Transform properties.
if (!empty($rule['isTransform'])) {
$testCases = array_merge($testCases, $this->generateTransformTests($prefix, $rule));
}
// Grid template properties.
if (!empty($rule['isGridTrackList'])) {
$testCases = array_merge($testCases, $this->generateGridTests($prefix, $rule));
}
// Integer properties (for fw, zi, or, etc)
if (!empty($rule['isInteger']) && empty($rule['isOpacity'])) {
$testCases = array_merge($testCases, $this->generateIntegerTests($prefix, $rule));
}
// Decimal fixed properties (for fg, fk)
if (!empty($rule['isDecimalFixed'])) {
$testCases = array_merge($testCases, $this->generateDecimalTests($prefix, $rule));
}
// Opacity special case.
if (!empty($rule['isOpacity'])) {
$testCases = array_merge($testCases, $this->generateOpacityTests($prefix));
}
// Range properties (grid column/row span)
if (!empty($rule['isRange'])) {
$testCases = array_merge($testCases, $this->generateRangeTests($prefix, $rule));
}
return $testCases;
}
/**
* Generates test cases for numeric flexible properties.
*
* Creates comprehensive test coverage for properties that accept
* numeric values with flexible units. Includes basic values,
* responsive variants, directional properties, shorthand notation,
* and special handling for specific properties like line-height.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property and behavior flags.
*
* @return array
* Array of test cases covering numeric value scenarios.
*/
private function generateNumericTests(string $prefix, array $rule): array {
$tests = [];
$cssProperty = $rule['css'];
$hasSides = !empty($rule['sides']);
$allowAuto = !empty($rule['allowAuto']);
// Basic numeric values.
foreach ($this->testValues['numeric'] as $value) {
// Skip small numeric values for line-height as they'll be treated as
// multipliers.
if ($prefix === 'lh' && $value <= 10) {
// Skip these - we'll handle them separately.
continue;
}
// For unitless properties, don't add 'px'.
$expectedValue = $value;
if (!in_array($prefix, ['zi', 'fg', 'fk', 'or', 'fw'])) {
$expectedValue = "{$value}px";
}
// Simple value.
$tests[] = $this->createTestCase(
"uk-$prefix--$value",
$cssProperty,
$expectedValue,
'basic',
"Basic value"
);
// Responsive variants.
foreach (['sm', 'md', 'lg', 'xl', 'xxl'] as $bp) {
// Skip small numeric values for line-height.
if ($prefix === 'lh' && $value <= 10) {
continue;
}
// For unitless properties, don't add 'px'.
$responsiveExpectedValue = $value;
if (!in_array($prefix, ['op', 'zi', 'fg', 'fk', 'or', 'fw'])) {
$responsiveExpectedValue = "{$value}px";
}
$tests[] = $this->createTestCase(
"uk-$bp-$prefix--$value",
$cssProperty,
$responsiveExpectedValue,
'responsive',
"Responsive at $bp breakpoint",
$bp
);
}
}
// Auto value.
if ($allowAuto) {
$tests[] = $this->createTestCase(
"uk-$prefix--auto",
$cssProperty,
"auto",
'auto',
"Auto value"
);
}
// Directional tests (sides)
if ($hasSides) {
foreach ($this->testValues['directions'] as $dir) {
$dirName = ['t' => 'Top', 'r' => 'Right', 'b' => 'Bottom', 'l' => 'Left'][$dir];
// For border properties, we need the specific directional property.
if ($prefix === 'bw') {
$cssProp = 'border' . $dirName . 'Width';
}
elseif ($prefix === 'br') {
// Border radius has different property names.
$radiusPropMap = [
't' => ['borderTopLeftRadius', 'borderTopRightRadius'],
'r' => ['borderTopRightRadius', 'borderBottomRightRadius'],
'b' => ['borderBottomLeftRadius', 'borderBottomRightRadius'],
'l' => ['borderTopLeftRadius', 'borderBottomLeftRadius'],
];
// For testing, we'll check just the first property.
$cssProp = $radiusPropMap[$dir][0];
}
else {
$cssProp = $cssProperty . $dirName;
}
// Numeric directional.
$tests[] = $this->createTestCase(
"uk-$prefix--$dir-16",
$cssProp,
"16px",
'directional',
"Directional $dirName"
);
// Auto directional.
if ($allowAuto) {
$tests[] = $this->createTestCase(
"uk-$prefix--$dir-auto",
$cssProp,
"auto",
'directional-auto',
"Directional $dirName auto"
);
}
}
// Shorthand tests - use property: shorthand format.
$tests[] = $this->createTestCase(
"uk-$prefix--16-32",
$cssProperty,
"16px 32px",
'shorthand-2',
"Two-value shorthand (vertical horizontal)"
);
$tests[] = $this->createTestCase(
"uk-$prefix--16-32-24",
$cssProperty,
"16px 32px 24px",
'shorthand-3',
"Three-value shorthand (top horizontal bottom)"
);
$tests[] = $this->createTestCase(
"uk-$prefix--16-32-24-48",
$cssProperty,
"16px 32px 24px 48px",
'shorthand-4',
"Four-value shorthand (top right bottom left)"
);
}
// Viewport units (for specific properties)
if (in_array($prefix, ['wd', 'ht', 'xw', 'xh', 'nw', 'nh', 'fs'])) {
foreach ($this->testValues['viewport'] as $value) {
$tests[] = $this->createTestCase(
"uk-$prefix--$value",
$cssProperty,
$value,
'viewport',
"Viewport unit"
);
}
}
// Special case for gap (can have two values)
if ($prefix === 'gp') {
// Gap only accepts integers in JS engine.
$tests[] = $this->createTestCase(
"uk-gp--16-32",
'gap',
"16px 32px",
'gap-2',
"Two-value gap (row column)"
);
}
// Special handling for line-height.
if ($prefix === 'lh') {
// Test unitless multipliers - but expect pixel values.
// This is what your test environment uses.
$defaultFontSize = 12;
foreach ([1, 1.2, 1.5, 1.75, 2, 2.5, 3] as $multiplier) {
$expectedPx = $multiplier * $defaultFontSize;
// Convert decimal to 'd' format for class name.
$classValue = str_replace('.', 'd', (string) $multiplier);
$tests[] = $this->createTestCase(
"uk-lh--" . $classValue,
'lineHeight',
// Expect the computed pixel value.
$expectedPx . 'px',
'line-height-unitless',
"Line height unitless multiplier {$multiplier}"
);
}
// Test with explicit px units.
foreach ([12, 16, 20, 24, 28, 32] as $value) {
$tests[] = $this->createTestCase(
"uk-lh--{$value}px",
'lineHeight',
"{$value}px",
'line-height-px',
"Line height {$value}px"
);
}
// Test with rem units - expect pixel values.
// Default root font size.
$rootFontSize = 16;
foreach ([1, 1.25, 1.5, 1.75, 2] as $remValue) {
$expectedPx = $remValue * $rootFontSize;
// Convert decimal to 'd' format for class name.
$classValue = str_replace('.', 'd', (string) $remValue);
$tests[] = $this->createTestCase(
"uk-lh--{$classValue}rem",
'lineHeight',
// Expect the computed pixel value.
$expectedPx . 'px',
'line-height-rem',
"Line height {$remValue}rem"
);
}
}
return $tests;
}
/**
* Generates test cases for integer-only properties.
*
* Creates test coverage for properties that accept only integer values,
* such as z-index, font-weight, and flex order. Includes appropriate
* value ranges for each property type.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for integer value scenarios.
*/
private function generateIntegerTests(string $prefix, array $rule): array {
$tests = [];
$values = [1, 100, 200, 300, 400, 500, 600, 700, 800, 900];
if ($prefix === 'zi') {
$values = [-1, 0, 1, 10, 100, 999, 9999];
}
elseif ($prefix === 'fw') {
$values = [100, 200, 300, 400, 500, 600, 700, 800, 900];
}
elseif ($prefix === 'or') {
$values = [-1, 0, 1, 2, 3];
}
foreach ($values as $value) {
$tests[] = $this->createTestCase(
"uk-$prefix--$value",
$rule['css'],
(string) $value,
'integer',
"Integer value $value"
);
}
return $tests;
}
/**
* Generates test cases for decimal fixed-precision properties.
*
* Creates test coverage for properties that accept decimal values
* with fixed precision, such as flex-grow and flex-shrink. Uses
* UtiliKit's 'd' notation for decimal points in class names.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for decimal value scenarios.
*/
private function generateDecimalTests(string $prefix, array $rule): array {
$tests = [];
// JS engine requires exactly 2 decimal places, but browser normalizes.
$values = [
'0' => '0',
// Changed from '0.50'.
'0d50' => '0.5',
'1' => '1',
// Changed from '1.50'.
'1d50' => '1.5',
'2' => '2',
// Changed from '2.25'.
'2d25' => '2.25',
];
foreach ($values as $input => $expected) {
$tests[] = $this->createTestCase(
"uk-$prefix--$input",
$rule['css'],
$expected,
'decimal',
"Decimal value $input"
);
}
return $tests;
}
/**
* Generates test cases for keyword-based properties.
*
* Creates comprehensive test coverage for properties that accept
* CSS keyword values. Includes both basic keyword tests and
* responsive variants across all breakpoints.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for keyword value scenarios.
*/
private function generateKeywordTests(string $prefix, array $rule): array {
$tests = [];
$cssProperty = $rule['css'];
$keywords = $this->keywordMap[$cssProperty] ?? [];
foreach ($keywords as $keyword) {
// Basic keyword.
$tests[] = $this->createTestCase(
"uk-$prefix--$keyword",
$cssProperty,
$keyword,
'keyword',
"Keyword: $keyword"
);
// Responsive keyword.
foreach (['md', 'lg'] as $bp) {
$tests[] = $this->createTestCase(
"uk-$bp-$prefix--$keyword",
$cssProperty,
$keyword,
'responsive-keyword',
"Responsive keyword at $bp",
$bp
);
}
}
return $tests;
}
/**
* Generates test cases for color properties.
*
* Creates comprehensive test coverage for color values including
* hex notation, alpha transparency, and various color formats
* supported by the UtiliKit color system.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for color value scenarios.
*/
private function generateColorTests(string $prefix, array $rule): array {
$tests = [];
$cssProperty = $rule['css'];
foreach ($this->testValues['colors'] as $color) {
// Basic color.
$tests[] = $this->createTestCase(
"uk-$prefix--$color",
$cssProperty,
"#$color",
'color',
"Hex color #$color"
);
// Color with alpha.
foreach ($this->testValues['alphas'] as $alpha) {
$alphaDecimal = $alpha / 100;
$r = hexdec(substr($color, 0, 2));
$g = hexdec(substr($color, 2, 2));
$b = hexdec(substr($color, 4, 2));
$tests[] = $this->createTestCase(
"uk-$prefix--$color-$alpha",
$cssProperty,
"rgba($r, $g, $b, $alphaDecimal)",
'color-alpha',
"Color with $alpha% alpha"
);
}
}
// 3-digit hex test
$tests[] = $this->createTestCase(
"uk-$prefix--f00",
$cssProperty,
"#ff0000",
'color-short',
"3-digit hex color"
);
return $tests;
}
/**
* Generates test cases for CSS transform properties.
*
* Creates test coverage for transform functions including rotation
* and scale transforms with support for decimal values using
* UtiliKit's 'd' notation system.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing transform type information.
*
* @return array
* Array of test cases for transform scenarios.
*/
private function generateTransformTests(string $prefix, array $rule): array {
$tests = [];
$transformType = $rule['isTransform'];
if ($transformType === 'rotate') {
foreach ([45, 90, 180, 270, 360] as $value) {
$tests[] = $this->createTestCase(
"uk-$prefix--$value",
'transform',
"rotate({$value}deg)",
'transform',
"Rotate {$value} degrees"
);
}
}
elseif ($transformType === 'scale') {
// Use 'd' format for decimal scale values.
$scaleValues = [
// 50% = 0.5x
'50' => 0.5,
// 75% = 0.75x
'75' => 0.75,
// 100% = 1x (normal)
'100' => 1,
// 110% = 1.1x
'110' => 1.1,
// 125% = 1.25x
'125' => 1.25,
// 150% = 1.5x
'150' => 1.5,
// 200% = 2x
'200' => 2.0,
// 250% = 2.5x
'250' => 2.5,
];
foreach ($scaleValues as $classValue => $scaleValue) {
$tests[] = $this->createTestCase(
"uk-$prefix--$classValue",
'transform',
"scale($scaleValue)",
'transform',
"Scale " . ($scaleValue * 100) . "%"
);
}
}
return $tests;
}
/**
* Generates test cases for CSS Grid template properties.
*
* Creates comprehensive test coverage for complex grid template
* definitions including repeat(), minmax(), and other CSS Grid
* functions. Includes both simple and complex grid patterns.
*
* @param string $prefix
* The UtiliKit prefix being tested (gc for columns, gr for rows).
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for grid template scenarios.
*/
private function generateGridTests(string $prefix, array $rule): array {
$tests = [];
$cssProperty = $rule['css'];
// For grid templates, we need to handle them differently.
if ($prefix === 'gc' || $prefix === 'gr') {
// Grid patterns that work in all browsers.
$workingPatterns = [
// ==========================================
// LEVEL 1: BASIC VALUES (Simplest)
// ==========================================
// Single values and basic combinations
'auto-auto' => 'auto auto',
'200px-1fr' => '200px 1fr',
'1fr-auto-2fr' => '1fr auto 2fr',
'100px-1fr-100px' => '100px 1fr 100px',
'1fr-2fr-0.5fr' => '1fr 2fr 0.5fr',
// ==========================================
// LEVEL 2: SIMPLE FUNCTIONS
// ==========================================
// Single function calls with basic parameters
// 2A: Simple repeat() functions
'repeat-3-1fr' => 'repeat(3, 1fr)',
'repeat-2-200px' => 'repeat(2, 200px)',
'repeat-4-1fr' => 'repeat(4, 1fr)',
// 2B: Simple minmax() functions
'minmax-100px-1fr' => 'minmax(100px, 1fr)',
'minmax-200px-1fr' => 'minmax(200px, 1fr)',
'minmax-50pr-2fr' => 'minmax(50%, 2fr)',
'minmax-10rem-1fr' => 'minmax(10rem, 1fr)',
// 2C: Simple fit-content() functions
'fitc-150px' => 'fit-content(150px)',
'fitc-250px' => 'fit-content(250px)',
// ==========================================
// LEVEL 3: NESTED FUNCTIONS
// ==========================================
// Functions within functions (higher complexity)
// 3A: repeat() with minmax() (fixed count)
'repeat-3-minmax-200px-1fr' => 'repeat(3, minmax(200px, 1fr))',
'repeat-2-minmax-100px-1fr' => 'repeat(2, minmax(100px, 1fr))',
// ==========================================
// LEVEL 4: AUTO-RESPONSIVE PATTERNS
// ==========================================
// Auto-fit and auto-fill (highest parsing complexity)
// 4A: auto-fit with minmax (most complex parsing)
'repeat-autofit-minmax-250px-1fr' => 'repeat(auto-fit, minmax(250px, 1fr))',
'repeat-autofit-minmax-280px-1fr' => 'repeat(auto-fit, minmax(280px, 1fr))',
'repeat-autofit-minmax-200px-1fr' => 'repeat(auto-fit, minmax(200px, 1fr))',
'repeat-autofit-minmax-300px-1fr' => 'repeat(auto-fit, minmax(300px, 1fr))',
// 4B: auto-fill with minmax
'repeat-autofill-minmax-200px-1fr' => 'repeat(auto-fill, minmax(200px, 1fr))',
'repeat-autofill-minmax-150px-1fr' => 'repeat(auto-fill, minmax(150px, 1fr))',
'repeat-autofill-minmax-250px-1fr' => 'repeat(auto-fill, minmax(250px, 1fr))',
// 4C: auto-fit/fill with fixed sizes (simpler than with minmax)
'repeat-autofit-250px' => 'repeat(auto-fit, 250px)',
'repeat-autofill-200px' => 'repeat(auto-fill, 200px)',
// ==========================================
// LEVEL 5: MIXED PATTERNS
// ==========================================
// Functions combined with basic values
// 5A: fit-content() with other values
'fitc-300px-1fr' => 'fit-content(300px) 1fr',
'1fr-fitc-250px' => '1fr fit-content(250px)',
// 5B: Complex combinations (existing patterns)
'fitc-150px-1fr-100px' => 'fit-content(150px) 1fr 100px',
'repeat-2-1fr-100px' => 'repeat(2, 1fr) 100px',
'repeat-4-minc' => 'repeat(4, min-content)',
'repeat-2-auto-1fr' => 'repeat(2, auto) 1fr',
'1fr-minc' => '1fr min-content',
'minc-1fr-auto-maxc' => 'min-content 1fr auto max-content',
'100px-minc-maxc' => '100px min-content max-content',
'minc-maxc' => 'min-content max-content',
// 5C: Advanced combinations (existing)
'minmax-100px-1fr-repeat-2-200px' => 'minmax(100px, 1fr) repeat(2, 200px)',
];
foreach ($workingPatterns as $pattern => $expected) {
$tests[] = $this->createTestCase(
"uk-$prefix--$pattern",
$cssProperty,
$expected,
'grid-template',
"Grid template: $expected"
);
}
// Add responsive variants.
$tests[] = $this->createTestCase(
"uk-md-$prefix--repeat-2-1fr-100px",
$cssProperty,
'repeat(2, 1fr) 100px',
'grid-template-responsive',
"Grid template at md: repeat(2, 1fr) 100px",
'md'
);
$tests[] = $this->createTestCase(
"uk-lg-$prefix--minmax-200px-1fr",
$cssProperty,
'minmax(200px, 1fr)',
'grid-template-responsive',
"Grid template at lg: minmax(200px, 1fr)",
'lg'
);
// Add these additional responsive test cases.
$tests[] = $this->createTestCase(
"uk-sm-$prefix--minmax-200px-1fr",
$cssProperty,
'minmax(200px, 1fr)',
'grid-template-responsive',
"Grid template at sm: minmax(200px, 1fr)",
'sm'
);
$tests[] = $this->createTestCase(
"uk-md-$prefix--repeat-2-1fr",
$cssProperty,
'repeat(2, 1fr)',
'grid-template-responsive',
"Grid template at md: repeat(2, 1fr)",
'md'
);
$tests[] = $this->createTestCase(
"uk-lg-$prefix--repeat-auto-fit-minmax-250px-1fr",
$cssProperty,
'repeat(auto-fit, minmax(250px, 1fr))',
'grid-template-responsive',
"Grid template at lg: repeat(auto-fit, minmax(250px, 1fr))",
'lg'
);
$tests[] = $this->createTestCase(
"uk-xl-$prefix--repeat-auto-fill-200px",
$cssProperty,
'repeat(auto-fill, 200px)',
'grid-template-responsive',
"Grid template at xl: repeat(auto-fill, 200px)",
'xl'
);
return $tests;
}
// Original fractional tests for non-grid properties.
foreach ([1, 2, 3] as $value) {
$tests[] = $this->createTestCase(
"uk-$prefix--{$value}fr",
$cssProperty,
"{$value}fr",
'fractional',
"Fractional unit {$value}fr"
);
}
return $tests;
}
/**
* Generates test cases for range-based properties.
*
* Creates test coverage for properties that use range notation,
* including CSS Grid positioning and aspect ratios. Supports
* the "start-end" format used by UtiliKit.
*
* @param string $prefix
* The UtiliKit prefix being tested.
* @param array $rule
* The rule definition containing CSS property information.
*
* @return array
* Array of test cases for range value scenarios.
*/
private function generateRangeTests(string $prefix, array $rule): array {
$tests = [];
$cssProperty = $rule['css'];
$ranges = [
'1-2' => '1 / 2',
'1-3' => '1 / 3',
'2-4' => '2 / 4',
'1-5' => '1 / 5',
];
foreach ($ranges as $range => $expected) {
$tests[] = $this->createTestCase(
"uk-$prefix--$range",
$cssProperty,
$expected,
'range',
"Grid range $expected"
);
}
// Test aspect ratio specifically.
if ($cssProperty === 'aspectRatio') {
$ratios = [
'16-9' => '16 / 9',
'4-3' => '4 / 3',
'1-1' => '1 / 1',
'21-9' => '21 / 9',
];
foreach ($ratios as $ratio => $expected) {
$tests[] = $this->createTestCase(
"uk-$prefix--$ratio",
$cssProperty,
$expected,
'aspect-ratio',
"Aspect ratio $expected"
);
}
}
return $tests;
}
/**
* Creates a standardized test case object.
*
* Generates a consistent test case structure with all necessary
* metadata for automated testing frameworks and validation systems.
*
* @param string $className
* The complete UtiliKit class name to test.
* @param string|null $cssProperty
* The expected CSS property name that should be set.
* @param mixed $expectedValue
* The expected CSS value that should be applied.
* @param string $testType
* The category/type of test for organization purposes.
* @param string $description
* Human-readable description of what the test validates.
* @param string|null $breakpoint
* The responsive breakpoint if applicable.
*
* @return array
* Structured test case array with all metadata.
*/
private function createTestCase(
string $className,
?string $cssProperty,
$expectedValue,
string $testType,
string $description,
?string $breakpoint = NULL,
): array {
return [
'className' => $className,
'cssProperty' => $cssProperty,
'expectedValue' => $expectedValue,
'testType' => $testType,
'description' => $description,
'breakpoint' => $breakpoint,
];
}
/**
* Generates test cases specifically for opacity properties.
*
* Creates comprehensive test coverage for opacity values including
* the conversion from 0-100 integer values to 0-1 decimal values
* as required by CSS. Includes responsive variants.
*
* @param string $prefix
* The UtiliKit prefix being tested (should be 'op').
*
* @return array
* Array of test cases for opacity scenarios.
*/
private function generateOpacityTests(string $prefix): array {
$tests = [];
// Standard opacity values.
foreach ([0, 25, 50, 75, 100] as $value) {
// Opacity is always a decimal between 0 and 1.
$cssValue = $value === 0 ? '0' : (string) ($value / 100);
// Base test.
$tests[] = $this->createTestCase(
"uk-op--$value",
'opacity',
$cssValue,
'opacity',
"Opacity $value%"
);
// Responsive variants.
foreach (['sm', 'md', 'lg', 'xl', 'xxl'] as $bp) {
$tests[] = $this->createTestCase(
"uk-$bp-op--$value",
'opacity',
$cssValue,
'opacity-responsive',
"Opacity $value% at $bp",
$bp
);
}
}
return $tests;
}
/**
* Calculates comprehensive test statistics for analysis and reporting.
*
* Analyzes the complete test suite to provide detailed statistics
* about test coverage, distribution across categories, and breakpoint
* coverage for quality assurance and reporting purposes.
*
* @return array
* Comprehensive statistics including:
* - totalTests: Total number of test cases generated
* - byGroup: Test distribution by rule group/category
* - byType: Test distribution by test type
* - byBreakpoint: Test distribution by responsive breakpoint
*/
public function getTestStatistics(): array {
$allTests = $this->generateAllTests();
$stats = [
'totalTests' => 0,
'byGroup' => [],
'byType' => [],
'byBreakpoint' => ['base' => 0],
];
foreach ($allTests as $ruleTests) {
$group = $ruleTests['group'];
$stats['byGroup'][$group] = ($stats['byGroup'][$group] ?? 0) + count($ruleTests['tests']);
foreach ($ruleTests['tests'] as $test) {
$stats['totalTests']++;
$type = $test['testType'];
$stats['byType'][$type] = ($stats['byType'][$type] ?? 0) + 1;
$bp = $test['breakpoint'] ?? 'base';
$stats['byBreakpoint'][$bp] = ($stats['byBreakpoint'][$bp] ?? 0) + 1;
}
}
return $stats;
}
/**
* Generates a flat array of all possible UtiliKit class names.
*
* Extracts all generated class names into a simple array format
* suitable for CSS generation, validation systems, and bulk
* processing operations. Removes duplicates and filters empty values.
*
* @return array
* Array of unique, valid UtiliKit class names ready for CSS generation
* or validation processing.
*/
public function getAllPossibleClasses(): array {
$allTests = $this->generateAllTests();
$classes = [];
foreach ($allTests as $ruleTest) {
if (!isset($ruleTest['tests']) || !is_array($ruleTest['tests'])) {
continue;
}
foreach ($ruleTest['tests'] as $test) {
// Use 'className' instead of 'class'.
if (isset($test['className']) && !empty($test['className']) && is_string($test['className'])) {
$classes[] = $test['className'];
}
}
}
// Remove duplicates and filter out any empty values.
$classes = array_unique(array_filter($classes));
return $classes;
}
}
