libra/tests/Feature/Components/AnimationComponentsTest.php

307 lines
8.6 KiB
PHP

<?php
use Illuminate\Support\Facades\Blade;
// ============================================
// Skeleton Component Tests
// ============================================
test('skeleton component renders default text type with 3 lines', function () {
$html = Blade::render('<x-skeleton />');
expect($html)
->toContain('skeleton')
->toContain('h-4')
->toContain('space-y-3');
// Should have 3 skeleton divs (default lines)
expect(substr_count($html, 'class="skeleton'))->toBe(3);
});
test('skeleton component renders custom number of lines', function () {
$html = Blade::render('<x-skeleton :lines="5" />');
expect(substr_count($html, 'class="skeleton'))->toBe(5);
});
test('skeleton component renders card type', function () {
$html = Blade::render('<x-skeleton type="card" />');
expect($html)
->toContain('skeleton')
->toContain('h-40') // Image placeholder height
->toContain('space-y-4');
});
test('skeleton component renders avatar type', function () {
$html = Blade::render('<x-skeleton type="avatar" />');
expect($html)
->toContain('skeleton')
->toContain('h-12')
->toContain('w-12')
->toContain('rounded-full');
});
test('skeleton component renders button type', function () {
$html = Blade::render('<x-skeleton type="button" />');
expect($html)
->toContain('skeleton')
->toContain('h-10')
->toContain('w-24')
->toContain('rounded-md');
});
test('skeleton component renders table-row type', function () {
$html = Blade::render('<x-skeleton type="table-row" />');
expect($html)
->toContain('skeleton')
->toContain('flex')
->toContain('items-center')
->toContain('gap-4');
});
test('skeleton component accepts custom attributes', function () {
$html = Blade::render('<x-skeleton class="custom-class" />');
expect($html)->toContain('custom-class');
});
// ============================================
// Spinner Component Tests
// ============================================
test('spinner component renders with default size and label', function () {
$html = Blade::render('<x-spinner />');
expect($html)
->toContain('animate-spin')
->toContain('h-5')
->toContain('w-5')
->toContain('text-gold')
->toContain(__('common.loading'));
});
test('spinner component renders small size', function () {
$html = Blade::render('<x-spinner size="sm" />');
expect($html)
->toContain('h-4')
->toContain('w-4');
});
test('spinner component renders large size', function () {
$html = Blade::render('<x-spinner size="lg" />');
expect($html)
->toContain('h-8')
->toContain('w-8');
});
test('spinner component renders custom label', function () {
$html = Blade::render('<x-spinner label="Processing..." />');
expect($html)
->toContain('Processing...')
->not->toContain(__('common.loading'));
});
test('spinner component hides label when label is false', function () {
$html = Blade::render('<x-spinner :label="false" />');
expect($html)
->not->toContain(__('common.loading'))
->toContain('animate-spin');
});
test('spinner component renders inline when inline is true', function () {
$html = Blade::render('<x-spinner :inline="true" />');
expect($html)->toContain('inline-flex');
});
test('spinner component renders as flex by default', function () {
$html = Blade::render('<x-spinner />');
expect($html)
->toContain('flex')
->not->toContain('inline-flex');
});
test('spinner svg has proper accessibility attributes', function () {
$html = Blade::render('<x-spinner />');
expect($html)->toContain('aria-hidden="true"');
});
// ============================================
// Toast Component Tests
// ============================================
test('toast component renders with alpine data binding', function () {
$html = Blade::render('<x-toast />');
expect($html)
->toContain('x-data')
->toContain('toasts: []')
->toContain('x-on:toast.window');
});
test('toast component renders at top-right position by default', function () {
$html = Blade::render('<x-toast />');
expect($html)
->toContain('top-4')
->toContain('end-4')
->toContain('fixed')
->toContain('z-50');
});
test('toast component renders at custom position', function () {
$html = Blade::render('<x-toast position="bottom-left" />');
expect($html)
->toContain('bottom-4')
->toContain('start-4');
});
test('toast component has aria-live for accessibility', function () {
$html = Blade::render('<x-toast />');
expect($html)
->toContain('aria-live="polite"')
->toContain('aria-atomic="true"');
});
test('toast component includes transition classes', function () {
$html = Blade::render('<x-toast />');
expect($html)
->toContain('toast-enter')
->toContain('toast-enter-active');
});
test('toast component accepts custom attributes', function () {
$html = Blade::render('<x-toast class="custom-toast" />');
expect($html)->toContain('custom-toast');
});
// ============================================
// Checkmark Icon Component Tests
// ============================================
test('checkmark icon renders with animated class by default', function () {
$html = Blade::render('<x-icons.checkmark />');
expect($html)
->toContain('checkmark-animated')
->toContain('text-success')
->toContain('svg');
});
test('checkmark icon renders without animation when animated is false', function () {
$html = Blade::render('<x-icons.checkmark :animated="false" />');
expect($html)
->not->toContain('checkmark-animated')
->toContain('text-success');
});
test('checkmark icon renders default medium size', function () {
$html = Blade::render('<x-icons.checkmark />');
expect($html)
->toContain('h-6')
->toContain('w-6');
});
test('checkmark icon renders small size', function () {
$html = Blade::render('<x-icons.checkmark size="sm" />');
expect($html)
->toContain('h-4')
->toContain('w-4');
});
test('checkmark icon renders large size', function () {
$html = Blade::render('<x-icons.checkmark size="lg" />');
expect($html)
->toContain('h-8')
->toContain('w-8');
});
test('checkmark icon renders xl size', function () {
$html = Blade::render('<x-icons.checkmark size="xl" />');
expect($html)
->toContain('h-12')
->toContain('w-12');
});
test('checkmark icon has proper accessibility attributes', function () {
$html = Blade::render('<x-icons.checkmark />');
expect($html)->toContain('aria-hidden="true"');
});
test('checkmark icon accepts custom attributes', function () {
$html = Blade::render('<x-icons.checkmark class="custom-class" />');
expect($html)->toContain('custom-class');
});
// ============================================
// Animation CSS Class Tests
// ============================================
test('animation classes exist in app.css', function () {
$css = file_get_contents(resource_path('css/app.css'));
expect($css)
->toContain('.transition-default')
->toContain('.transition-slow')
->toContain('.btn')
->toContain('.card-hover')
->toContain('.skeleton')
->toContain('.toast-enter')
->toContain('.toast-enter-active')
->toContain('@keyframes checkmark')
->toContain('.checkmark-animated')
->toContain('@keyframes shake')
->toContain('.shake')
->toContain('.modal-enter')
->toContain('.progress-bar');
});
test('animation durations are under 300ms in app.css', function () {
$css = file_get_contents(resource_path('css/app.css'));
// Check that we use duration-150 and duration-200 (both under 300ms)
expect($css)
->toContain('duration-150')
->toContain('duration-200');
// Check keyframe animations are 0.3s (300ms) or less
expect($css)
->toContain('animation: checkmark 0.3s')
->toContain('animation: shake 0.3s');
});
test('reduced motion media query exists in app.css', function () {
$css = file_get_contents(resource_path('css/app.css'));
expect($css)->toContain('@media (prefers-reduced-motion: reduce)');
});
test('rtl toast animation classes exist in app.css', function () {
$css = file_get_contents(resource_path('css/app.css'));
expect($css)
->toContain('[dir="rtl"] .toast-enter')
->toContain('[dir="rtl"] .toast-enter-active');
});