forked from cardosofelipe/fast-next-template
- Refactored JSX elements to improve readability by collapsing multi-line props and attributes into single lines if their length permits. - Improved consistency in component imports by grouping and consolidating them. - No functional changes, purely restructuring for clarity and maintainability.
205 lines
6.8 KiB
TypeScript
205 lines
6.8 KiB
TypeScript
/**
|
|
* E2E Tests for Activity Feed Page
|
|
*
|
|
* Tests the real-time activity feed functionality:
|
|
* - Page navigation and layout
|
|
* - Event display and filtering
|
|
* - Search functionality
|
|
* - Approval handling
|
|
* - Time-based grouping
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Activity Feed Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Navigate to activity page (authenticated route)
|
|
// The page uses demo mode when SSE is not connected
|
|
await page.goto('/en/activity');
|
|
});
|
|
|
|
test('displays page header with title', async ({ page }) => {
|
|
await expect(page.getByRole('heading', { name: 'Activity Feed', level: 1 })).toBeVisible();
|
|
await expect(page.getByText('Real-time updates from your projects')).toBeVisible();
|
|
});
|
|
|
|
test('shows demo mode banner when not connected to SSE', async ({ page }) => {
|
|
// Demo mode banner should be visible
|
|
await expect(page.getByText(/Demo Mode/)).toBeVisible();
|
|
await expect(page.getByText(/Showing sample events/)).toBeVisible();
|
|
});
|
|
|
|
test('displays activity feed component', async ({ page }) => {
|
|
await expect(page.getByTestId('activity-feed')).toBeVisible();
|
|
});
|
|
|
|
test('displays demo events in time groups', async ({ page }) => {
|
|
// Should have time-based grouping
|
|
await expect(page.getByTestId('event-group-today')).toBeVisible();
|
|
});
|
|
|
|
test('search functionality filters events', async ({ page }) => {
|
|
const searchInput = page.getByTestId('search-input');
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
// Search for a specific term
|
|
await searchInput.fill('JWT');
|
|
|
|
// Should find the JWT-related event
|
|
await expect(page.getByText(/Completed JWT/)).toBeVisible();
|
|
|
|
// Clear search
|
|
await searchInput.clear();
|
|
});
|
|
|
|
test('filter panel toggles visibility', async ({ page }) => {
|
|
const filterToggle = page.getByTestId('filter-toggle');
|
|
await expect(filterToggle).toBeVisible();
|
|
|
|
// Click to open filter panel
|
|
await filterToggle.click();
|
|
await expect(page.getByTestId('filter-panel')).toBeVisible();
|
|
|
|
// Click to close filter panel
|
|
await filterToggle.click();
|
|
await expect(page.getByTestId('filter-panel')).not.toBeVisible();
|
|
});
|
|
|
|
test('filter by event category', async ({ page }) => {
|
|
// Open filter panel
|
|
await page.getByTestId('filter-toggle').click();
|
|
|
|
// Select Agent Actions filter
|
|
await page.getByLabel(/Agent Actions/).click();
|
|
|
|
// Should filter events
|
|
// Agent events should still be visible
|
|
await expect(page.getByText(/Completed JWT/)).toBeVisible();
|
|
});
|
|
|
|
test('pending approvals filter', async ({ page }) => {
|
|
// Open filter panel
|
|
await page.getByTestId('filter-toggle').click();
|
|
|
|
// Select pending only filter
|
|
await page.getByLabel(/Show only pending approvals/).click();
|
|
|
|
// Only approval events should be visible
|
|
await expect(page.getByText(/Approval required/)).toBeVisible();
|
|
await expect(page.getByText(/Completed JWT/)).not.toBeVisible();
|
|
});
|
|
|
|
test('event item can be expanded', async ({ page }) => {
|
|
// Find and click an event item
|
|
const firstEvent = page.getByTestId(/event-item-/).first();
|
|
await firstEvent.click();
|
|
|
|
// Event details should be visible
|
|
await expect(page.getByTestId('event-details')).toBeVisible();
|
|
|
|
// Should show raw payload option
|
|
await expect(page.getByText('View raw payload')).toBeVisible();
|
|
});
|
|
|
|
test('approval actions are visible for pending approvals', async ({ page }) => {
|
|
// Find approval event
|
|
const approvalEvent = page
|
|
.locator('[data-testid^="event-item-"]', {
|
|
has: page.getByText('Action Required'),
|
|
})
|
|
.first();
|
|
|
|
// Approval buttons should be visible
|
|
await expect(approvalEvent.getByTestId('approve-button')).toBeVisible();
|
|
await expect(approvalEvent.getByTestId('reject-button')).toBeVisible();
|
|
});
|
|
|
|
test('notification toggle works', async ({ page }) => {
|
|
const bellButton = page.getByLabel(/notifications/i);
|
|
await expect(bellButton).toBeVisible();
|
|
|
|
// Click to toggle notifications
|
|
await bellButton.click();
|
|
|
|
// Bell icon should change (hard to verify icon change in E2E, but click should work)
|
|
await expect(bellButton).toBeEnabled();
|
|
});
|
|
|
|
test('refresh button triggers reconnection', async ({ page }) => {
|
|
const refreshButton = page.getByLabel('Refresh connection');
|
|
await expect(refreshButton).toBeVisible();
|
|
|
|
// Click refresh
|
|
await refreshButton.click();
|
|
|
|
// Button should still be visible and enabled
|
|
await expect(refreshButton).toBeEnabled();
|
|
});
|
|
|
|
test('pending count badge shows correct count', async ({ page }) => {
|
|
// Should show pending count
|
|
await expect(page.getByText(/pending$/)).toBeVisible();
|
|
});
|
|
|
|
test('keyboard navigation works for events', async ({ page }) => {
|
|
// Tab to first event
|
|
await page.keyboard.press('Tab');
|
|
await page.keyboard.press('Tab');
|
|
await page.keyboard.press('Tab');
|
|
await page.keyboard.press('Tab');
|
|
|
|
// Find focused element and press Enter
|
|
const focusedElement = page.locator(':focus');
|
|
|
|
// If it's an event item, pressing Enter should expand it
|
|
const testId = await focusedElement.getAttribute('data-testid');
|
|
if (testId?.startsWith('event-item-')) {
|
|
await page.keyboard.press('Enter');
|
|
await expect(page.getByTestId('event-details')).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('clear filters button works', async ({ page }) => {
|
|
// Open filter panel and set some filters
|
|
await page.getByTestId('filter-toggle').click();
|
|
await page.getByLabel(/Agent Actions/).click();
|
|
|
|
// Click clear filters
|
|
await page.getByText('Clear Filters').click();
|
|
|
|
// All events should be visible again
|
|
await expect(page.getByText(/Completed JWT/)).toBeVisible();
|
|
await expect(page.getByText(/Approval required/)).toBeVisible();
|
|
});
|
|
|
|
test('responsive layout adapts to viewport', async ({ page }) => {
|
|
// Test mobile viewport
|
|
await page.setViewportSize({ width: 375, height: 667 });
|
|
|
|
// Page should still be functional
|
|
await expect(page.getByTestId('activity-feed')).toBeVisible();
|
|
await expect(page.getByTestId('search-input')).toBeVisible();
|
|
|
|
// Reset viewport
|
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
});
|
|
});
|
|
|
|
test.describe('Activity Feed - Authenticated Routes', () => {
|
|
test('redirects to login when not authenticated', async ({ page }) => {
|
|
// Clear any existing auth state
|
|
await page.context().clearCookies();
|
|
|
|
// Try to access activity page
|
|
await page.goto('/en/activity');
|
|
|
|
// Should redirect to login (AuthGuard behavior)
|
|
// The exact behavior depends on AuthGuard implementation
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Either on activity page (if demo mode) or redirected to login
|
|
const url = page.url();
|
|
expect(url.includes('/activity') || url.includes('/login')).toBeTruthy();
|
|
});
|
|
});
|