Files
leptos-shadcn-ui/examples/leptos/tests/tailwind-rs-core.spec.ts
Peter Hanssens 7a36292cf9 🚀 Release v0.1.0: WASM-compatible components with tailwind-rs-core v0.4.0
- Fixed compilation errors in menubar, combobox, and drawer packages
- Updated to tailwind-rs-core v0.4.0 and tailwind-rs-wasm v0.4.0 for WASM compatibility
- Cleaned up unused variable warnings across packages
- Updated release documentation with WASM integration details
- Demo working with dynamic color API and Tailwind CSS generation
- All 25+ core components ready for crates.io publication

Key features:
 WASM compatibility (no more tokio/mio dependencies)
 Dynamic Tailwind CSS class generation
 Type-safe color utilities
 Production-ready component library
2025-09-16 08:36:13 +10:00

258 lines
9.1 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Tailwind-RS-Core Integration Tests @tailwind-rs-core', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
await page.waitForSelector('h1', { timeout: 10000 });
});
test('should generate dynamic classes using TailwindClasses API', async ({ page }) => {
// Check that the page has proper Tailwind classes applied
const body = page.locator('body');
const bodyClasses = await body.evaluate((el) => el.className);
// Should have responsive classes
expect(bodyClasses).toContain('min-h-screen');
expect(bodyClasses).toContain('transition-all');
expect(bodyClasses).toContain('duration-700');
// Check hero section for TailwindClasses API usage
const heroSection = page.locator('section').first();
const heroClasses = await heroSection.evaluate((el) => el.className);
// Should have classes generated by TailwindClasses API
expect(heroClasses).toContain('text-white');
expect(heroClasses).toContain('py-24');
expect(heroClasses).toContain('relative');
expect(heroClasses).toContain('overflow-hidden');
expect(heroClasses).toContain('flex');
expect(heroClasses).toContain('items-center');
expect(heroClasses).toContain('justify-center');
});
test('should apply responsive classes correctly', async ({ page }) => {
const heroSection = page.locator('section').first();
// Test desktop view
await page.setViewportSize({ width: 1920, height: 1080 });
await page.waitForTimeout(500);
const desktopStyles = await heroSection.evaluate((el) => {
const styles = window.getComputedStyle(el);
return {
paddingTop: styles.paddingTop,
paddingBottom: styles.paddingBottom,
};
});
// Should have lg padding (py-24 = 6rem = 96px)
expect(parseInt(desktopStyles.paddingTop)).toBeGreaterThanOrEqual(90);
// Test tablet view
await page.setViewportSize({ width: 768, height: 1024 });
await page.waitForTimeout(500);
const tabletStyles = await heroSection.evaluate((el) => {
const styles = window.getComputedStyle(el);
return {
paddingTop: styles.paddingTop,
paddingBottom: styles.paddingBottom,
};
});
// Should have md padding (py-20 = 5rem = 80px)
expect(parseInt(tabletStyles.paddingTop)).toBeLessThan(parseInt(desktopStyles.paddingTop));
// Test mobile view
await page.setViewportSize({ width: 375, height: 667 });
await page.waitForTimeout(500);
const mobileStyles = await heroSection.evaluate((el) => {
const styles = window.getComputedStyle(el);
return {
paddingTop: styles.paddingTop,
paddingBottom: styles.paddingBottom,
};
});
// Should have sm padding (py-16 = 4rem = 64px)
expect(parseInt(mobileStyles.paddingTop)).toBeLessThan(parseInt(tabletStyles.paddingTop));
});
test('should merge custom classes with base classes', async ({ page }) => {
// Check that custom classes are properly merged
const heroSection = page.locator('section').first();
const heroClasses = await heroSection.evaluate((el) => el.className);
// Should have both base classes and custom classes
expect(heroClasses).toContain('text-white'); // base class
expect(heroClasses).toContain('py-24'); // base class
expect(heroClasses).toContain('flex'); // custom class
expect(heroClasses).toContain('items-center'); // custom class
expect(heroClasses).toContain('justify-center'); // custom class
// Should have gradient background (custom class)
const backgroundImage = await heroSection.evaluate((el) => {
const styles = window.getComputedStyle(el);
return styles.backgroundImage;
});
expect(backgroundImage).toContain('gradient');
});
test('should apply state classes correctly', async ({ page }) => {
// Check for hover states and transitions
const buttons = page.locator('button');
const firstButton = buttons.first();
const buttonClasses = await firstButton.evaluate((el) => el.className);
// Should have transition classes
expect(buttonClasses).toMatch(/transition/);
// Test hover state
await firstButton.hover();
await page.waitForTimeout(100);
// Check that hover styles are applied
const hoverStyles = await firstButton.evaluate((el) => {
const styles = window.getComputedStyle(el);
return {
transform: styles.transform,
boxShadow: styles.boxShadow,
};
});
// Should have some hover effect
expect(hoverStyles.transform).not.toBe('none');
});
test('should generate color-specific classes dynamically', async ({ page }) => {
// Get initial color classes
const heroSection = page.locator('section').first();
const initialClasses = await heroSection.evaluate((el) => el.className);
// Click on blue color
const blueButton = page.locator('button').filter({ hasText: 'blue' });
await blueButton.click();
await page.waitForTimeout(500);
const blueClasses = await heroSection.evaluate((el) => el.className);
// Should have blue-specific classes
expect(blueClasses).toMatch(/blue/);
// Click on green color
const greenButton = page.locator('button').filter({ hasText: 'green' });
await greenButton.click();
await page.waitForTimeout(500);
const greenClasses = await heroSection.evaluate((el) => el.className);
// Should have green-specific classes
expect(greenClasses).toMatch(/green/);
expect(greenClasses).not.toMatch(/blue/);
// Click on purple color
const purpleButton = page.locator('button').filter({ hasText: 'purple' });
await purpleButton.click();
await page.waitForTimeout(500);
const purpleClasses = await heroSection.evaluate((el) => el.className);
// Should have purple-specific classes
expect(purpleClasses).toMatch(/purple/);
expect(purpleClasses).not.toMatch(/green/);
});
test('should apply theme-specific classes correctly', async ({ page }) => {
// Get initial theme classes
const body = page.locator('body');
const initialBackground = await body.evaluate((el) => {
const styles = window.getComputedStyle(el);
return styles.backgroundImage;
});
// Click on light theme
const lightThemeButton = page.locator('button').filter({ hasText: 'light' });
await lightThemeButton.click();
await page.waitForTimeout(500);
const lightBackground = await body.evaluate((el) => {
const styles = window.getComputedStyle(el);
return styles.backgroundImage;
});
// Should have light theme classes
expect(lightBackground).not.toBe(initialBackground);
expect(lightBackground).toContain('gradient');
// Click on dark theme
const darkThemeButton = page.locator('button').filter({ hasText: 'dark' });
await darkThemeButton.click();
await page.waitForTimeout(500);
const darkBackground = await body.evaluate((el) => {
const styles = window.getComputedStyle(el);
return styles.backgroundImage;
});
// Should have dark theme classes
expect(darkBackground).not.toBe(lightBackground);
expect(darkBackground).toContain('gradient');
});
test('should validate class generation performance', async ({ page }) => {
// Measure time for theme changes
const startTime = Date.now();
// Perform multiple theme changes
const lightThemeButton = page.locator('button').filter({ hasText: 'light' });
const darkThemeButton = page.locator('button').filter({ hasText: 'dark' });
const defaultThemeButton = page.locator('button').filter({ hasText: 'default' });
await lightThemeButton.click();
await page.waitForTimeout(100);
await darkThemeButton.click();
await page.waitForTimeout(100);
await defaultThemeButton.click();
await page.waitForTimeout(100);
const endTime = Date.now();
const totalTime = endTime - startTime;
// Theme changes should be fast (less than 1 second for all changes)
expect(totalTime).toBeLessThan(1000);
});
test('should handle class conflicts gracefully', async ({ page }) => {
// Check that conflicting classes are handled properly
const heroSection = page.locator('section').first();
// Get all applied classes
const allClasses = await heroSection.evaluate((el) => el.className);
const classArray = allClasses.split(' ');
// Check for common conflicts
const hasConflictingPadding = classArray.some(cls =>
cls.includes('py-') && classArray.some(other =>
other.includes('pt-') || other.includes('pb-')
)
);
// Should not have conflicting padding classes
expect(hasConflictingPadding).toBe(false);
// Check for conflicting margin classes
const hasConflictingMargin = classArray.some(cls =>
cls.includes('my-') && classArray.some(other =>
other.includes('mt-') || other.includes('mb-')
)
);
// Should not have conflicting margin classes
expect(hasConflictingMargin).toBe(false);
});
});