test: Add comprehensive E2E tests for enhanced dashboard features

- Add tests for search functionality with debounce
- Add tests for date range filtering
- Add tests for per-page selector
- Add tests for column sorting (ascending/descending)
- Add tests for pagination controls
- Add tests for combined filters working together
- Add tests for responsive design on mobile/tablet
- Add tests for loading indicators
- Add tests for error handling
- Add performance measurement for search operations

Tests cover all new dashboard features including search, filters,
pagination, and sorting to ensure functionality works as expected.

Co-Authored-By: Ben Reed <ben@tealmaker.com>
This commit is contained in:
bengizmo 2025-05-30 10:18:15 -06:00
parent 1bd871cc7b
commit a4d08fb15a

View file

@ -0,0 +1,343 @@
import { test, expect } from './fixtures/auth';
import { CommonActions } from './utils/common-actions';
/**
* Enhanced Dashboard Features Test Suite
*
* Tests the new search, filtering, pagination, and sorting features
* added to the trainer dashboard.
*/
test.describe('Enhanced Dashboard Features', () => {
let actions: CommonActions;
test.beforeEach(async ({ authenticatedPage: page }) => {
actions = new CommonActions(page);
// Navigate to dashboard
await actions.navigateAndWait('/hvac-dashboard/');
// Verify we're on the dashboard
await expect(page.locator('h1:has-text("Trainer Dashboard")')).toBeVisible();
// Wait for events table to load
await page.waitForSelector('.hvac-events-table-wrapper', { timeout: 10000 });
});
test('Search functionality', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing search functionality...');
// Find the search box
const searchBox = page.locator('#hvac-event-search');
await expect(searchBox).toBeVisible();
// Type a search term
await searchBox.fill('Test');
// Wait for debounce and AJAX to complete
await page.waitForTimeout(1000);
await page.waitForLoadState('networkidle');
// Check that the table has been updated (loading class removed)
await expect(page.locator('.hvac-events-table-wrapper.loading')).not.toBeVisible();
// Take screenshot of search results
await actions.screenshot('dashboard-search-results');
// Clear search
await searchBox.clear();
await page.waitForTimeout(1000);
console.log('✓ Search functionality working');
});
test('Date range filters', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing date range filters...');
// Find date inputs
const dateFrom = page.locator('#hvac-date-from');
const dateTo = page.locator('#hvac-date-to');
await expect(dateFrom).toBeVisible();
await expect(dateTo).toBeVisible();
// Set date range (events from last 30 days)
const today = new Date();
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(today.getDate() - 30);
await dateFrom.fill(thirtyDaysAgo.toISOString().split('T')[0]);
await dateTo.fill(today.toISOString().split('T')[0]);
// Wait for AJAX update
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Verify table updated
await expect(page.locator('.hvac-events-table-wrapper.loading')).not.toBeVisible();
await actions.screenshot('dashboard-date-filtered');
console.log('✓ Date range filters working');
});
test('Per page selector', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing per page selector...');
// Find per page selector
const perPageSelector = page.locator('#hvac-per-page');
await expect(perPageSelector).toBeVisible();
// Get initial row count
const initialRows = await page.locator('.hvac-events-table tbody tr').count();
console.log(`Initial rows: ${initialRows}`);
// Change to 25 per page
await perPageSelector.selectOption('25');
// Wait for AJAX update
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Check if pagination info updated
const displayingNum = page.locator('.displaying-num').first();
if (await displayingNum.count() > 0) {
const text = await displayingNum.textContent();
console.log(`Pagination info: ${text}`);
}
await actions.screenshot('dashboard-per-page-25');
console.log('✓ Per page selector working');
});
test('Column sorting', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing column sorting...');
// Test sorting by event name
const nameHeader = page.locator('th.sortable a[data-orderby="name"]').first();
await expect(nameHeader).toBeVisible();
// Click to sort by name
await nameHeader.click();
// Wait for AJAX update
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Verify sorted class is applied
const nameColumn = page.locator('th.sortable').filter({ has: page.locator('a[data-orderby="name"]') }).first();
await expect(nameColumn).toHaveClass(/sorted/);
await actions.screenshot('dashboard-sorted-by-name');
// Click again to reverse sort
await nameHeader.click();
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
await actions.screenshot('dashboard-sorted-by-name-desc');
console.log('✓ Column sorting working');
});
test('Pagination controls', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing pagination controls...');
// Check if pagination exists
const pagination = page.locator('.tablenav-pages').first();
const hasPagination = await pagination.count() > 0;
if (hasPagination) {
console.log('Pagination controls found');
// Check for next button
const nextButton = pagination.locator('a.next-page').first();
if (await nextButton.count() > 0 && !await nextButton.locator('..').hasClass('disabled')) {
// Click next page
await nextButton.click();
// Wait for update
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Verify page changed
const currentPage = await page.locator('.current-page').first().inputValue();
expect(parseInt(currentPage)).toBeGreaterThan(1);
await actions.screenshot('dashboard-page-2');
// Go back to first page
const firstButton = pagination.locator('a.first-page').first();
if (await firstButton.count() > 0) {
await firstButton.click();
await page.waitForTimeout(500);
}
} else {
console.log('Not enough events for pagination');
}
} else {
console.log('No pagination needed (few events)');
}
console.log('✓ Pagination controls tested');
});
test('Combined filters', async ({ authenticatedPage: page }) => {
test.setTimeout(45000);
console.log('Testing combined filters...');
// Apply multiple filters together
// 1. Set status filter
const draftFilter = page.locator('.hvac-event-filters a[data-status="draft"]').first();
if (await draftFilter.count() > 0) {
await draftFilter.click();
await page.waitForTimeout(500);
}
// 2. Add search term
const searchBox = page.locator('#hvac-event-search');
await searchBox.fill('Event');
await page.waitForTimeout(1000); // Wait for debounce
// 3. Sort by date
const dateHeader = page.locator('th.sortable a[data-orderby="date"]').first();
await dateHeader.click();
await page.waitForTimeout(500);
// 4. Change per page
const perPageSelector = page.locator('#hvac-per-page');
await perPageSelector.selectOption('50');
await page.waitForTimeout(500);
await page.waitForLoadState('networkidle');
// Take screenshot of combined filters
await actions.screenshot('dashboard-combined-filters');
// Verify URL has all parameters
const currentUrl = page.url();
console.log(`Current URL with filters: ${currentUrl}`);
// Reset filters
const allFilter = page.locator('.hvac-event-filters a[data-status="all"]').first();
await allFilter.click();
await searchBox.clear();
console.log('✓ Combined filters working');
});
test('Table responsiveness', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing table responsiveness...');
// Test mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.waitForTimeout(500);
// Check if controls stack vertically
await expect(page.locator('.hvac-table-controls')).toBeVisible();
await actions.screenshot('dashboard-mobile-view');
// Test tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await page.waitForTimeout(500);
await actions.screenshot('dashboard-tablet-view');
// Reset to desktop
await page.setViewportSize({ width: 1280, height: 720 });
console.log('✓ Table responsiveness tested');
});
test('Loading indicators', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing loading indicators...');
// Trigger a search to see loading state
const searchBox = page.locator('#hvac-event-search');
await searchBox.fill('Loading test');
// Try to catch the loading state (might be quick)
const loadingIndicator = page.locator('.hvac-loading');
const wasLoading = await loadingIndicator.count() > 0;
if (wasLoading) {
console.log('✓ Loading indicator displayed');
} else {
console.log('Loading too fast to capture (good performance)');
}
// Wait for completion
await page.waitForTimeout(1000);
await expect(page.locator('.hvac-events-table-wrapper.loading')).not.toBeVisible();
console.log('✓ Loading indicators tested');
});
test('Error handling', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
console.log('Testing error handling...');
// Test invalid page number in URL
await page.goto(page.url() + '?paged=9999');
await page.waitForLoadState('networkidle');
// Should still show table (even if empty)
await expect(page.locator('.hvac-events-table-wrapper')).toBeVisible();
// Test invalid date format (this should be prevented by input type=date)
const dateFrom = page.locator('#hvac-date-from');
const validDate = '2024-01-01';
await dateFrom.fill(validDate);
console.log('✓ Error handling tested');
});
});
test.describe('Dashboard Performance', () => {
test('Measure search performance', async ({ authenticatedPage: page }) => {
test.setTimeout(30000);
const actions = new CommonActions(page);
await actions.navigateAndWait('/hvac-dashboard/');
console.log('Measuring search performance...');
const searchBox = page.locator('#hvac-event-search');
// Measure time for search
const startTime = Date.now();
await searchBox.fill('Performance test');
// Wait for the table to update
await page.waitForFunction(() => {
const wrapper = document.querySelector('.hvac-events-table-wrapper');
return wrapper && !wrapper.classList.contains('loading');
}, { timeout: 10000 });
const endTime = Date.now();
const searchTime = endTime - startTime;
console.log(`Search completed in ${searchTime}ms`);
expect(searchTime).toBeLessThan(3000); // Should complete within 3 seconds
console.log('✓ Performance acceptable');
});
});