/** * header.steps.ts * Step definitions for unified App Header testing */ import { Given, When, Then, expect } from '../fixtures/bdd-fixtures'; // Header Navigation & Title Steps Then('the app header should be visible', async ({ page }) => { const header = page.locator('header').first(); await expect(header).toBeVisible(); }); Then('the page title should show {string} with icon', async ({ page }, title: string) => { const heading = page.getByRole('heading', { name: new RegExp(title, 'i') }); await expect(heading).toBeVisible(); }); // Header Navigation Links - Navigate to pages When('I click on the {string} link in the header', async ({ page }, linkName: string) => { const pageKey = mapPageName(linkName); const link = page.getByTestId(`header-nav-${pageKey}`); await expect(link).toBeVisible(); await link.click(); await page.waitForLoadState('networkidle').catch(() => {}); }); Then('I should be on the {string} page', async ({ page }, pageName: string) => { const path = mapPagePath(pageName); await expect(page).toHaveURL(new RegExp(path)); }); Then('the {string} link in the header should be highlighted', async ({ page }, linkName: string) => { const pageKey = mapPageName(linkName); const link = page.getByTestId(`header-nav-${pageKey}`); // Check for active state (font-bold and text-gold) const classes = await link.getAttribute('class'); expect(classes).toMatch(/font-bold|text-gold/); }); // Scoring Card Steps Then('the scoring card should be visible', async ({ page }) => { const scoringCard = page.getByTestId('scoring-card'); await expect(scoringCard).toBeVisible(); }); Then('the gold value should be displayed', async ({ page }) => { const goldCard = page.getByTestId('scoring-card-gold'); await expect(goldCard).toBeVisible(); }); Then('the completed quests count should be displayed', async ({ page }) => { const questsCard = page.getByTestId('scoring-card-quests'); await expect(questsCard).toBeVisible(); }); Then('the average savings value should show {string}', async ({ page }, expectedValue: string) => { const savingsCard = page.getByTestId('scoring-card-savings'); await expect(savingsCard).toContainText(expectedValue); }); Then('the average savings percentage should be displayed', async ({ page }) => { const savingsCard = page.getByTestId('scoring-card-savings'); const savingsText = await savingsCard.textContent(); // Should contain a percentage value (e.g., "15.50%") not "—" expect(savingsText).toMatch(/%/); }); // Logout Steps (specific to header) When('I click the {string} button in the header', async ({ page }, buttonName: string) => { const button = page.getByTestId('header-logout-button'); await expect(button).toBeVisible(); await button.click(); // Wait for navigation or redirect await page.waitForTimeout(1000); }); // Then 'I should be logged out' and 'I should be redirected to the login page' already exist in common.steps.ts // Responsive Steps When('I resize the browser to mobile size', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); }); Then('the header should be visible and functional', async ({ page }) => { const header = page.locator('header').first(); await expect(header).toBeVisible(); }); Then('navigation links should be accessible', async ({ page }) => { const viewportWidth = page.viewportSize()?.width ?? 1280; const isMobile = viewportWidth < 768; if (isMobile) { // On mobile the nav is hidden behind the hamburger menu. // Verify the hamburger toggle exists and opens the nav. const hamburger = page.getByTestId('header-mobile-menu-btn'); await expect(hamburger).toBeVisible(); // Open the menu and confirm the nav becomes visible await hamburger.click(); const nav = page.getByTestId('header-navigation'); await expect(nav).toBeVisible(); // Close the menu again to leave the page in a clean state await hamburger.click(); } else { // On desktop the nav is always visible const nav = page.getByTestId('header-navigation'); await expect(nav).toBeVisible(); } }); // Dynamic Update Steps When('I complete a quest from the dashboard', async ({ page }) => { // Find and click a "Complete Quest" button const completeButton = page.getByRole('button', { name: /complete quest/i }).first(); if (await completeButton.isVisible({ timeout: 2000 }).catch(() => false)) { await completeButton.click(); } // Wait for the update await page.waitForTimeout(1000); }); Then('the completed quests count in the header should increase', async ({ page }) => { const questsCard = page.getByTestId('scoring-card-quests'); await expect(questsCard).toBeVisible(); const text = await questsCard.textContent(); expect(text).toBeTruthy(); }); Then('the scoring card should reflect the new count', async ({ page }) => { const scoringCard = page.getByTestId('scoring-card'); await expect(scoringCard).toBeVisible(); }); // Zero Values Step Then('the scoring card should show:', async ({ page }, table: any) => { const rows = table.hashes(); for (const row of rows) { const label = row.field; const value = row.value; if (label === 'gold') { const goldCard = page.getByTestId('scoring-card-gold'); await expect(goldCard).toContainText(value); } else if (label === 'completed quests') { const questsCard = page.getByTestId('scoring-card-quests'); await expect(questsCard).toContainText(value); } else if (label === 'average savings') { const savingsCard = page.getByTestId('scoring-card-savings'); await expect(savingsCard).toContainText(value); } } }); // ===== Helper Functions ===== function mapPageName(pageName: string): string { const mapping: Record = { 'Dashboard': 'dashboard', 'Quests': 'quests', 'Middle-Earth Map': 'map', 'Inventory': 'inventory', }; return mapping[pageName] || pageName.toLowerCase(); } function mapPagePath(pageName: string): string { const mapping: Record = { 'dashboard': '/dashboard', 'quests': '/quests', 'map': '/map', 'inventory': '/inventory', }; return mapping[pageName.toLowerCase()] || `/${pageName.toLowerCase()}`; }