diff --git a/.dockerignore b/.dockerignore index e798925f26..86346269ac 100644 --- a/.dockerignore +++ b/.dockerignore @@ -54,7 +54,6 @@ Thumbs.db # Test files and coverage tests/ -!tests/integration/package.json coverage/ .nyc_output/ test-results/ diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 591deab220..fb6ce1d3ff 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,7 +3,6 @@ packages: - 'apps/business-strategy-cms' - 'agents/*' - 'packages/*' - - 'tests/integration' - 'foresight/cli' - 'tools/px' # Committed lockfile is the trusted source of truth; diff --git a/tests/browser-compatibility.spec.ts b/tests/browser-compatibility.spec.ts deleted file mode 100644 index b0fc74d618..0000000000 --- a/tests/browser-compatibility.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { test, expect } from '@playwright/test' - -test.describe('Browser Compatibility Tests', () => { - test('homepage loads correctly', async ({ page }) => { - await page.goto('/') - await expect(page).toHaveTitle(/Pixelated Empathy/) - const heroHeading = page.getByRole('heading', { level: 1 }) - await expect(heroHeading).toBeVisible() - await expect(heroHeading).toContainText(/The Art of Therapy/i) - }) - - test('navigation works across browsers', async ({ page }) => { - await page.goto('/') - - const aboutLink = page.locator('a[href="/about"]').first() - if (await aboutLink.isVisible()) { - await aboutLink.click() - await expect(page).toHaveURL(/.*about/) - } - }) - - test('responsive design works', async ({ page }) => { - await page.goto('/') - - await page.setViewportSize({ width: 375, height: 667 }) - await expect(page.locator('body')).toBeVisible() - - await page.setViewportSize({ width: 1920, height: 1080 }) - await expect(page.locator('body')).toBeVisible() - }) - - test('basic functionality works', async ({ page }) => { - await page.goto('/') - - const buttons = page.locator('button:visible') - const buttonCount = await buttons.count() - - if (buttonCount > 0) { - await expect(buttons.first()).toBeVisible() - } - }) -}) diff --git a/tests/cross-browser/browser-compatibility.spec.ts b/tests/cross-browser/browser-compatibility.spec.ts deleted file mode 100644 index 977e95118b..0000000000 --- a/tests/cross-browser/browser-compatibility.spec.ts +++ /dev/null @@ -1,490 +0,0 @@ -import type { Browser, BrowserContext, Page } from '@playwright/test' -import { devices, expect, test } from '@playwright/test' - -// Define browsers to test -const browsers = ['chromium', 'firefox', 'webkit'] as const - -// Test on different browser engines -browsers.forEach((browserName) => { - test.describe(`Cross-Browser Compatibility - ${browserName}`, () => { - let browser: Browser - let context: BrowserContext - let page: Page - - test.beforeAll(async ({ playwright }) => { - browser = await playwright[browserName].launch() - context = await browser.newContext() - page = await context.newPage() - }) - - test.afterAll(async () => { - await context.close() - await browser.close() - }) - - test.beforeEach(async () => { - await page.goto('/') - await page.waitForLoadState('networkidle') - }) - - test('Basic page functionality works across browsers', async () => { - await test.step('Page loads and renders correctly', async () => { - // Check that main elements are visible - await expect(page.locator('h1')).toBeVisible() - await expect(page.locator('nav')).toBeVisible() - await expect(page.locator('footer')).toBeVisible() - }) - - await test.step('Navigation between tabs works', async () => { - // Test tab navigation - await page.click('a[href="/blog"]') - await expect(page).toHaveURL('/blog') - - await page.click('a[href="/docs"]') - await expect(page).toHaveURL('/docs/getting-started') - - await page.click('a[href="/"]') - await expect(page).toHaveURL('/') - }) - - await test.step('Interactive elements respond correctly', async () => { - // Test buttons - const buttons = page.locator('button') - const buttonCount = await buttons.count() - expect(buttonCount).toBeGreaterThan(0) - - // Test first few buttons - const limit = Math.min(buttonCount, 5) - for (let i = 0; i < limit; i++) { - const button = buttons.nth(i) - if ((await button.isVisible()) && (await button.isEnabled())) { - await button.hover() - // Button should be hoverable without errors - } - } - }) - }) - - test('File upload functionality across browsers', async () => { - await test.step('File input works correctly', async () => { - await page.goto('/contact') - const fileInput = page.locator('input[type="file"]') - await expect(fileInput).toBeVisible() - - // Test file upload - await fileInput.setInputFiles([ - { - name: 'test-file.json', - mimeType: 'application/json', - buffer: Buffer.from('{"test": "data"}'), - }, - ]) - - // Should show uploaded file - await expect(page.locator('text=test-file.json')).toBeVisible({ - timeout: 10000, - }) - }) - }) - - test('Form inputs and validation across browsers', async () => { - await test.step('Text input and validation', async () => { - await page.goto('/contact') - - const textArea = page.locator('textarea[name="message"]') - await expect(textArea).toBeVisible() - - // Test text input - await textArea.fill('Test content for cross-browser validation') - - await page.click('button[type="submit"]') - - // Should show validation results - await expect(page.locator('text=Message sent')).toBeVisible({ - timeout: 5000, - }) - }) - }) - - test('CSS and styling consistency across browsers', async () => { - await test.step('Layout and positioning', async () => { - // Check that key elements are positioned correctly - const mainContent = page.locator('main') - if (await mainContent.isVisible()) { - const boundingBox = await mainContent.boundingBox() - expect(boundingBox?.width).toBeGreaterThan(300) - expect(boundingBox?.height).toBeGreaterThan(200) - } - }) - - await test.step('Colors and visual elements', async () => { - // Check that styled elements have proper colors - const buttons = page.locator('button').first() - if (await buttons.isVisible()) { - const styles = await buttons.evaluate((el) => { - const computed = window.getComputedStyle(el) - return { - backgroundColor: computed.backgroundColor, - color: computed.color, - borderRadius: computed.borderRadius, - } - }) - - // Should have some styling applied - expect(styles.backgroundColor).not.toBe('rgba(0, 0, 0, 0)') - } - }) - - await test.step('Responsive design elements', async () => { - // Test different viewport sizes - await page.setViewportSize({ width: 1200, height: 800 }) - await expect(page.locator('main')).toBeVisible() - - await page.setViewportSize({ width: 768, height: 600 }) - await expect(page.locator('main')).toBeVisible() - - await page.setViewportSize({ width: 375, height: 667 }) - await expect(page.locator('main')).toBeVisible() - }) - }) - - test('JavaScript functionality across browsers', async () => { - await test.step('Event handling works correctly', async () => { - // Test button clicks - const button = page.locator('button').first() - if (await button.isVisible()) { - await button.click() - // Should not cause JavaScript errors - } - }) - - await test.step('Async operations work correctly', async () => { - await page.goto('/blog') - // Should show processing status - await expect(page.locator('h1')).toBeVisible() - }) - }) - - test('Performance across browsers', async () => { - await test.step('Page load performance', async () => { - const startTime = performance.now() - await page.reload() - await page.waitForLoadState('networkidle') - const loadTime = performance.now() - startTime - - console.log(`${browserName} page load time: ${loadTime}ms`) - - // Should load within reasonable time (browser-specific tolerances) - const maxLoadTime = - ( - { webkit: 8000, chromium: 6000, firefox: 6000 } as Record< - string, - number - > - )[browserName] ?? 6000 - expect(loadTime).toBeLessThan(maxLoadTime) - }) - - await test.step('Interaction responsiveness', async () => { - const startTime = performance.now() - - // Perform several interactions - await page.click('a[href="/blog"]') - await page.click('a[href="/docs"]') - await page.click('a[href="/"]') - - const interactionTime = performance.now() - startTime - - console.log(`${browserName} interaction time: ${interactionTime}ms`) - expect(interactionTime).toBeLessThan(3000) - }) - }) - - test('Browser-specific features and limitations', async () => { - await test.step('Local storage and session storage', async () => { - // Test storage capabilities - const storageTest = await page.evaluate(() => { - try { - localStorage.setItem('test', 'value') - const retrieved = localStorage.getItem('test') - localStorage.removeItem('test') - return Object.is(retrieved, 'value') - } catch (_err) { - return false - } - }) - - expect(storageTest).toBe(true) - }) - - await test.step('Modern JavaScript features', async () => { - // Test modern JS features support - const jsFeatures = await page.evaluate(() => { - return { - arrow_functions: true, - async_await: true, - promises: 'Promise' in globalThis, - fetch: 'fetch' in globalThis, - const_let: (() => { - try { - return true - } catch (_err) { - return false - } - })(), - } - }) - - // Modern browsers should support these features - expect(jsFeatures.arrow_functions).toBe(true) - expect(jsFeatures.promises).toBe(true) - - console.log(`${browserName} JS features:`, jsFeatures) - }) - - await test.step('CSS features support', async () => { - // Test CSS features support - const cssFeatures = await page.evaluate(() => { - const testElement = document.createElement('div') - document.body.appendChild(testElement) - - const features = { - flexbox: CSS.supports('display', 'flex'), - grid: CSS.supports('display', 'grid'), - custom_properties: CSS.supports('--custom-property', 'value'), - transforms: CSS.supports('transform', 'translateX(10px)'), - } - - document.body.removeChild(testElement) - return features - }) - - console.log(`${browserName} CSS features:`, cssFeatures) - - // Modern browsers should support flexbox - expect(cssFeatures.flexbox).toBe(true) - }) - }) - - test('Accessibility across browsers', async () => { - await test.step('Focus management', async () => { - // Test focus behavior - await page.keyboard.press('Tab') - const focusedElement = page.locator(':focus') - await expect(focusedElement).toBeVisible() - - // Focus should be visible in all browsers - const focusStyles = await focusedElement.evaluate((el) => { - const computed = window.getComputedStyle(el) - return { - outline: computed.outline, - outlineWidth: computed.outlineWidth, - } - }) - - // Should have some form of focus indicator - const noneOrZero = (v: string) => v === 'none' || v === '0px' - const hasFocusIndicator = !( - noneOrZero(focusStyles.outline) && - noneOrZero(focusStyles.outlineWidth) - ) - - expect(hasFocusIndicator).toBe(true) - }) - - await test.step('ARIA support', async () => { - // Test ARIA attribute support - const ariaSupport = await page.evaluate(() => { - const testElement = document.createElement('div') - testElement.setAttribute('aria-label', 'test') - testElement.setAttribute('role', 'button') - - return { - ariaLabel: testElement.matches('[aria-label="test"]'), - role: testElement.matches('[role="button"]'), - } - }) - - expect(ariaSupport.ariaLabel).toBe(true) - expect(ariaSupport.role).toBe(true) - }) - }) - }) -}) - -// Test specific browser combinations and edge cases -test.describe('Browser-Specific Edge Cases', () => { - test('Safari-specific tests', async ({ playwright }) => { - const browser = await playwright.webkit.launch() - const context = await browser.newContext() - const page = await context.newPage() - - await page.goto('/') - await page.waitForLoadState('networkidle') - - await test.step('Safari file upload behavior', async () => { - await page.goto('/contact') - - // Safari has specific file upload behaviors - const fileInput = page.locator('input[type="file"]') - await fileInput.setInputFiles([ - { - name: 'safari-test.json', - mimeType: 'application/json', - buffer: Buffer.from('{"safari": "test"}'), - }, - ]) - - await expect(page.locator('text=safari-test.json')).toBeVisible() - }) - - await test.step('Safari CSS and layout', async () => { - // Test Safari-specific CSS behaviors - const elements = page.locator('button, input, textarea') - const elementCount = await elements.count() - - // Check that elements render properly in Safari - const limit = Math.min(elementCount, 5) - for (let i = 0; i < limit; i++) { - const element = elements.nth(i) - if (await element.isVisible()) { - const boundingBox = await element.boundingBox() - expect((boundingBox?.width ?? 0) > 0).toBe(true) - expect((boundingBox?.height ?? 0) > 0).toBe(true) - } - } - }) - - await browser.close() - }) - - test('Firefox-specific tests', async ({ playwright }) => { - const browser = await playwright.firefox.launch() - const context = await browser.newContext() - const page = await context.newPage() - - await page.goto('/') - await page.waitForLoadState('networkidle') - - await test.step('Firefox event handling', async () => { - await page.goto('/contact') - - // Firefox has specific event handling behaviors - const textArea = page.locator('textarea[name="message"]') - await textArea.fill('Firefox-specific test content') - - await page.click('button[type="submit"]') - - await expect(page.locator('text=Message sent')).toBeVisible() - }) - - await test.step('Firefox CSS rendering', async () => { - // Test Firefox-specific rendering - const mainContent = page.locator('main') - if (await mainContent.isVisible()) { - const styles = await mainContent.evaluate((el) => { - const computed = window.getComputedStyle(el) - return { - display: computed.display, - position: computed.position, - } - }) - - expect(Boolean(styles.display)).toBe(true) - } - }) - - await browser.close() - }) - - test('Chrome/Chromium-specific tests', async ({ playwright }) => { - const browser = await playwright.chromium.launch() - const context = await browser.newContext() - const page = await context.newPage() - - await page.goto('/') - await page.waitForLoadState('networkidle') - - await test.step('Chrome performance features', async () => { - // Test Chrome-specific performance APIs - const performanceSupport = await page.evaluate(() => { - return { - performance: !!performance, - memory: !!(performance as any).memory, - navigation: !!performance.navigation, - } - }) - - expect(performanceSupport.performance).toBe(true) - console.log('Chrome performance features:', performanceSupport) - }) - - await browser.close() - }) -}) - -// Test older browser compatibility (if needed) -test.describe('Legacy Browser Support', () => { - test('Graceful degradation', async ({ page }) => { - await page.goto('/') - - // Test that the page works even with limited JavaScript - await test.step('Basic functionality without modern features', async () => { - // Disable some modern features - await page.addInitScript(() => { - // Simulate older browser by removing some modern APIs - delete (window as any).fetch - delete (window as any).Promise - }) - - await page.reload() - await page.waitForLoadState('networkidle') - - // Basic navigation should still work - await expect(page.locator('h1')).toBeVisible() - }) - }) -}) - -// Test browser-specific device combinations -test.describe('Browser-Device Combinations', () => { - const deviceBrowserCombinations = [ - { - deviceName: 'iPhone 12', - device: devices['iPhone 12'], - browser: 'webkit' as const, - }, - { - deviceName: 'Pixel 5', - device: devices['Pixel 5'], - browser: 'chromium' as const, - }, - { - deviceName: 'iPad Pro', - device: devices['iPad Pro'], - browser: 'webkit' as const, - }, - ] - - deviceBrowserCombinations.forEach(({ deviceName, device, browser }) => { - test(`${deviceName} with ${browser}`, async ({ playwright }) => { - const browserInstance = await playwright[browser].launch() - const context = await browserInstance.newContext({ - ...device, - }) - const page = await context.newPage() - - await page.goto('/') - await page.waitForLoadState('networkidle') - - // Test basic functionality on device-browser combination - await expect(page.locator('h1')).toBeVisible() - - // Test touch interactions - await page.click('a[href="/blog"]') - await expect(page).toHaveURL('/blog') - - await browserInstance.close() - }) - }) -}) diff --git a/tests/e2e-config.ts b/tests/e2e-config.ts deleted file mode 100644 index 0c5e547600..0000000000 --- a/tests/e2e-config.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { defineConfig } from '@playwright/test' -const isCi = !!process.env['CI'] -import { dirname, resolve } from 'path' -import { fileURLToPath } from 'url' - -// Get current directory -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) - -/** - * Configuration for End-to-End Tests - * - * This configuration extends the base Playwright config to focus specifically - * on critical user journey tests. - */ - -export default defineConfig({ - // Use this configuration in addition to playwright.config.ts - // by running: pnpm dlx playwright test --config=tests/e2e-config.ts - - testDir: resolve(__dirname, './e2e'), - - // Longer timeout for E2E tests that involve multiple pages and complex flows - timeout: 60000, - - // Slow down tests for better visibility in debugging - use: { - actionTimeout: 10000, - navigationTimeout: 30000, - video: 'on', - trace: 'on', - baseURL: 'http://localhost:3000', - }, - - // Limit to smaller set of test browsers for faster iteration - projects: [ - { - name: 'chromium', - use: { - browserName: 'chromium', - }, - }, - { - name: 'Mobile Chrome', - use: { - browserName: 'chromium', - viewport: { width: 390, height: 844 }, - deviceScaleFactor: 2, - isMobile: true, - }, - }, - ], - - // More detailed reporting for critical user journeys - reporter: [ - [ - 'html', - { - open: 'on-failure', - outputFolder: resolve(__dirname, '../test-results/e2e-report'), - }, - ], - [ - 'json', - { outputFile: resolve(__dirname, '../test-results/e2e-results.json') }, - ], - ], - - // Run your local dev server before starting tests - webServer: isCi - ? { - command: 'pnpm run build && pnpm run preview -- --port 3000', - url: 'http://localhost:3000', - reuseExistingServer: false, - timeout: 10 * 60 * 1000, - } - : { - command: 'pnpm run dev', - port: 3000, - reuseExistingServer: true, - timeout: 60000, - }, -}) diff --git a/tests/e2e/README.md b/tests/e2e/README.md index a3fca4de20..3527f43b61 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -43,14 +43,9 @@ pnpm test:e2e:report ## Configuration -The tests use a dedicated configuration file at `tests/e2e-config.ts`, which -extends the base Playwright configuration with settings specific to end-to-end -testing: - -- Increased timeouts for complex flows -- Video recording for better debugging -- Focused browser selection for faster iteration -- Detailed HTML and JSON reports +E2E tests use the shared Playwright configuration at `config/playwright.config.ts` +(`testDir: tests/`). Use the `pnpm test:e2e*` scripts above rather than ad-hoc +config files under `tests/`. ## Test Coverage @@ -109,70 +104,3 @@ If tests become flaky, consider: Some tests include visual comparison using Playwright's screenshot comparison capabilities. The baseline screenshots are stored in the repository and compared during test execution. - -# MCP Integration with Playwright - -This directory contains end-to-end tests that utilize the Model Context Protocol -(MCP) server integration with Playwright. - -## Setup and Configuration - -The MCP integration is configured in `tests/mcp-e2e-config.ts`. This -configuration: - -- Connects to an MCP server running at `http://localhost:8033` -- Configures test reporters and output formats -- Sets up browsers and devices for testing -- Manages the web server for testing - -## Running MCP Tests - -To run the MCP-integrated tests: - -```bash -# First, ensure you've connected to the MCP server through Cursor -# Then run tests with the MCP configuration -pnpm test:e2e:mcp -``` - -## Available Test Features - -The MCP integration enables several advanced testing capabilities: - -1. **Browser Tools Integration**: Access browser console logs, network requests, - screenshots, and more through the MCP server -2. **Accessibility Testing**: Run automated accessibility audits on your pages -3. **Performance Testing**: Generate performance reports for your application -4. **SEO Auditing**: Check SEO best practices compliance -5. **Network Analysis**: Monitor and analyze network requests and responses - -## Example Tests - -See `mcp-example.spec.ts` for a basic example of how to integrate with the MCP -server in your tests. - -## Adding Your Own MCP Tests - -When creating new MCP-integrated tests: - -1. Create a new `.spec.ts` file in this directory -2. Import from `@playwright/test` as usual -3. Use the standard Playwright test API -4. Leverage MCP-specific features through annotations or API calls -5. Run using the MCP configuration - -## Troubleshooting - -If you encounter issues with the MCP integration: - -1. Ensure the MCP server is running and accessible -2. Verify your connection to the MCP server through Cursor -3. Check that the configuration points to the correct MCP server URL -4. Review browser console logs for any connection errors -5. Make sure all required dependencies are installed - -## Resources - -- [MCP Documentation](https://example.com/mcp-docs) -- [Playwright Documentation](https://playwright.dev/docs/intro) -- [Astro Testing Guide](https://docs.astro.build/en/guides/testing/) diff --git a/tests/e2e/mobile-responsiveness.spec.ts b/tests/e2e/mobile-responsiveness.spec.ts deleted file mode 100644 index 698bb228bb..0000000000 --- a/tests/e2e/mobile-responsiveness.spec.ts +++ /dev/null @@ -1,379 +0,0 @@ -import { test, expect, devices } from '@playwright/test' - -// Test on different mobile devices -const mobileDevices = [ - devices['iPhone 12'], - devices['iPhone 12 Pro'], - devices['Pixel 5'], - devices['Galaxy S21'], -] - -mobileDevices.forEach( - (device: { name?: string; viewport?: { width: number; height: number } }) => { - test.describe(`Mobile Responsiveness - ${device.name ?? 'unknown'}`, () => { - test.use({ ...(device as any) }) - - test.beforeEach(async ({ page }) => { - await page.goto('/demo') - await page.waitForLoadState('networkidle') - }) - - test('Demo components are responsive on mobile', async ({ page }) => { - // Test viewport dimensions - const viewport = page.viewportSize() - expect(viewport?.width).toBeLessThanOrEqual(device.viewport!.width) - - // Test navigation menu on mobile - await test.step('Mobile Navigation', async () => { - // Check if mobile menu button exists - const mobileMenuButton = page.locator( - '[data-testid="mobile-menu-button"]', - ) - if (await mobileMenuButton.isVisible()) { - await mobileMenuButton.click() - await expect( - page.locator('[data-testid="mobile-menu"]'), - ).toBeVisible() - } - }) - - // Test data ingestion on mobile - await test.step('Data Ingestion Mobile UI', async () => { - await page.click('[data-testid="data-ingestion-tab"]') - - // Check upload area is properly sized - const uploadArea = page.locator('[data-testid="upload-area"]') - await expect(uploadArea).toBeVisible() - - const uploadBox = await uploadArea.boundingBox() - expect(uploadBox?.width).toBeLessThanOrEqual(viewport?.width ?? 0) - - // Test file format cards stack properly on mobile - const formatCards = page.locator('[data-testid="format-card"]') - const cardCount = await formatCards.count() - - if (cardCount > 0) { - // On mobile, cards should stack vertically - const firstCard = await formatCards.first().boundingBox() - const secondCard = await formatCards.nth(1).boundingBox() - - if (firstCard && secondCard) { - // Second card should be below first card on mobile - expect(secondCard.y).toBeGreaterThan( - firstCard.y + firstCard.height - 10, - ) - } - } - }) - - // Test validation interface on mobile - await test.step('Validation Mobile UI', async () => { - await page.click('[data-testid="validation-tab"]') - - // Check text area is properly sized - const textArea = page.locator( - '[placeholder*="Enter psychology content"]', - ) - await expect(textArea).toBeVisible() - - const textAreaBox = await textArea.boundingBox() - expect(textAreaBox?.width).toBeLessThanOrEqual( - (viewport?.width ?? 0) - 40, - ) // Account for padding - - // Test validation results layout - await textArea.fill('Test content for mobile validation') - await expect(page.locator('text=Validation Results')).toBeVisible() - - // Check validation cards are stacked on mobile - const validationCards = page.locator( - '[data-testid="validation-card"]', - ) - const validationCardCount = await validationCards.count() - - if (validationCardCount > 1) { - const firstValidationCard = await validationCards - .first() - .boundingBox() - const secondValidationCard = await validationCards - .nth(1) - .boundingBox() - - if (firstValidationCard && secondValidationCard) { - expect(secondValidationCard.y).toBeGreaterThan( - firstValidationCard.y, - ) - } - } - }) - - // Test category balancing on mobile - await test.step('Category Balancing Mobile UI', async () => { - await page.click('[data-testid="category-balancing-tab"]') - - // Check category cards layout on mobile - const categoryCards = page.locator('[data-testid="category-card"]') - const categoryCount = await categoryCards.count() - - if (categoryCount > 0) { - // Categories should stack on mobile - for (let i = 0; i < Math.min(categoryCount, 3); i++) { - const card = categoryCards.nth(i) - await expect(card).toBeVisible() - - const cardBox = await card.boundingBox() - expect(cardBox?.width).toBeLessThanOrEqual( - (viewport?.width ?? 0) - 20, - ) - } - } - - // Test sliders work on mobile - const sliders = page.locator('[role="slider"]') - const sliderCount = await sliders.count() - - if (sliderCount > 0) { - const firstSlider = sliders.first() - await expect(firstSlider).toBeVisible() - - // Test touch interaction - await firstSlider.tap() - } - }) - - // Test export interface on mobile - await test.step('Export Mobile UI', async () => { - await page.click('[data-testid="export-tab"]') - - // Check export format selection on mobile - const formatOptions = page.locator('[data-testid="format-option"]') - const formatCount = await formatOptions.count() - - if (formatCount > 0) { - // Format options should be properly sized for mobile - for (let i = 0; i < Math.min(formatCount, 3); i++) { - const option = formatOptions.nth(i) - const optionBox = await option.boundingBox() - expect(optionBox?.width).toBeLessThanOrEqual( - (viewport?.width ?? 0) - 20, - ) - } - } - - // Test export buttons are accessible on mobile - const exportButtons = page.locator('button:has-text("Export")') - const buttonCount = await exportButtons.count() - - if (buttonCount > 0) { - const firstButton = exportButtons.first() - await expect(firstButton).toBeVisible() - - const buttonBox = await firstButton.boundingBox() - expect(buttonBox?.height).toBeGreaterThanOrEqual(44) // Minimum touch target size - } - }) - }) - - test('Touch interactions work properly', async ({ page }) => { - // Test touch scrolling - await test.step('Touch Scrolling', async () => { - await page.click('[data-testid="category-balancing-tab"]') - - // Scroll down using touch - await page.touchscreen.tap(200, 300) - await page.mouse.wheel(0, 500) - - // Verify content is still accessible - await expect(page.locator('text=Category Balancing')).toBeVisible() - }) - - // Test touch gestures on interactive elements - await test.step('Touch Gestures', async () => { - await page.click('[data-testid="validation-tab"]') - - const textArea = page.locator( - '[placeholder*="Enter psychology content"]', - ) - - // Test tap to focus - await textArea.tap() - await expect(textArea).toBeFocused() - - // Test touch typing - await page.keyboard.type('Mobile touch input test') - await expect(textArea).toHaveValue('Mobile touch input test') - }) - - // Test swipe gestures if applicable - await test.step('Swipe Gestures', async () => { - // Test horizontal swipe on tabs if implemented - const tabContainer = page.locator('[data-testid="tab-container"]') - if (await tabContainer.isVisible()) { - const containerBox = await tabContainer.boundingBox() - if (containerBox) { - // Swipe left - await page.touchscreen.tap( - containerBox.x + containerBox.width - 50, - containerBox.y + containerBox.height / 2, - ) - await page.mouse.move( - containerBox.x + 50, - containerBox.y + containerBox.height / 2, - ) - } - } - }) - }) - - test('Mobile-specific features work correctly', async ({ page }) => { - // Test orientation changes - await test.step('Orientation Changes', async () => { - // Test portrait mode (default) - await expect( - page.locator('[data-testid="main-content"]'), - ).toBeVisible() - - // Switch to landscape mode - await page.setViewportSize({ - width: device.viewport!.height, - height: device.viewport!.width, - }) - - // Verify layout adapts to landscape - await expect( - page.locator('[data-testid="main-content"]'), - ).toBeVisible() - - // Switch back to portrait - await page.setViewportSize(device.viewport!) - }) - - // Test mobile-specific UI elements - await test.step('Mobile UI Elements', async () => { - // Check for mobile-optimized buttons - const buttons = page.locator('button') - const buttonCount = await buttons.count() - - for (let i = 0; i < Math.min(buttonCount, 5); i++) { - const button = buttons.nth(i) - if (await button.isVisible()) { - const buttonBox = await button.boundingBox() - // Buttons should meet minimum touch target size (44px) - expect(buttonBox?.height).toBeGreaterThanOrEqual(40) - } - } - }) - - // Test mobile performance - await test.step('Mobile Performance', async () => { - const startTime = Date.now() - - // Navigate between tabs - await page.click('[data-testid="data-ingestion-tab"]') - await page.click('[data-testid="validation-tab"]') - await page.click('[data-testid="category-balancing-tab"]') - - const navigationTime = Date.now() - startTime - - // Navigation should be responsive on mobile (under 3 seconds) - expect(navigationTime).toBeLessThan(3000) - }) - }) - - test('Mobile accessibility features', async ({ page }) => { - // Test screen reader compatibility on mobile - await test.step('Mobile Screen Reader', async () => { - // Check for proper ARIA labels - const ariaElements = page.locator('[aria-label]') - const ariaCount = await ariaElements.count() - expect(ariaCount).toBeGreaterThan(0) - - // Check for proper heading structure - const headings = page.locator('h1, h2, h3, h4, h5, h6') - const headingCount = await headings.count() - expect(headingCount).toBeGreaterThan(0) - }) - - // Test focus management on mobile - await test.step('Mobile Focus Management', async () => { - // Test tab navigation - await page.keyboard.press('Tab') - const focusedElement = page.locator(':focus') - await expect(focusedElement).toBeVisible() - - // Verify focus is visible and properly sized - const focusBox = await focusedElement.boundingBox() - if (focusBox) { - expect(focusBox.height).toBeGreaterThanOrEqual(44) // Minimum touch target - } - }) - - // Test color contrast on mobile - await test.step('Mobile Color Contrast', async () => { - // Check that text is readable on mobile - const textElements = page - .locator('p, span, div') - .filter({ hasText: /\w+/ }) - const textCount = await textElements.count() - - // Verify text elements are visible (basic contrast check) - for (let i = 0; i < Math.min(textCount, 10); i++) { - const textElement = textElements.nth(i) - if (await textElement.isVisible()) { - await expect(textElement).toBeVisible() - } - } - }) - }) - }) - }, -) - -// Test tablet responsiveness -test.describe('Tablet Responsiveness', () => { - test.use({ ...devices['iPad Pro'] }) - - test('Demo works properly on tablet', async ({ page }) => { - await page.goto('/demo') - await page.waitForLoadState('networkidle') - - // Test tablet-specific layout - await test.step('Tablet Layout', async () => { - const viewport = page.viewportSize() - expect(viewport?.width).toBe(1024) - expect(viewport?.height).toBe(1366) - - // Check that components use tablet layout (between mobile and desktop) - await page.click('[data-testid="category-balancing-tab"]') - - const categoryCards = page.locator('[data-testid="category-card"]') - const cardCount = await categoryCards.count() - - if (cardCount >= 2) { - const firstCard = await categoryCards.first().boundingBox() - const secondCard = await categoryCards.nth(1).boundingBox() - - if (firstCard && secondCard) { - // On tablet, cards might be side by side or stacked depending on design - const isHorizontal = Math.abs(firstCard.y - secondCard.y) < 50 - const isVertical = secondCard.y > firstCard.y + firstCard.height - 50 - - expect(isHorizontal || isVertical).toBe(true) - } - } - }) - - // Test tablet touch interactions - await test.step('Tablet Touch Interactions', async () => { - await page.click('[data-testid="validation-tab"]') - - const textArea = page.locator('[placeholder*="Enter psychology content"]') - await textArea.tap() - await page.keyboard.type( - 'Tablet input test with longer content to verify text area sizing and functionality on tablet devices.', - ) - - await expect(textArea).toHaveValue(/Tablet input test/) - }) - }) -}) diff --git a/tests/home-hero-layout.mjs b/tests/home-hero-layout.mjs deleted file mode 100644 index ac05645b64..0000000000 --- a/tests/home-hero-layout.mjs +++ /dev/null @@ -1,81 +0,0 @@ -import assert from 'node:assert/strict' -import { readFile } from 'node:fs/promises' - -import { chromium } from '@playwright/test' - -const source = await readFile('apps/web/src/pages/index.astro', 'utf8') -const workstation = await readFile( - 'apps/web/src/components/landing/SimulationWorkstation.astro', - 'utf8', -) -const font = await readFile('apps/web/src/assets/fonts/monocraft/Monocraft.ttf') -const css = ( - await readFile('apps/web/src/styles/np-landing.css', 'utf8') -).replace( - '../assets/fonts/monocraft/Monocraft.ttf', - `data:font/ttf;base64,${font.toString('base64')}`, -) -const hero = source - .slice(source.indexOf('') + 10) - .replace( - '', - workstation.split('---')[2].split('