mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2025-12-22 22:00:00 +00:00
Add comprehensive demo with CI/CD pipeline
- Complete GitHub Actions workflow for demo deployment - Playwright test suite with 50+ tests - Performance monitoring and accessibility testing - Cross-browser compatibility testing - Local deployment scripts and documentation
This commit is contained in:
368
tests/e2e/comprehensive-demo.spec.ts
Normal file
368
tests/e2e/comprehensive-demo.spec.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Comprehensive Demo E2E Tests
|
||||
*
|
||||
* These tests ensure the comprehensive dashboard demo functions as expected,
|
||||
* showcasing all interactive features, responsive design, and component capabilities.
|
||||
*/
|
||||
|
||||
test.describe('Comprehensive Dashboard Demo E2E Tests', () => {
|
||||
let page: Page;
|
||||
|
||||
test.beforeEach(async ({ page: testPage }) => {
|
||||
page = testPage;
|
||||
// Navigate to the comprehensive demo
|
||||
await page.goto('http://localhost:8001');
|
||||
await page.waitForLoadState('networkidle');
|
||||
// Wait for WASM to load
|
||||
await page.waitForSelector('nav', { timeout: 10000 });
|
||||
});
|
||||
|
||||
test.describe('Page Structure and Navigation', () => {
|
||||
test('should load the comprehensive dashboard successfully', async () => {
|
||||
await expect(page).toHaveTitle(/Leptos Dashboard - ShadCN UI Demo/);
|
||||
|
||||
// Check for main navigation
|
||||
const nav = page.locator('nav');
|
||||
await expect(nav).toBeVisible();
|
||||
|
||||
// Check for dashboard title
|
||||
const dashboardTitle = page.locator('h1:has-text("Dashboard")');
|
||||
await expect(dashboardTitle).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have proper sidebar navigation', async () => {
|
||||
const sidebar = page.locator('div:has-text("Leptos Dashboard")');
|
||||
await expect(sidebar).toBeVisible();
|
||||
|
||||
// Check navigation links
|
||||
const dashboardLink = page.locator('a:has-text("Dashboard")');
|
||||
const analyticsLink = page.locator('a:has-text("Analytics")');
|
||||
const projectsLink = page.locator('a:has-text("Projects")');
|
||||
const teamLink = page.locator('a:has-text("Team")');
|
||||
const documentsLink = page.locator('a:has-text("Documents")');
|
||||
const settingsLink = page.locator('a:has-text("Settings")');
|
||||
|
||||
await expect(dashboardLink).toBeVisible();
|
||||
await expect(analyticsLink).toBeVisible();
|
||||
await expect(projectsLink).toBeVisible();
|
||||
await expect(teamLink).toBeVisible();
|
||||
await expect(documentsLink).toBeVisible();
|
||||
await expect(settingsLink).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have welcome section with proper messaging', async () => {
|
||||
const welcomeTitle = page.locator('h2:has-text("Welcome back!")');
|
||||
const welcomeDescription = page.locator('text=Here\'s what\'s happening with your projects today.');
|
||||
|
||||
await expect(welcomeTitle).toBeVisible();
|
||||
await expect(welcomeDescription).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Metrics Cards', () => {
|
||||
test('should display all metric cards', async () => {
|
||||
const metricCards = page.locator('div:has-text("Total Revenue"), div:has-text("New Customers"), div:has-text("Active Accounts"), div:has-text("Growth Rate")');
|
||||
await expect(metricCards).toHaveCount(4);
|
||||
|
||||
// Check individual cards
|
||||
await expect(page.locator('text=Total Revenue')).toBeVisible();
|
||||
await expect(page.locator('text=New Customers')).toBeVisible();
|
||||
await expect(page.locator('text=Active Accounts')).toBeVisible();
|
||||
await expect(page.locator('text=Growth Rate')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have interactive metric cards', async () => {
|
||||
// Test Total Revenue card
|
||||
const revenueCard = page.locator('div:has-text("Total Revenue")').first();
|
||||
await expect(revenueCard).toBeVisible();
|
||||
|
||||
// Click the card to update revenue
|
||||
await revenueCard.click();
|
||||
|
||||
// Check that revenue value is displayed
|
||||
const revenueValue = page.locator('text=/\\$\\d+\\.\\d+/').first();
|
||||
await expect(revenueValue).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have hover effects on metric cards', async () => {
|
||||
const metricCard = page.locator('div:has-text("Total Revenue")').first();
|
||||
await metricCard.hover();
|
||||
|
||||
// Card should still be visible after hover
|
||||
await expect(metricCard).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Interactive Dashboard Section', () => {
|
||||
test('should have interactive counter', async () => {
|
||||
const counterSection = page.locator('text=Interactive Counter');
|
||||
await expect(counterSection).toBeVisible();
|
||||
|
||||
// Test counter buttons
|
||||
const incrementButton = page.locator('button:has-text("+")');
|
||||
const decrementButton = page.locator('button:has-text("-")');
|
||||
const resetButton = page.locator('button:has-text("Reset")');
|
||||
|
||||
await expect(incrementButton).toBeVisible();
|
||||
await expect(decrementButton).toBeVisible();
|
||||
await expect(resetButton).toBeVisible();
|
||||
|
||||
// Test counter functionality
|
||||
await incrementButton.click();
|
||||
await incrementButton.click();
|
||||
|
||||
// Check that counter value is displayed
|
||||
const counterValue = page.locator('text=/\\d+/').first();
|
||||
await expect(counterValue).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have input component', async () => {
|
||||
const inputSection = page.locator('text=Input Component');
|
||||
await expect(inputSection).toBeVisible();
|
||||
|
||||
// Test input field
|
||||
const inputField = page.locator('input[placeholder="Type something..."]');
|
||||
await expect(inputField).toBeVisible();
|
||||
|
||||
// Type in the input
|
||||
await inputField.fill('Test input');
|
||||
await expect(inputField).toHaveValue('Test input');
|
||||
});
|
||||
|
||||
test('should have Tailwind-RS-WASM demo section', async () => {
|
||||
const tailwindSection = page.locator('text=Tailwind-RS-WASM Demo');
|
||||
await expect(tailwindSection).toBeVisible();
|
||||
|
||||
// Check for demo elements
|
||||
const shadcnButton = page.locator('button:has-text("ShadCN Button")');
|
||||
const dynamicStyling = page.locator('text=Dynamic styling with Tailwind CSS');
|
||||
const bestOfBothWorlds = page.locator('button:has-text("Best of both worlds!")');
|
||||
|
||||
await expect(shadcnButton).toBeVisible();
|
||||
await expect(dynamicStyling).toBeVisible();
|
||||
await expect(bestOfBothWorlds).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Recent Activity Section', () => {
|
||||
test('should display recent activity feed', async () => {
|
||||
const activitySection = page.locator('text=Recent Activity');
|
||||
await expect(activitySection).toBeVisible();
|
||||
|
||||
// Check for activity items
|
||||
const activityItems = page.locator('div:has-text("Eddie Lake completed Cover page")');
|
||||
await expect(activityItems).toBeVisible();
|
||||
|
||||
// Check for timestamps
|
||||
const timestamps = page.locator('text=2 hours ago, text=4 hours ago, text=6 hours ago');
|
||||
await expect(timestamps.first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Data Table Section', () => {
|
||||
test('should display project documents table', async () => {
|
||||
const tableSection = page.locator('text=Project Documents');
|
||||
await expect(tableSection).toBeVisible();
|
||||
|
||||
// Check table headers
|
||||
const headers = page.locator('th');
|
||||
await expect(headers.filter({ hasText: 'Document' })).toBeVisible();
|
||||
await expect(headers.filter({ hasText: 'Type' })).toBeVisible();
|
||||
await expect(headers.filter({ hasText: 'Status' })).toBeVisible();
|
||||
await expect(headers.filter({ hasText: 'Assignee' })).toBeVisible();
|
||||
await expect(headers.filter({ hasText: 'Actions' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have functional open menu buttons', async () => {
|
||||
const openMenuButtons = page.locator('button:has-text("Open menu")');
|
||||
await expect(openMenuButtons).toHaveCount(3);
|
||||
|
||||
// Click the first open menu button
|
||||
await openMenuButtons.first().click();
|
||||
|
||||
// Check that dropdown menu appears
|
||||
const dropdownMenu = page.locator('div:has-text("Edit"), div:has-text("Copy"), div:has-text("Delete")');
|
||||
await expect(dropdownMenu).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have status badges', async () => {
|
||||
const statusBadges = page.locator('span:has-text("In Process"), span:has-text("Done")');
|
||||
await expect(statusBadges).toHaveCount(3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Theme and Sidebar Toggle', () => {
|
||||
test('should have theme toggle functionality', async () => {
|
||||
const themeToggle = page.locator('button:has-text("Dark"), button:has-text("Light")');
|
||||
await expect(themeToggle).toBeVisible();
|
||||
|
||||
// Click theme toggle
|
||||
await themeToggle.click();
|
||||
|
||||
// Check that theme changes (dark class should be applied)
|
||||
const body = page.locator('body');
|
||||
await expect(body).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have sidebar toggle functionality', async () => {
|
||||
const sidebarToggle = page.locator('button:has-text("☰")');
|
||||
await expect(sidebarToggle).toBeVisible();
|
||||
|
||||
// Click sidebar toggle
|
||||
await sidebarToggle.click();
|
||||
|
||||
// Check that sidebar is hidden/shown
|
||||
const sidebar = page.locator('div:has-text("Leptos Dashboard")');
|
||||
await expect(sidebar).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Responsive Design', () => {
|
||||
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 dashboard content is visible
|
||||
const dashboardTitle = page.locator('h1:has-text("Dashboard")');
|
||||
await expect(dashboardTitle).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('main');
|
||||
await expect(sections).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Accessibility', () => {
|
||||
test('should have proper heading structure', async () => {
|
||||
const h1 = page.locator('h1');
|
||||
const h2 = page.locator('h2');
|
||||
|
||||
await expect(h1).toHaveCount(1);
|
||||
const h2Count = await h2.count();
|
||||
expect(h2Count).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('should have proper button labels', async () => {
|
||||
const buttons = page.locator('button');
|
||||
const buttonCount = await buttons.count();
|
||||
|
||||
for (let i = 0; i < Math.min(buttonCount, 10); i++) {
|
||||
const button = buttons.nth(i);
|
||||
const text = await button.textContent();
|
||||
expect(text?.trim()).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
test('should have proper input labels', async () => {
|
||||
const inputs = page.locator('input');
|
||||
const inputCount = await inputs.count();
|
||||
|
||||
for (let i = 0; i < inputCount; i++) {
|
||||
const input = inputs.nth(i);
|
||||
const placeholder = await input.getAttribute('placeholder');
|
||||
expect(placeholder).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Performance and Loading', () => {
|
||||
test('should load within acceptable time', async () => {
|
||||
const startTime = Date.now();
|
||||
await page.goto('http://localhost:8001');
|
||||
await page.waitForLoadState('networkidle');
|
||||
const loadTime = Date.now() - startTime;
|
||||
|
||||
// Should load within 5 seconds
|
||||
expect(loadTime).toBeLessThan(5000);
|
||||
});
|
||||
|
||||
test('should have proper WASM loading', async () => {
|
||||
// Check that WASM is loaded by looking for interactive elements
|
||||
const interactiveButton = page.locator('button:has-text("+")');
|
||||
await expect(interactiveButton).toBeVisible();
|
||||
|
||||
// Test that WASM interactions work
|
||||
await interactiveButton.click();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Visual Quality', () => {
|
||||
test('should have proper styling and colors', async () => {
|
||||
// Check for proper button styling
|
||||
const primaryButton = page.locator('button:has-text("+")').first();
|
||||
await expect(primaryButton).toBeVisible();
|
||||
|
||||
// Check for card styling
|
||||
const card = page.locator('div:has-text("Total Revenue")').first();
|
||||
await expect(card).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have proper spacing and layout', async () => {
|
||||
// Check that sections are properly spaced
|
||||
const main = page.locator('main');
|
||||
await expect(main).toBeVisible();
|
||||
|
||||
// Check for proper grid layouts
|
||||
const grid = page.locator('.grid').first();
|
||||
await expect(grid).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Interactive Features', () => {
|
||||
test('should handle button interactions smoothly', async () => {
|
||||
const buttons = page.locator('button');
|
||||
const buttonCount = await buttons.count();
|
||||
|
||||
// Test clicking several 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 dashboardTitle = page.locator('h1:has-text("Dashboard")');
|
||||
await expect(dashboardTitle).toBeVisible();
|
||||
});
|
||||
|
||||
test('should handle hover effects', async () => {
|
||||
const hoverElements = page.locator('div:has-text("Total Revenue")');
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Error Handling', () => {
|
||||
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 dashboardTitle = page.locator('h1:has-text("Dashboard")');
|
||||
await expect(dashboardTitle).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user