Files
leptos-shadcn-ui/tests/e2e/demo-page.spec.ts
Peter Hanssens eba29c0868 feat: Complete Leptos 0.8.8 Signal Integration with 100% Component Migration
�� 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! 🚀
2025-09-13 15:41:24 +10:00

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();
});
});