mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2025-12-22 22:00:00 +00:00
�� MAJOR MILESTONE: Full Signal Management Integration Complete ## Signal Management System - ✅ Complete signal management infrastructure with ArcRwSignal & ArcMemo - ✅ Batched updates for performance optimization - ✅ Memory management with leak detection and pressure monitoring - ✅ Signal lifecycle management with automatic cleanup - ✅ Comprehensive testing with cargo nextest integration ## Component Migration (42/42 - 100% Success) - ✅ All 42 components migrated to new signal patterns - ✅ Signal-managed versions of all components (signal_managed.rs) - ✅ Zero compilation errors across entire workspace - ✅ Production-ready components with signal integration ## Developer Experience - ✅ Complete Storybook setup with interactive component playground - ✅ Comprehensive API documentation and migration guides - ✅ Integration examples and best practices - ✅ Component stories for Button, Input, Card, and Overview ## Production Infrastructure - ✅ Continuous benchmarking system (benchmark_runner.sh) - ✅ Production monitoring and health checks (production_monitor.sh) - ✅ Deployment validation scripts (deployment_validator.sh) - ✅ Performance tracking and optimization tools ## Key Features - ArcRwSignal for persistent state management - ArcMemo for computed values and optimization - BatchedSignalUpdater for performance - SignalMemoryManager for memory optimization - MemoryLeakDetector for leak prevention - TailwindSignalManager for styling integration ## Testing & Quality - ✅ Comprehensive test suite with TDD methodology - ✅ Integration tests for signal management - ✅ Performance benchmarks established - ✅ Memory management validation ## Documentation - ✅ Complete API documentation - ✅ Migration guides for Leptos 0.8.8 - ✅ Integration examples and tutorials - ✅ Architecture documentation This release represents a complete transformation of the component library to leverage Leptos 0.8.8's advanced signal system, providing developers with production-ready components that are optimized for performance, memory efficiency, and developer experience. Ready for production deployment and community adoption! 🚀
441 lines
15 KiB
TypeScript
441 lines
15 KiB
TypeScript
import { test, expect, Page } from '@playwright/test';
|
|
|
|
/**
|
|
* Demo Page E2E Tests
|
|
*
|
|
* Comprehensive tests for the leptos-shadcn-ui demo page showcasing
|
|
* performance advantages and component capabilities.
|
|
*/
|
|
|
|
test.describe('Demo Page E2E Tests', () => {
|
|
let page: Page;
|
|
|
|
test.beforeEach(async ({ page: testPage }) => {
|
|
page = testPage;
|
|
await page.goto('/demo/index.html');
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
// ===== PAGE LOAD AND STRUCTURE TESTS =====
|
|
|
|
test('should load demo page successfully', async () => {
|
|
await expect(page).toHaveTitle(/leptos-shadcn-ui Demo - Performance Champion/);
|
|
|
|
// Check main navigation
|
|
const nav = page.locator('nav');
|
|
await expect(nav).toBeVisible();
|
|
|
|
// Check hero section
|
|
const hero = page.locator('section.gradient-bg');
|
|
await expect(hero).toBeVisible();
|
|
|
|
// Check main heading
|
|
const mainHeading = page.locator('h1').first();
|
|
await expect(mainHeading).toHaveText(/Performance Champion/);
|
|
});
|
|
|
|
test('should have proper navigation links', async () => {
|
|
const navLinks = page.locator('nav a');
|
|
|
|
// Check navigation links exist
|
|
await expect(navLinks.filter({ hasText: 'Performance' })).toBeVisible();
|
|
await expect(navLinks.filter({ hasText: 'Components' })).toBeVisible();
|
|
await expect(navLinks.filter({ hasText: 'Comparison' })).toBeVisible();
|
|
await expect(navLinks.filter({ hasText: 'Live Demo' })).toBeVisible();
|
|
await expect(navLinks.filter({ hasText: 'GitHub' })).toBeVisible();
|
|
});
|
|
|
|
test('should have smooth scrolling navigation', async () => {
|
|
// Test smooth scrolling to sections
|
|
await page.click('a[href="#performance"]');
|
|
await page.waitForTimeout(500); // Wait for scroll animation
|
|
|
|
const performanceSection = page.locator('#performance');
|
|
await expect(performanceSection).toBeInViewport();
|
|
|
|
await page.click('a[href="#components"]');
|
|
await page.waitForTimeout(500);
|
|
|
|
const componentsSection = page.locator('#components');
|
|
await expect(componentsSection).toBeInViewport();
|
|
});
|
|
|
|
// ===== PERFORMANCE METRICS TESTS =====
|
|
|
|
test('should display performance metrics correctly', async () => {
|
|
const performanceSection = page.locator('#performance');
|
|
await expect(performanceSection).toBeVisible();
|
|
|
|
// Check performance grid
|
|
const performanceGrid = performanceSection.locator('.performance-grid');
|
|
await expect(performanceGrid).toBeVisible();
|
|
|
|
// Check individual metrics
|
|
const metrics = [
|
|
'3-5x', '5x', '3-8x', '0', '60 FPS', '100%'
|
|
];
|
|
|
|
for (const metric of metrics) {
|
|
const metricElement = performanceGrid.locator(`text=${metric}`).first();
|
|
await expect(metricElement).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should show performance comparison cards', async () => {
|
|
const comparisonCards = page.locator('.metric-card');
|
|
await expect(comparisonCards).toHaveCount(3);
|
|
|
|
// Check card titles
|
|
await expect(comparisonCards.nth(0)).toContainText('Initial Load Time');
|
|
await expect(comparisonCards.nth(1)).toContainText('Memory Usage');
|
|
await expect(comparisonCards.nth(2)).toContainText('Bundle Size');
|
|
|
|
// Check leptos-shadcn-ui metrics (should be green)
|
|
const leptosMetrics = comparisonCards.locator('.bg-green-100');
|
|
await expect(leptosMetrics).toHaveCount(3);
|
|
});
|
|
|
|
// ===== COMPONENT SHOWCASE TESTS =====
|
|
|
|
test('should display component showcase', async () => {
|
|
const componentsSection = page.locator('#components');
|
|
await expect(componentsSection).toBeVisible();
|
|
|
|
// Check component grid
|
|
const componentGrid = componentsSection.locator('.component-grid');
|
|
await expect(componentGrid).toBeVisible();
|
|
|
|
// Check component cards
|
|
const componentCards = componentGrid.locator('.component-showcase');
|
|
await expect(componentCards).toHaveCount(6);
|
|
|
|
// Check component titles
|
|
const componentTitles = [
|
|
'Button', 'Input', 'Card', 'Modal', 'Data Table', 'Form'
|
|
];
|
|
|
|
for (const title of componentTitles) {
|
|
const titleElement = componentCards.locator(`h3:has-text("${title}")`);
|
|
await expect(titleElement).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should show component performance metrics', async () => {
|
|
const componentCards = page.locator('.component-showcase');
|
|
|
|
// Check that each component card has performance metrics
|
|
for (let i = 0; i < 6; i++) {
|
|
const card = componentCards.nth(i);
|
|
const metrics = card.locator('.text-sm.text-gray-600');
|
|
await expect(metrics).toBeVisible();
|
|
|
|
// Should have render time and memory metrics
|
|
await expect(metrics).toContainText('Render Time:');
|
|
await expect(metrics).toContainText('Memory:');
|
|
}
|
|
});
|
|
|
|
// ===== INTERACTIVE DEMO TESTS =====
|
|
|
|
test('should have working performance test', async () => {
|
|
const perfTestButton = page.locator('#perfTest');
|
|
await expect(perfTestButton).toBeVisible();
|
|
await expect(perfTestButton).toHaveText('Run Performance Test');
|
|
|
|
// Click performance test button
|
|
await perfTestButton.click();
|
|
|
|
// Wait for results to appear
|
|
await page.waitForTimeout(100);
|
|
|
|
const results = page.locator('#perfResults');
|
|
await expect(results).toBeVisible();
|
|
|
|
// Check that results show performance metrics
|
|
await expect(results).toContainText('Click Response:');
|
|
await expect(results).toContainText('Render Time:');
|
|
await expect(results).toContainText('Memory Usage:');
|
|
});
|
|
|
|
test('should have working memory test', async () => {
|
|
const memoryTestButton = page.locator('#memoryTest');
|
|
await expect(memoryTestButton).toBeVisible();
|
|
await expect(memoryTestButton).toHaveText('Start Memory Test');
|
|
|
|
// Click memory test button
|
|
await memoryTestButton.click();
|
|
|
|
// Wait for memory test to run
|
|
await page.waitForTimeout(1000);
|
|
|
|
const memoryUsage = page.locator('#memoryUsage');
|
|
await expect(memoryUsage).toBeVisible();
|
|
|
|
const memoryBar = page.locator('#memoryBar');
|
|
await expect(memoryBar).toBeVisible();
|
|
});
|
|
|
|
test('should have working speed test', async () => {
|
|
const speedTestButton = page.locator('#speedTest');
|
|
await expect(speedTestButton).toBeVisible();
|
|
await expect(speedTestButton).toHaveText('Run Speed Test');
|
|
|
|
// Click speed test button
|
|
await speedTestButton.click();
|
|
|
|
// Wait for results to appear
|
|
await page.waitForTimeout(100);
|
|
|
|
const results = page.locator('#speedResults');
|
|
await expect(results).toBeVisible();
|
|
|
|
// Check that results show speed metrics
|
|
await expect(results).toContainText('Button Render:');
|
|
await expect(results).toContainText('Input Render:');
|
|
await expect(results).toContainText('Card Render:');
|
|
});
|
|
|
|
// ===== COMPARISON TABLE TESTS =====
|
|
|
|
test('should display detailed comparison table', async () => {
|
|
const comparisonSection = page.locator('#comparison');
|
|
await expect(comparisonSection).toBeVisible();
|
|
|
|
const comparisonTable = comparisonSection.locator('.comparison-table table');
|
|
await expect(comparisonTable).toBeVisible();
|
|
|
|
// Check table headers
|
|
const headers = comparisonTable.locator('th');
|
|
await expect(headers).toHaveCount(7);
|
|
|
|
const expectedHeaders = [
|
|
'Framework', 'Language', 'Initial Load', 'Memory Usage',
|
|
'Bundle Size', 'Type Safety', 'Memory Safety'
|
|
];
|
|
|
|
for (const header of expectedHeaders) {
|
|
await expect(headers.filter({ hasText: header })).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('should highlight leptos-shadcn-ui advantages', async () => {
|
|
const comparisonTable = page.locator('.comparison-table table');
|
|
|
|
// Check that leptos-shadcn-ui row has winner styling
|
|
const leptosRow = comparisonTable.locator('tr').filter({ hasText: 'leptos-shadcn-ui' });
|
|
await expect(leptosRow).toBeVisible();
|
|
|
|
// Check that leptos metrics are highlighted
|
|
const leptosCells = leptosRow.locator('td.leptos-winner');
|
|
await expect(leptosCells).toHaveCount(6); // All metric cells should be highlighted
|
|
});
|
|
|
|
// ===== RESPONSIVE DESIGN TESTS =====
|
|
|
|
test('should be responsive on mobile', async () => {
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
|
|
// Check that navigation is still accessible
|
|
const nav = page.locator('nav');
|
|
await expect(nav).toBeVisible();
|
|
|
|
// Check that hero section adapts
|
|
const hero = page.locator('section.gradient-bg');
|
|
await expect(hero).toBeVisible();
|
|
|
|
// Check that performance grid adapts
|
|
const performanceGrid = page.locator('.performance-grid');
|
|
await expect(performanceGrid).toBeVisible();
|
|
});
|
|
|
|
test('should be responsive on tablet', async () => {
|
|
await page.setViewportSize({ width: 768, height: 1024 });
|
|
|
|
// Check that all sections are visible
|
|
const sections = page.locator('section');
|
|
await expect(sections).toHaveCount(6); // Should have 6 main sections
|
|
|
|
// Check that component grid adapts
|
|
const componentGrid = page.locator('.component-grid');
|
|
await expect(componentGrid).toBeVisible();
|
|
});
|
|
|
|
// ===== ACCESSIBILITY TESTS =====
|
|
|
|
test('should be keyboard accessible', async () => {
|
|
// Test keyboard navigation
|
|
await page.keyboard.press('Tab');
|
|
await page.keyboard.press('Tab');
|
|
await page.keyboard.press('Tab');
|
|
|
|
// Check that focus is visible
|
|
const focusedElement = page.locator(':focus');
|
|
await expect(focusedElement).toBeVisible();
|
|
});
|
|
|
|
test('should have proper ARIA labels', async () => {
|
|
// Check that interactive elements have proper labels
|
|
const buttons = page.locator('button');
|
|
const buttonCount = await buttons.count();
|
|
|
|
for (let i = 0; i < buttonCount; i++) {
|
|
const button = buttons.nth(i);
|
|
const text = await button.textContent();
|
|
const ariaLabel = await button.getAttribute('aria-label');
|
|
|
|
// Should have either text content or aria-label
|
|
expect(text || ariaLabel).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
test('should have proper heading hierarchy', async () => {
|
|
// Check heading structure
|
|
const h1 = page.locator('h1');
|
|
await expect(h1).toHaveCount(1);
|
|
|
|
const h2 = page.locator('h2');
|
|
await expect(h2).toHaveCount(4); // Should have 4 main sections
|
|
|
|
const h3 = page.locator('h3');
|
|
await expect(h3).toHaveCount(6); // Should have 6 component cards
|
|
});
|
|
|
|
// ===== PERFORMANCE TESTS =====
|
|
|
|
test('should load within performance budget', async () => {
|
|
const startTime = Date.now();
|
|
|
|
await page.goto('/demo/index.html');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
// Demo page should load within 2 seconds
|
|
expect(loadTime).toBeLessThan(2000);
|
|
});
|
|
|
|
test('should have optimal bundle size', async () => {
|
|
// Check that external resources load efficiently
|
|
const responses = await page.evaluate(() => {
|
|
return performance.getEntriesByType('resource')
|
|
.filter((entry: any) => entry.name.includes('tailwindcss.com'))
|
|
.map((entry: any) => ({
|
|
name: entry.name,
|
|
size: entry.transferSize || 0,
|
|
duration: entry.duration
|
|
}));
|
|
});
|
|
|
|
// Tailwind CSS should load efficiently
|
|
if (responses.length > 0) {
|
|
expect(responses[0].duration).toBeLessThan(1000);
|
|
}
|
|
});
|
|
|
|
// ===== INTERACTION TESTS =====
|
|
|
|
test('should handle button interactions smoothly', async () => {
|
|
const buttons = page.locator('button');
|
|
const buttonCount = await buttons.count();
|
|
|
|
// Test clicking all buttons
|
|
for (let i = 0; i < Math.min(buttonCount, 5); i++) {
|
|
const button = buttons.nth(i);
|
|
await button.click();
|
|
await page.waitForTimeout(100);
|
|
}
|
|
|
|
// Page should still be responsive
|
|
const hero = page.locator('section.gradient-bg');
|
|
await expect(hero).toBeVisible();
|
|
});
|
|
|
|
test('should handle hover effects', async () => {
|
|
const hoverElements = page.locator('.card-hover');
|
|
const elementCount = await hoverElements.count();
|
|
|
|
// Test hover effects
|
|
for (let i = 0; i < Math.min(elementCount, 3); i++) {
|
|
const element = hoverElements.nth(i);
|
|
await element.hover();
|
|
await page.waitForTimeout(200);
|
|
}
|
|
|
|
// Elements should still be visible after hover
|
|
await expect(hoverElements.first()).toBeVisible();
|
|
});
|
|
|
|
// ===== CONTENT VALIDATION TESTS =====
|
|
|
|
test('should have correct performance messaging', async () => {
|
|
// Check hero section messaging
|
|
const heroText = page.locator('section.gradient-bg p');
|
|
await expect(heroText).toContainText('3-5x Faster than React/Next.js');
|
|
|
|
// Check performance section messaging
|
|
const performanceText = page.locator('#performance p');
|
|
await expect(performanceText).toContainText('Measurable performance advantages');
|
|
});
|
|
|
|
test('should have correct component information', async () => {
|
|
const componentsText = page.locator('#components p');
|
|
await expect(componentsText).toContainText('38 production-ready components');
|
|
});
|
|
|
|
test('should have correct comparison information', async () => {
|
|
const comparisonText = page.locator('#comparison p');
|
|
await expect(comparisonText).toContainText('Comprehensive performance comparison');
|
|
});
|
|
|
|
// ===== EXTERNAL LINKS TESTS =====
|
|
|
|
test('should have working external links', async () => {
|
|
const githubLink = page.locator('a[href*="github.com"]');
|
|
await expect(githubLink).toBeVisible();
|
|
|
|
// Check that link opens in new tab
|
|
const target = await githubLink.getAttribute('target');
|
|
expect(target).toBe('_blank');
|
|
});
|
|
|
|
// ===== ERROR HANDLING TESTS =====
|
|
|
|
test('should handle missing resources gracefully', async () => {
|
|
// Simulate network failure for external resources
|
|
await page.route('**/tailwindcss.com/**', (route) => {
|
|
route.abort('failed');
|
|
});
|
|
|
|
// Page should still load and function
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const hero = page.locator('section.gradient-bg');
|
|
await expect(hero).toBeVisible();
|
|
});
|
|
|
|
// ===== CROSS-BROWSER COMPATIBILITY TESTS =====
|
|
|
|
test('should work consistently across browsers', async () => {
|
|
// Test basic functionality
|
|
const hero = page.locator('section.gradient-bg');
|
|
await expect(hero).toBeVisible();
|
|
|
|
// Test navigation
|
|
await page.click('a[href="#performance"]');
|
|
await page.waitForTimeout(500);
|
|
|
|
const performanceSection = page.locator('#performance');
|
|
await expect(performanceSection).toBeInViewport();
|
|
|
|
// Test interactions
|
|
const perfTestButton = page.locator('#perfTest');
|
|
await perfTestButton.click();
|
|
await page.waitForTimeout(100);
|
|
|
|
const results = page.locator('#perfResults');
|
|
await expect(results).toBeVisible();
|
|
});
|
|
});
|
|
|