185 lines
6.1 KiB
TypeScript
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()}`;
|
|
}
|