'); 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(''); expect(substr_count($html, 'class="skeleton'))->toBe(5); }); test('skeleton component renders card type', function () { $html = Blade::render(''); expect($html) ->toContain('skeleton') ->toContain('h-40') // Image placeholder height ->toContain('space-y-4'); }); test('skeleton component renders avatar type', function () { $html = Blade::render(''); expect($html) ->toContain('skeleton') ->toContain('h-12') ->toContain('w-12') ->toContain('rounded-full'); }); test('skeleton component renders button type', function () { $html = Blade::render(''); expect($html) ->toContain('skeleton') ->toContain('h-10') ->toContain('w-24') ->toContain('rounded-md'); }); test('skeleton component renders table-row type', function () { $html = Blade::render(''); expect($html) ->toContain('skeleton') ->toContain('flex') ->toContain('items-center') ->toContain('gap-4'); }); test('skeleton component accepts custom attributes', function () { $html = Blade::render(''); expect($html)->toContain('custom-class'); }); // ============================================ // Spinner Component Tests // ============================================ test('spinner component renders with default size and label', function () { $html = Blade::render(''); 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(''); expect($html) ->toContain('h-4') ->toContain('w-4'); }); test('spinner component renders large size', function () { $html = Blade::render(''); expect($html) ->toContain('h-8') ->toContain('w-8'); }); test('spinner component renders custom label', function () { $html = Blade::render(''); expect($html) ->toContain('Processing...') ->not->toContain(__('common.loading')); }); test('spinner component hides label when label is false', function () { $html = Blade::render(''); expect($html) ->not->toContain(__('common.loading')) ->toContain('animate-spin'); }); test('spinner component renders inline when inline is true', function () { $html = Blade::render(''); expect($html)->toContain('inline-flex'); }); test('spinner component renders as flex by default', function () { $html = Blade::render(''); expect($html) ->toContain('flex') ->not->toContain('inline-flex'); }); test('spinner svg has proper accessibility attributes', function () { $html = Blade::render(''); expect($html)->toContain('aria-hidden="true"'); }); // ============================================ // Toast Component Tests // ============================================ test('toast component renders with alpine data binding', function () { $html = Blade::render(''); 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(''); expect($html) ->toContain('top-4') ->toContain('end-4') ->toContain('fixed') ->toContain('z-50'); }); test('toast component renders at custom position', function () { $html = Blade::render(''); expect($html) ->toContain('bottom-4') ->toContain('start-4'); }); test('toast component has aria-live for accessibility', function () { $html = Blade::render(''); expect($html) ->toContain('aria-live="polite"') ->toContain('aria-atomic="true"'); }); test('toast component includes transition classes', function () { $html = Blade::render(''); expect($html) ->toContain('toast-enter') ->toContain('toast-enter-active'); }); test('toast component accepts custom attributes', function () { $html = Blade::render(''); expect($html)->toContain('custom-toast'); }); // ============================================ // Checkmark Icon Component Tests // ============================================ test('checkmark icon renders with animated class by default', function () { $html = Blade::render(''); expect($html) ->toContain('checkmark-animated') ->toContain('text-success') ->toContain('svg'); }); test('checkmark icon renders without animation when animated is false', function () { $html = Blade::render(''); expect($html) ->not->toContain('checkmark-animated') ->toContain('text-success'); }); test('checkmark icon renders default medium size', function () { $html = Blade::render(''); expect($html) ->toContain('h-6') ->toContain('w-6'); }); test('checkmark icon renders small size', function () { $html = Blade::render(''); expect($html) ->toContain('h-4') ->toContain('w-4'); }); test('checkmark icon renders large size', function () { $html = Blade::render(''); expect($html) ->toContain('h-8') ->toContain('w-8'); }); test('checkmark icon renders xl size', function () { $html = Blade::render(''); expect($html) ->toContain('h-12') ->toContain('w-12'); }); test('checkmark icon has proper accessibility attributes', function () { $html = Blade::render(''); expect($html)->toContain('aria-hidden="true"'); }); test('checkmark icon accepts custom attributes', function () { $html = Blade::render(''); 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'); });