lotr-sut/tests/e2e/steps/header.steps.ts
Fellowship Scholar f6a5823439 init commit
2026-03-29 20:07:56 +00:00

185 lines
6.1 KiB
TypeScript

/**
* 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<string, string> = {
'Dashboard': 'dashboard',
'Quests': 'quests',
'Middle-Earth Map': 'map',
'Inventory': 'inventory',
};
return mapping[pageName] || pageName.toLowerCase();
}
function mapPagePath(pageName: string): string {
const mapping: Record<string, string> = {
'dashboard': '/dashboard',
'quests': '/quests',
'map': '/map',
'inventory': '/inventory',
};
return mapping[pageName.toLowerCase()] || `/${pageName.toLowerCase()}`;
}