import { test, expect } from '@playwright/test'; /** * E2E Critical User Journeys Test Suite * Tests the most important user flows through the application */ test.describe('Critical User Journey: First Time Visitor -> Contact', () => { test('complete journey from homepage to contact submission', async ({ page }) => { // 1. Land on homepage await page.goto('/'); await expect(page).toHaveTitle(/WorkRoot/i); // 2. Verify hero section is visible const hero = page.locator('h1').first(); await expect(hero).toBeVisible(); // 3. Scroll and view services const servicesSection = page.locator('text=/Services|What We Do|Our Services/i').first(); if (await servicesSection.isVisible()) { await servicesSection.scrollIntoViewIfNeeded(); await page.waitForTimeout(500); // Let user "read" } // 4. Navigate to Services page via link const servicesLink = page.locator('a[href="/services"]').first(); await servicesLink.click(); await expect(page).toHaveURL('/services'); // 5. View service details await page.waitForLoadState('networkidle'); const serviceCards = page.locator('article, [class*="service"], [class*="card"]'); expect(await serviceCards.count()).toBeGreaterThan(0); // 6. Click "Contact Us" CTA const contactButton = page.locator('a[href="/contact"]').first(); await contactButton.click(); await expect(page).toHaveURL('/contact'); // 7. Fill out contact form await page.locator('input[name="name"]').fill('John Doe'); await page.locator('#contact-form input[name="email"]').fill('john.doe@example.com'); await page.locator('input[name="phone"]').fill('+1 555-123-4567'); await page.locator('select[name="subject"]').selectOption('web-development'); await page.locator('textarea[name="message"]').fill('I am interested in building a custom web application for my business. Please contact me to discuss requirements.'); // 8. Submit form await page.locator('#contact-form button[type="submit"]').click(); // 9. Verify success message (or rate limit toast - both indicate form was submitted) await page.waitForFunction(() => { const successState = document.getElementById('success-state'); const toastAlert = document.querySelector('#toast-container [role="alert"]'); const successVisible = successState && !successState.classList.contains('hidden'); const toastVisible = toastAlert && (toastAlert as HTMLElement).offsetParent !== null; return successVisible || toastVisible; }, { timeout: 10000 }); }); }); test.describe('Critical User Journey: Technical Reader -> Blog -> Contact', () => { test('user discovers blog content and contacts for services', async ({ page }) => { // 1. Land on homepage await page.goto('/'); // 2. Navigate to blog await page.locator('a[href="/blog"]').first().click(); await expect(page).toHaveURL('/blog'); // 3. Verify blog posts are visible const posts = page.locator('article, [class*="post"]'); expect(await posts.count()).toBeGreaterThan(0); // 4. Click first blog post const firstPostLink = page.locator('article a, [class*="post"] a').first(); await firstPostLink.click(); await expect(page).toHaveURL(/\/blog\//); // 5. Read content (scroll to bottom) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); await page.waitForTimeout(1000); // 6. Navigate to contact from blog post await page.locator('a[href="/contact"]').first().click(); await expect(page).toHaveURL('/contact'); // 7. Submit inquiry await page.locator('input[name="name"]').fill('Jane Smith'); await page.locator('#contact-form input[name="email"]').fill('jane.smith@techcorp.com'); await page.locator('select[name="subject"]').selectOption('consulting'); await page.locator('textarea[name="message"]').fill('Read your blog post and would like to learn more about your consulting services.'); await page.locator('#contact-form button[type="submit"]').click(); await page.waitForFunction(() => { const successState = document.getElementById('success-state'); const toastAlert = document.querySelector('#toast-container [role="alert"]'); const successVisible = successState && !successState.classList.contains('hidden'); const toastVisible = toastAlert && (toastAlert as HTMLElement).offsetParent !== null; return successVisible || toastVisible; }, { timeout: 10000 }); }); }); test.describe('Critical User Journey: Portfolio Exploration', () => { test('user explores portfolio and services', async ({ page }) => { // 1. Direct navigation to portfolio await page.goto('/portfolio'); // 2. Verify portfolio items load const portfolioSection = page.locator('main'); await expect(portfolioSection).toBeVisible(); // 3. Check for portfolio content (may be placeholder or actual projects) const heading = page.locator('h1'); await expect(heading).toBeVisible(); // 4. Navigate to About page await page.locator('a[href="/about"]').first().click(); await expect(page).toHaveURL('/about'); // 5. Verify about content await expect(page.locator('h1')).toBeVisible(); // 6. Final CTA to contact const ctaButton = page.locator('a[href="/contact"]').first(); if (await ctaButton.isVisible()) { await ctaButton.click(); await expect(page).toHaveURL('/contact'); } }); }); test.describe('Critical User Journey: Mobile First-Time Visitor', () => { test.use({ viewport: { width: 375, height: 667 } }); test('mobile user navigates and contacts', async ({ page }) => { // 1. Land on mobile homepage await page.goto('/'); // 2. Open mobile menu const menuButton = page.locator('#mobile-menu-toggle'); if (await menuButton.isVisible()) { await menuButton.click(); await page.waitForTimeout(500); // wait for slide-in animation } // 3. Navigate to services via mobile nav (or direct navigation if menu didn't open) const mobileServicesLink = page.locator('#mobile-menu a[href="/services"]').first(); if (await mobileServicesLink.isVisible()) { await mobileServicesLink.click(); } else { await page.goto('/services'); } await expect(page).toHaveURL('/services'); // 4. Verify mobile responsiveness - no horizontal scroll (allow small overflow margin) const bodyWidth = await page.evaluate(() => document.body.scrollWidth); const viewportWidth = page.viewportSize()?.width || 375; expect(bodyWidth).toBeLessThanOrEqual(viewportWidth + 50); // 50px margin for rounding/scrollbar // 5. Navigate to contact (use direct navigation on mobile since nav links are in hidden desktop menu) await page.goto('/contact'); await expect(page).toHaveURL('/contact'); // 6. Fill mobile form await page.locator('input[name="name"]').fill('Mobile User'); await page.locator('#contact-form input[name="email"]').fill('mobile@example.com'); await page.locator('select[name="subject"]').selectOption('mobile-development'); await page.locator('textarea[name="message"]').fill('Contacting from mobile device.'); // 7. Submit await page.locator('#contact-form button[type="submit"]').click(); await page.waitForFunction(() => { const successState = document.getElementById('success-state'); const toastAlert = document.querySelector('#toast-container [role="alert"]'); const successVisible = successState && !successState.classList.contains('hidden'); const toastVisible = toastAlert && (toastAlert as HTMLElement).offsetParent !== null; return successVisible || toastVisible; }, { timeout: 10000 }); }); }); test.describe('Critical User Journey: Quick Information Seeker', () => { test('user quickly finds information via footer links', async ({ page }) => { // 1. Land on homepage await page.goto('/'); // 2. Scroll to footer await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); // 3. Click Privacy Policy const privacyLink = page.locator('footer a[href="/privacy"]').first(); await privacyLink.click(); await expect(page).toHaveURL('/privacy'); await expect(page.locator('h1')).toBeVisible(); // 4. Navigate to Terms const termsLink = page.locator('footer a[href="/terms"]').first(); await termsLink.click(); await expect(page).toHaveURL('/terms'); await expect(page.locator('h1')).toBeVisible(); // 5. Return home via logo await page.locator('header a').first().click(); await expect(page).toHaveURL('/'); }); }); test.describe('Critical User Journey: Return Visitor - Direct Blog Access', () => { test('returning user accesses specific blog post directly', async ({ page }) => { // 1. Direct access to blog post (simulating bookmark or search result) await page.goto('/blog/getting-started-with-astro'); // 2. Verify content loads await expect(page.locator('h1')).toBeVisible(); await expect(page.locator('article, [class*="prose"]').first()).toBeVisible(); // 3. Verify images load await page.waitForLoadState('networkidle'); const images = page.locator('img'); if (await images.count() > 0) { const firstImage = images.first(); await expect(firstImage).toBeVisible(); } // 4. Navigate to blog listing await page.locator('a[href="/blog"]').first().click(); await expect(page).toHaveURL('/blog'); // 5. Verify other posts are visible const posts = page.locator('article, [class*="post"]'); expect(await posts.count()).toBeGreaterThan(1); }); }); test.describe('Critical User Journey: Security & Trust Verification', () => { test('user checks security and privacy information', async ({ page }) => { // 1. Check homepage security indicators await page.goto('/'); // 2. Verify HTTPS (in real deployment) const url = page.url(); // In dev mode it's http://localhost, but we check structure expect(url).toContain('://'); // 3. Navigate to privacy policy await page.locator('a[href="/privacy"]').first().click(); await expect(page).toHaveURL('/privacy'); // 4. Verify privacy content exists const privacyContent = page.locator('main, article'); await expect(privacyContent).toBeVisible(); const textContent = await privacyContent.textContent(); expect(textContent?.length).toBeGreaterThan(100); // 5. Check terms of service await page.locator('a[href="/terms"]').first().click(); await expect(page).toHaveURL('/terms'); // 6. Verify terms content const termsContent = page.locator('main, article'); await expect(termsContent).toBeVisible(); const termsText = await termsContent.textContent(); expect(termsText?.length).toBeGreaterThan(100); }); }); test.describe('Critical User Journey: Multi-page Session', () => { test('user browses multiple pages in single session', async ({ page }) => { const visitedPages = [ '/', '/about', '/services', '/portfolio', '/blog', '/contact' ]; for (const pagePath of visitedPages) { await page.goto(pagePath); // Verify basic page structure await expect(page.locator('header')).toBeVisible(); await expect(page.locator('main')).toBeVisible(); await expect(page.locator('footer')).toBeVisible(); // Verify no console errors (critical) const consoleErrors: string[] = []; page.on('console', msg => { if (msg.type() === 'error') { consoleErrors.push(msg.text()); } }); // Wait for page to fully load await page.waitForLoadState('networkidle'); // No layout shift (basic check) const bodyWidth = await page.evaluate(() => document.body.scrollWidth); const viewportWidth = page.viewportSize()?.width || 1280; expect(bodyWidth).toBeLessThanOrEqual(viewportWidth + 20); } }); });