Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped
281 lines
10 KiB
TypeScript
281 lines
10 KiB
TypeScript
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('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('button[type="submit"]').click();
|
|
|
|
// 9. Verify success message
|
|
await expect(page.locator('text=/sent successfully|thank you/i')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
});
|
|
|
|
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('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('button[type="submit"]').click();
|
|
await expect(page.locator('text=/sent successfully/i')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
});
|
|
|
|
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('button[aria-label*="menu"], button[aria-expanded]').first();
|
|
if (await menuButton.isVisible()) {
|
|
await menuButton.click();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
|
|
// 3. Navigate to services
|
|
await page.locator('a[href="/services"]').first().click();
|
|
await expect(page).toHaveURL('/services');
|
|
|
|
// 4. Verify mobile responsiveness - no horizontal scroll
|
|
const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
|
|
expect(bodyWidth).toBeLessThanOrEqual(395);
|
|
|
|
// 5. Navigate to contact
|
|
await page.locator('a[href="/contact"]').first().click();
|
|
await expect(page).toHaveURL('/contact');
|
|
|
|
// 6. Fill mobile form
|
|
await page.locator('input[name="name"]').fill('Mobile User');
|
|
await page.locator('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('button[type="submit"]').click();
|
|
await expect(page.locator('text=/sent successfully/i')).toBeVisible({ timeout: 5000 });
|
|
});
|
|
});
|
|
|
|
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"]')).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);
|
|
}
|
|
});
|
|
});
|