Added new systems.
440
frontend/tests/rewards-comprehensive.spec.ts
Normal file
@ -0,0 +1,440 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
// Test data
|
||||
const TEST_USER = {
|
||||
email: 'alice@example.com',
|
||||
password: 'password123',
|
||||
};
|
||||
|
||||
// Helper to capture console errors
|
||||
function setupErrorCapture(page: Page): string[] {
|
||||
const errors: string[] = [];
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
errors.push(msg.text());
|
||||
}
|
||||
});
|
||||
page.on('pageerror', err => {
|
||||
errors.push(err.message);
|
||||
});
|
||||
return errors;
|
||||
}
|
||||
|
||||
// Helper to filter out expected API errors (backend may not be running)
|
||||
function filterCriticalErrors(errors: string[]): string[] {
|
||||
return errors.filter(e =>
|
||||
!e.includes('Failed to load resource') &&
|
||||
!e.includes('ERR_CONNECTION_REFUSED') &&
|
||||
!e.includes('NetworkError') &&
|
||||
!e.includes('net::ERR')
|
||||
);
|
||||
}
|
||||
|
||||
test.describe('Home Page Loading', () => {
|
||||
test('should load home page without getting stuck in spinner', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Wait for initial content
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Check H2H branding is visible
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Page should show content, not just a spinner
|
||||
// Either shows loading state briefly or actual content
|
||||
const hasContent = await page.locator('main').isVisible() ||
|
||||
await page.locator('[class*="container"]').isVisible();
|
||||
expect(hasContent).toBe(true);
|
||||
|
||||
// Check for critical errors (not API errors from missing backend)
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should show H2H header branding', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should have navigation links including Rewards', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
// Check Rewards navigation exists (dropdown button)
|
||||
await expect(page.locator('nav button:has-text("Rewards"), nav a:has-text("Rewards")').first()).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Rewards Pages Navigation', () => {
|
||||
test('should navigate to /rewards overview page', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Rewards")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Check for no critical errors
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should navigate to /rewards/leaderboard page', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards/leaderboard', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Leaderboard")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should navigate to /rewards/achievements page', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards/achievements', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Achievements")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should navigate to /rewards/loot-boxes page', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards/loot-boxes', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Loot Boxes")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should navigate to /rewards/activity page', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards/activity', { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Activity")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Header Rewards Dropdown', () => {
|
||||
test('should show Rewards dropdown button in header', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Find the Rewards dropdown button
|
||||
const rewardsButton = page.locator('nav button:has-text("Rewards")').first();
|
||||
await expect(rewardsButton).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should open dropdown menu when clicking Rewards', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Click Rewards dropdown
|
||||
const rewardsButton = page.locator('nav button:has-text("Rewards")').first();
|
||||
await rewardsButton.click();
|
||||
|
||||
// Check dropdown menu appears with all options
|
||||
await expect(page.locator('a[href="/rewards"]')).toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('a[href="/rewards/leaderboard"]')).toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('a[href="/rewards/achievements"]')).toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('a[href="/rewards/loot-boxes"]')).toBeVisible({ timeout: 3000 });
|
||||
await expect(page.locator('a[href="/rewards/activity"]')).toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('should navigate to rewards overview from dropdown', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Click Rewards dropdown
|
||||
await page.locator('nav button:has-text("Rewards")').first().click();
|
||||
|
||||
// Click Overview link
|
||||
await page.locator('a[href="/rewards"]:has-text("Overview")').click();
|
||||
|
||||
// Verify navigation
|
||||
await expect(page).toHaveURL('/rewards');
|
||||
await expect(page.locator('h1:has-text("Rewards")')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should navigate to leaderboard from dropdown', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
await page.locator('nav button:has-text("Rewards")').first().click();
|
||||
await page.locator('a[href="/rewards/leaderboard"]').click();
|
||||
|
||||
await expect(page).toHaveURL('/rewards/leaderboard');
|
||||
await expect(page.locator('h1:has-text("Leaderboard")')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should close dropdown when clicking outside', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Open dropdown
|
||||
await page.locator('nav button:has-text("Rewards")').first().click();
|
||||
await expect(page.locator('a[href="/rewards/leaderboard"]')).toBeVisible();
|
||||
|
||||
// Click outside
|
||||
await page.locator('header h1:has-text("H2H")').click();
|
||||
|
||||
// Dropdown should close (link should not be visible in dropdown context)
|
||||
await page.waitForTimeout(500);
|
||||
// The dropdown should be hidden
|
||||
const dropdown = page.locator('div.absolute a[href="/rewards/leaderboard"]');
|
||||
await expect(dropdown).not.toBeVisible({ timeout: 2000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Theme Consistency - Light Theme', () => {
|
||||
test('rewards overview page should have light theme with white backgrounds', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for Header component
|
||||
await expect(page.locator('header')).toBeVisible();
|
||||
|
||||
// Check header has white/light background
|
||||
const header = page.locator('header');
|
||||
await expect(header).toHaveCSS('background-color', 'rgb(255, 255, 255)');
|
||||
|
||||
// Check page background is light (gray-50 = rgb(249, 250, 251))
|
||||
const body = page.locator('div.min-h-screen');
|
||||
const bgColor = await body.evaluate((el) => window.getComputedStyle(el).backgroundColor);
|
||||
expect(['rgb(249, 250, 251)', 'rgb(248, 250, 252)']).toContain(bgColor);
|
||||
});
|
||||
|
||||
test('leaderboard page should have light theme', async ({ page }) => {
|
||||
await page.goto('/rewards/leaderboard');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for Header component
|
||||
await expect(page.locator('header')).toBeVisible();
|
||||
|
||||
// Page title should be dark text
|
||||
const title = page.locator('h1:has-text("Leaderboard")');
|
||||
await expect(title).toHaveCSS('color', 'rgb(17, 24, 39)'); // gray-900
|
||||
});
|
||||
|
||||
test('achievements page should have light theme', async ({ page }) => {
|
||||
await page.goto('/rewards/achievements');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(page.locator('header')).toBeVisible();
|
||||
const title = page.locator('h1:has-text("Achievements")');
|
||||
await expect(title).toHaveCSS('color', 'rgb(17, 24, 39)');
|
||||
});
|
||||
|
||||
test('loot boxes page should have light theme', async ({ page }) => {
|
||||
await page.goto('/rewards/loot-boxes');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(page.locator('header')).toBeVisible();
|
||||
const title = page.locator('h1:has-text("Loot Boxes")');
|
||||
await expect(title).toHaveCSS('color', 'rgb(17, 24, 39)');
|
||||
});
|
||||
|
||||
test('activity page should have light theme', async ({ page }) => {
|
||||
await page.goto('/rewards/activity');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(page.locator('header')).toBeVisible();
|
||||
const title = page.locator('h1:has-text("Activity")');
|
||||
await expect(title).toHaveCSS('color', 'rgb(17, 24, 39)');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Authentication Gates', () => {
|
||||
test('achievements page should show "Sign In Required" when not logged in', async ({ page }) => {
|
||||
await page.goto('/rewards/achievements');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should show sign in required message
|
||||
await expect(page.locator('text=Sign In Required')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Should have sign in button
|
||||
await expect(page.locator('a[href="/login"]:has-text("Sign In")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('loot boxes page should show "Sign In Required" when not logged in', async ({ page }) => {
|
||||
await page.goto('/rewards/loot-boxes');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should show sign in required message
|
||||
await expect(page.locator('text=Sign In Required')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Should have sign in button
|
||||
await expect(page.locator('a[href="/login"]:has-text("Sign In")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('rewards overview should show "Sign In to Track Progress" when not logged in', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should show sign in prompt
|
||||
await expect(page.locator('text=Sign In to Track Progress')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('leaderboard page should be accessible without auth', async ({ page }) => {
|
||||
await page.goto('/rewards/leaderboard');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should NOT show sign in required
|
||||
await expect(page.locator('text=Sign In Required')).not.toBeVisible();
|
||||
|
||||
// Should show leaderboard content area
|
||||
await expect(page.locator('h1:has-text("Leaderboard")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('activity page should be accessible without auth', async ({ page }) => {
|
||||
await page.goto('/rewards/activity');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should NOT show sign in required
|
||||
await expect(page.locator('text=Sign In Required')).not.toBeVisible();
|
||||
|
||||
// Should show activity page
|
||||
await expect(page.locator('h1:has-text("Activity")')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Gamification Component Rendering', () => {
|
||||
test('Leaderboard component should render on rewards page', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Should have Leaderboard section with trophy icon/heading
|
||||
await expect(page.locator('h3:has-text("Leaderboard")').first()).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('Leaderboard component should have category tabs', async ({ page }) => {
|
||||
await page.goto('/rewards/leaderboard');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for category buttons
|
||||
await expect(page.locator('button:has-text("Top Earners")').first()).toBeVisible({ timeout: 5000 });
|
||||
await expect(page.locator('button:has-text("High Rollers")').first()).toBeVisible();
|
||||
await expect(page.locator('button:has-text("Most Wins")').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('WhaleTracker component should render on rewards page', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// WhaleTracker should be visible (may have whale icon or "Whale" text)
|
||||
const whaleSection = page.locator('text=/whale/i').first();
|
||||
const isVisible = await whaleSection.isVisible().catch(() => false);
|
||||
|
||||
// May show loading or content - just verify the section exists
|
||||
expect(isVisible || await page.locator('.bg-white.rounded-xl').count() > 0).toBe(true);
|
||||
});
|
||||
|
||||
test('rewards overview should have quick links section', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Quick links to sub-pages
|
||||
await expect(page.locator('a[href="/rewards/leaderboard"]').first()).toBeVisible({ timeout: 5000 });
|
||||
await expect(page.locator('a[href="/rewards/achievements"]').first()).toBeVisible();
|
||||
await expect(page.locator('a[href="/rewards/loot-boxes"]').first()).toBeVisible();
|
||||
await expect(page.locator('a[href="/rewards/activity"]').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('rewards overview should have "How Tiers Work" section', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(page.locator('text=How Tiers Work')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('activity page should render ActivityFeed component', async ({ page }) => {
|
||||
await page.goto('/rewards/activity');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Activity page should show feed area (may be empty or loading)
|
||||
// The page should render without errors
|
||||
await expect(page.locator('h1:has-text("Activity")')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Sub-Navigation on Rewards Pages', () => {
|
||||
test('rewards pages should have sub-navigation tabs', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for sub-nav with all links
|
||||
const subNav = page.locator('nav a[href="/rewards"]');
|
||||
await expect(subNav.first()).toBeVisible({ timeout: 5000 });
|
||||
|
||||
await expect(page.locator('nav a[href="/rewards/leaderboard"]').first()).toBeVisible();
|
||||
await expect(page.locator('nav a[href="/rewards/achievements"]').first()).toBeVisible();
|
||||
await expect(page.locator('nav a[href="/rewards/loot-boxes"]').first()).toBeVisible();
|
||||
await expect(page.locator('nav a[href="/rewards/activity"]').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('sub-navigation should highlight active page', async ({ page }) => {
|
||||
await page.goto('/rewards/leaderboard');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// The Leaderboard tab should have the active styling (border-primary)
|
||||
const leaderboardTab = page.locator('nav a[href="/rewards/leaderboard"]').first();
|
||||
await expect(leaderboardTab).toHaveClass(/border-primary|text-primary/);
|
||||
});
|
||||
|
||||
test('clicking sub-nav should navigate between pages', async ({ page }) => {
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Click Achievements in sub-nav
|
||||
await page.locator('nav a[href="/rewards/achievements"]').first().click();
|
||||
await expect(page).toHaveURL('/rewards/achievements');
|
||||
await expect(page.locator('h1:has-text("Achievements")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Click Activity
|
||||
await page.locator('nav a[href="/rewards/activity"]').first().click();
|
||||
await expect(page).toHaveURL('/rewards/activity');
|
||||
await expect(page.locator('h1:has-text("Activity")')).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Error Handling', () => {
|
||||
test('rewards page should handle API errors gracefully', async ({ page }) => {
|
||||
const errors = setupErrorCapture(page);
|
||||
|
||||
await page.goto('/rewards');
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Page should still render even if API fails
|
||||
await expect(page.locator('h1:has-text("Rewards")')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Should not have JS errors (API errors are expected)
|
||||
const criticalErrors = filterCriticalErrors(errors);
|
||||
expect(criticalErrors).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('leaderboard should show empty state or loading when API unavailable', async ({ page }) => {
|
||||
await page.goto('/rewards/leaderboard');
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Should either show loading skeleton, empty state, or actual data
|
||||
const hasContent =
|
||||
await page.locator('.animate-pulse').count() > 0 ||
|
||||
await page.locator('text=No entries').isVisible() ||
|
||||
await page.locator('text=/\\#[0-9]+|🥇|🥈|🥉/').count() > 0;
|
||||
|
||||
expect(hasContent).toBe(true);
|
||||
});
|
||||
});
|
||||
121
frontend/tests/rewards-verification.spec.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Rewards Pages Verification', () => {
|
||||
test('Home page loads without getting stuck', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/')
|
||||
|
||||
// Wait for page to load - should not be stuck on spinner
|
||||
await expect(page.locator('h1:has-text("H2H")')).toBeVisible({ timeout: 10000 })
|
||||
|
||||
// The page should show content, not just a spinner
|
||||
// Look for navigation links which indicate the page loaded
|
||||
await expect(page.locator('text=Sports').first()).toBeVisible()
|
||||
await expect(page.locator('text=Live').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('Header Rewards dropdown works', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/')
|
||||
|
||||
// Find and click the Rewards dropdown
|
||||
const rewardsButton = page.locator('button:has-text("Rewards")')
|
||||
await expect(rewardsButton).toBeVisible()
|
||||
await rewardsButton.click()
|
||||
|
||||
// Verify dropdown menu appears with all options
|
||||
await expect(page.locator('a[href="/rewards"]:has-text("Overview")')).toBeVisible()
|
||||
await expect(page.locator('a[href="/rewards/leaderboard"]')).toBeVisible()
|
||||
await expect(page.locator('a[href="/rewards/achievements"]')).toBeVisible()
|
||||
await expect(page.locator('a[href="/rewards/loot-boxes"]')).toBeVisible()
|
||||
await expect(page.locator('a[href="/rewards/activity"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Rewards Overview page loads with Header', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards')
|
||||
|
||||
// Check Header is present
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible()
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Rewards")')).toBeVisible()
|
||||
|
||||
// Check page has light theme (white/light backgrounds)
|
||||
const mainContent = page.locator('.bg-white').first()
|
||||
await expect(mainContent).toBeVisible()
|
||||
})
|
||||
|
||||
test('Leaderboard page loads', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards/leaderboard')
|
||||
|
||||
// Check Header is present
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible()
|
||||
|
||||
// Check page content
|
||||
await expect(page.locator('h1:has-text("Leaderboard")')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Achievements page shows login prompt when not authenticated', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards/achievements')
|
||||
|
||||
// Check Header is present
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible()
|
||||
|
||||
// Should show sign in required message
|
||||
await expect(page.locator('text=Sign In Required')).toBeVisible()
|
||||
// Use more specific selector for the sign in link in the content area
|
||||
await expect(page.locator('.bg-white a[href="/login"]:has-text("Sign In")')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Loot Boxes page shows login prompt when not authenticated', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards/loot-boxes')
|
||||
|
||||
// Check Header is present
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible()
|
||||
|
||||
// Should show sign in required message
|
||||
await expect(page.locator('text=Sign In Required')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Activity page loads', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards/activity')
|
||||
|
||||
// Check Header is present
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible()
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h1:has-text("Activity")')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Navigate between rewards pages via sub-nav', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/rewards')
|
||||
|
||||
// Click on Leaderboard in sub-nav
|
||||
await page.locator('nav a[href="/rewards/leaderboard"]').click()
|
||||
await expect(page).toHaveURL(/\/rewards\/leaderboard/)
|
||||
|
||||
// Click on Activity in sub-nav
|
||||
await page.locator('nav a[href="/rewards/activity"]').click()
|
||||
await expect(page).toHaveURL(/\/rewards\/activity/)
|
||||
})
|
||||
|
||||
test('All rewards pages have consistent light theme', async ({ page }) => {
|
||||
const pages = [
|
||||
'/rewards',
|
||||
'/rewards/leaderboard',
|
||||
'/rewards/achievements',
|
||||
'/rewards/loot-boxes',
|
||||
'/rewards/activity'
|
||||
]
|
||||
|
||||
for (const pagePath of pages) {
|
||||
await page.goto(`http://localhost:5173${pagePath}`)
|
||||
|
||||
// Verify light gray background (bg-gray-50)
|
||||
const body = page.locator('.min-h-screen.bg-gray-50')
|
||||
await expect(body).toBeVisible()
|
||||
|
||||
// Verify white content boxes exist
|
||||
const whiteBox = page.locator('.bg-white').first()
|
||||
await expect(whiteBox).toBeVisible()
|
||||
}
|
||||
})
|
||||
})
|
||||
72
frontend/tests/rewards.spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('Rewards page loads without errors', async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
|
||||
// Capture console errors
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
errors.push(msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
// Capture page errors
|
||||
page.on('pageerror', err => {
|
||||
errors.push(err.message);
|
||||
});
|
||||
|
||||
// Navigate to rewards page
|
||||
await page.goto('/rewards', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Wait for page content
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for the Rewards heading
|
||||
const heading = page.locator('h1:has-text("Rewards")');
|
||||
await expect(heading).toBeVisible({ timeout: 5000 });
|
||||
console.log('✓ Rewards page heading found');
|
||||
|
||||
// Check for key sections - use first() since there are multiple matches
|
||||
const leaderboardSection = page.locator('h3:has-text("Leaderboard")').first();
|
||||
const activitySection = page.locator('h3:has-text("Activity")').first();
|
||||
|
||||
// At least one of these should be visible
|
||||
const hasContent = await leaderboardSection.isVisible() || await activitySection.isVisible();
|
||||
expect(hasContent).toBe(true);
|
||||
console.log('✓ Gamification content sections found');
|
||||
|
||||
// Report any errors
|
||||
if (errors.length > 0) {
|
||||
console.log('Console errors found:', errors);
|
||||
}
|
||||
|
||||
// Filter out API errors (expected when backend isn't running)
|
||||
const criticalErrors = errors.filter(e =>
|
||||
!e.includes('Failed to load resource') &&
|
||||
!e.includes('ERR_CONNECTION_REFUSED')
|
||||
);
|
||||
|
||||
expect(criticalErrors.length).toBe(0);
|
||||
console.log('✓ No critical errors on Rewards page');
|
||||
});
|
||||
|
||||
test('Home page loads (may show loading if backend unavailable)', async ({ page }) => {
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Wait for content
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check for H2H branding in header
|
||||
await expect(page.locator('header h1:has-text("H2H")')).toBeVisible({ timeout: 5000 });
|
||||
console.log('✓ H2H header found');
|
||||
|
||||
// Check navigation includes Rewards link (use first() since there may be multiple)
|
||||
await expect(page.locator('nav a[href="/rewards"]').first()).toBeVisible({ timeout: 5000 });
|
||||
console.log('✓ Rewards nav link found');
|
||||
|
||||
// Page either shows loading state or full content - both are acceptable
|
||||
// when backend is not running
|
||||
const hasSpinner = await page.locator('.animate-spin').first().isVisible();
|
||||
const hasContent = await page.locator('text=Upcoming Events').isVisible();
|
||||
console.log(`✓ Home page rendered (loading: ${hasSpinner}, content: ${hasContent})`);
|
||||
});
|
||||
|
Before Width: | Height: | Size: 601 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 601 KiB After Width: | Height: | Size: 346 KiB |
|
Before Width: | Height: | Size: 601 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 601 KiB After Width: | Height: | Size: 131 KiB |