upskill-event-manager/tests/e2e/performance-resource.test.js
Ben 7c9ca65cf2
Some checks are pending
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Notification (push) Blocked by required conditions
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Waiting to run
Security Monitoring & Compliance / Secrets & Credential Scan (push) Waiting to run
Security Monitoring & Compliance / WordPress Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Static Code Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Security Compliance Validation (push) Waiting to run
Security Monitoring & Compliance / Security Summary Report (push) Blocked by required conditions
Security Monitoring & Compliance / Security Team Notification (push) Blocked by required conditions
feat: add comprehensive test framework and test files
- Add 90+ test files including E2E, unit, and integration tests
- Implement Page Object Model (POM) architecture
- Add Docker testing environment with comprehensive services
- Include modernized test framework with error recovery
- Add specialized test suites for master trainer and trainer workflows
- Update .gitignore to properly track test infrastructure

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 23:23:26 -03:00

433 lines
No EOL
17 KiB
JavaScript

/**
* Performance & Resource Optimization Test Suite
*
* Tests the major performance improvements after architectural refactoring:
* - CSS consolidation (250+ files → 5 bundles)
* - HTTP request reduction (85% target)
* - Page load performance (85% faster)
* - Safari browser stability
* - Memory usage optimization
*
* @package HVAC_Community_Events
* @version 3.0.0
* @created 2025-08-20
*/
const { test, expect, authHelpers, LoginPage } = require('../helpers/auth-fixtures');
const path = require('path');
// Test configuration
const BASE_URL = process.env.UPSKILL_STAGING_URL || 'https://upskill-staging.measurequick.com';
const TEST_TIMEOUT = 120000; // Extended for performance tests
// Performance thresholds (based on 85% improvement target)
const PERFORMANCE_TARGETS = {
maxCssFiles: 5, // Down from 250+
maxJsFiles: 10, // Reasonable limit
maxPageLoadTime: 3000, // 3 seconds (85% faster)
maxHttpRequests: 25, // 85% reduction target
maxMemoryUsage: 100, // MB threshold
maxDomNodes: 5000 // DOM complexity limit
};
async function measurePagePerformance(page, url) {
const startTime = Date.now();
// Clear cache and storage
await page.evaluate(() => {
if (window.performance && window.performance.clearResourceTimings) {
window.performance.clearResourceTimings();
}
if (window.caches) {
caches.keys().then(names => {
names.forEach(name => caches.delete(name));
});
}
});
// Navigate and measure
await page.goto(url, { waitUntil: 'networkidle' });
const loadTime = Date.now() - startTime;
// Get resource counts
const resources = await page.evaluate(() => {
const entries = performance.getEntriesByType('resource');
const cssFiles = entries.filter(e => e.name.includes('.css')).length;
const jsFiles = entries.filter(e => e.name.includes('.js')).length;
const totalRequests = entries.length;
return { cssFiles, jsFiles, totalRequests, loadTime: performance.timing.loadEventEnd - performance.timing.navigationStart };
});
// Get DOM complexity
const domStats = await page.evaluate(() => {
return {
nodeCount: document.querySelectorAll('*').length,
depth: Math.max(...Array.from(document.querySelectorAll('*')).map(el => {
let depth = 0;
let parent = el.parentElement;
while (parent) {
depth++;
parent = parent.parentElement;
}
return depth;
}))
};
});
return {
loadTime,
clientLoadTime: resources.loadTime,
...resources,
...domStats
};
}
async function monitorMemoryUsage(page) {
const memoryInfo = await page.evaluate(() => {
if (performance.memory) {
return {
usedJSHeapSize: performance.memory.usedJSHeapSize / 1024 / 1024, // MB
totalJSHeapSize: performance.memory.totalJSHeapSize / 1024 / 1024, // MB
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit / 1024 / 1024 // MB
};
}
return null;
});
return memoryInfo;
}
async function takePerformanceScreenshot(page, name, metrics) {
const screenshotDir = path.join(__dirname, '../../screenshots/performance');
await require('fs').promises.mkdir(screenshotDir, { recursive: true });
await page.screenshot({
path: path.join(screenshotDir, `${name}-${Date.now()}.png`),
fullPage: true
});
// Log metrics for debugging
console.log(`Performance metrics for ${name}:`, metrics);
}
test.describe('Performance & Resource Optimization Tests', () => {
test.setTimeout(TEST_TIMEOUT);
test.beforeEach(async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 720 });
// Login before each performance test
await authHelpers.loginAs(page, 'trainer');
});
test.describe('CSS Consolidation Tests', () => {
test('should load maximum 5 CSS files on dashboard', async ({ page }) => {
const metrics = await measurePagePerformance(page, `${BASE_URL}/trainer/dashboard/`);
console.log(`CSS files loaded: ${metrics.cssFiles}`);
expect(metrics.cssFiles).toBeLessThanOrEqual(PERFORMANCE_TARGETS.maxCssFiles);
await takePerformanceScreenshot(page, 'css-consolidation-dashboard', metrics);
});
test('should maintain CSS consolidation across all trainer pages', async ({ page }) => {
const pages = [
'/trainer/dashboard/',
'/trainer/profile/',
'/trainer/certificate-reports/',
'/trainer/events/',
'/trainer/events/create/'
];
for (const pagePath of pages) {
const metrics = await measurePagePerformance(page, `${BASE_URL}${pagePath}`);
console.log(`CSS files on ${pagePath}: ${metrics.cssFiles}`);
expect(metrics.cssFiles).toBeLessThanOrEqual(PERFORMANCE_TARGETS.maxCssFiles);
}
});
test('should not load foreign CSS files', async ({ page }) => {
await page.goto(`${BASE_URL}/trainer/dashboard/`);
// Check for unwanted CSS files
const foreignCss = await page.evaluate(() => {
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
return links.filter(link => {
const href = link.href;
return !href.includes('hvac-') &&
!href.includes('wordpress') &&
!href.includes('wp-') &&
!href.includes('astra') &&
href.includes('.css');
}).map(link => link.href);
});
console.log('Foreign CSS files detected:', foreignCss);
expect(foreignCss.length).toBeLessThan(10); // Allow some theme/plugin CSS
});
});
test.describe('Page Load Performance Tests', () => {
test('should load dashboard within performance target', async ({ page }) => {
// Dashboard is already loaded from beforeEach login, measure reload performance
const dashboardStartTime = Date.now();
await page.goto(`${BASE_URL}/trainer/dashboard/`, { waitUntil: 'networkidle' });
const dashboardLoadTime = Date.now() - dashboardStartTime;
console.log(`Dashboard load time: ${dashboardLoadTime}ms`);
expect(dashboardLoadTime).toBeLessThan(PERFORMANCE_TARGETS.maxPageLoadTime);
await takePerformanceScreenshot(page, 'dashboard-performance', { loadTime: dashboardLoadTime });
});
test('should maintain fast load times on subsequent pages', async ({ page }) => {
const pages = [
'/trainer/profile/',
'/trainer/certificate-reports/',
'/trainer/events/'
];
for (const pagePath of pages) {
const startTime = Date.now();
await page.goto(`${BASE_URL}${pagePath}`, { waitUntil: 'networkidle' });
const loadTime = Date.now() - startTime;
console.log(`Load time for ${pagePath}: ${loadTime}ms`);
expect(loadTime).toBeLessThan(PERFORMANCE_TARGETS.maxPageLoadTime);
}
});
test('should handle concurrent page loads without degradation', async ({ browser }) => {
const contexts = await Promise.all([
browser.newContext(),
browser.newContext(),
browser.newContext()
]);
const pages = await Promise.all(contexts.map(ctx => ctx.newPage()));
// Simulate concurrent users
const loadPromises = pages.map(async (page, index) => {
const startTime = Date.now();
await page.goto(`${BASE_URL}/trainer/dashboard/`);
const loadTime = Date.now() - startTime;
return { page: index, loadTime };
});
const results = await Promise.all(loadPromises);
// All concurrent loads should complete within acceptable time
results.forEach(result => {
console.log(`Concurrent load ${result.page}: ${result.loadTime}ms`);
expect(result.loadTime).toBeLessThan(PERFORMANCE_TARGETS.maxPageLoadTime * 2); // Allow 2x for concurrent
});
// Cleanup
await Promise.all(contexts.map(ctx => ctx.close()));
});
});
test.describe('HTTP Request Optimization Tests', () => {
test('should minimize HTTP requests on dashboard', async ({ page }) => {
const metrics = await measurePagePerformance(page, `${BASE_URL}/trainer/dashboard/`);
console.log(`Total HTTP requests: ${metrics.totalRequests}`);
expect(metrics.totalRequests).toBeLessThan(PERFORMANCE_TARGETS.maxHttpRequests);
await takePerformanceScreenshot(page, 'http-requests-dashboard', metrics);
});
test('should use resource caching effectively', async ({ page }) => {
// First load
const firstLoad = await measurePagePerformance(page, `${BASE_URL}/trainer/dashboard/`);
// Second load (should use cache)
const secondLoad = await measurePagePerformance(page, `${BASE_URL}/trainer/dashboard/`);
console.log(`First load requests: ${firstLoad.totalRequests}, Second load: ${secondLoad.totalRequests}`);
// Second load should have fewer requests due to caching
expect(secondLoad.totalRequests).toBeLessThanOrEqual(firstLoad.totalRequests);
});
});
test.describe('Safari Browser Stability Tests', () => {
test('should load without hanging in Safari/WebKit', async ({ page, browserName }) => {
test.skip(browserName !== 'webkit', 'Safari-specific test');
// Extended timeout for Safari
test.setTimeout(180000);
const startTime = Date.now();
try {
await page.goto(`${BASE_URL}/find-trainer/`, {
waitUntil: 'networkidle',
timeout: 60000
});
const loadTime = Date.now() - startTime;
console.log(`Safari load time: ${loadTime}ms`);
// Verify page actually loaded
await expect(page.locator('body')).toBeVisible();
// Check for interactive elements
const interactiveElements = await page.$$('button, a, input, select');
expect(interactiveElements.length).toBeGreaterThan(0);
await takePerformanceScreenshot(page, 'safari-stability', { loadTime });
} catch (error) {
console.error('Safari loading failed:', error);
await page.screenshot({
path: path.join(__dirname, '../../screenshots/safari-failure.png'),
fullPage: true
});
throw error;
}
});
test('should handle Safari resource loading efficiently', async ({ page, browserName }) => {
test.skip(browserName !== 'webkit', 'Safari-specific test');
await page.goto(`${BASE_URL}/trainer/dashboard/`);
// Monitor for resource loading cascade issues
const resourceTimings = await page.evaluate(() => {
const entries = performance.getEntriesByType('resource');
return entries.map(entry => ({
name: entry.name,
duration: entry.duration,
transferSize: entry.transferSize || 0
}));
});
// Check for excessively long resource loads
const slowResources = resourceTimings.filter(r => r.duration > 5000);
console.log('Slow resources in Safari:', slowResources);
expect(slowResources.length).toBeLessThan(3); // Allow some tolerance
});
});
test.describe('Memory Usage Tests', () => {
test('should maintain reasonable memory usage', async ({ page, browserName }) => {
test.skip(browserName !== 'chromium', 'Memory API only available in Chromium');
// Initial memory
const initialMemory = await monitorMemoryUsage(page);
// Navigate through multiple pages to stress test
const pages = [
'/trainer/dashboard/',
'/trainer/profile/',
'/trainer/certificate-reports/',
'/trainer/events/',
'/trainer/profile/edit/'
];
for (const pagePath of pages) {
await page.goto(`${BASE_URL}${pagePath}`);
await page.waitForLoadState('networkidle');
}
// Final memory check
const finalMemory = await monitorMemoryUsage(page);
if (initialMemory && finalMemory) {
const memoryIncrease = finalMemory.usedJSHeapSize - initialMemory.usedJSHeapSize;
console.log(`Memory usage increase: ${memoryIncrease.toFixed(2)}MB`);
expect(finalMemory.usedJSHeapSize).toBeLessThan(PERFORMANCE_TARGETS.maxMemoryUsage);
expect(memoryIncrease).toBeLessThan(50); // Max 50MB increase during navigation
}
});
test('should clean up memory after heavy operations', async ({ page, browserName }) => {
test.skip(browserName !== 'chromium', 'Memory API only available in Chromium');
await page.goto(`${BASE_URL}/trainer/events/create/`);
const beforeMemory = await monitorMemoryUsage(page);
// Simulate heavy form interaction
for (let i = 0; i < 10; i++) {
await page.reload();
await page.waitForLoadState('networkidle');
}
// Force garbage collection if available
await page.evaluate(() => {
if (window.gc) {
window.gc();
}
});
const afterMemory = await monitorMemoryUsage(page);
if (beforeMemory && afterMemory) {
console.log(`Memory before: ${beforeMemory.usedJSHeapSize.toFixed(2)}MB, after: ${afterMemory.usedJSHeapSize.toFixed(2)}MB`);
// Memory should not have grown excessively
const memoryGrowth = afterMemory.usedJSHeapSize - beforeMemory.usedJSHeapSize;
expect(memoryGrowth).toBeLessThan(30); // Max 30MB growth for heavy operations
}
});
});
test.describe('DOM Complexity Tests', () => {
test('should maintain reasonable DOM complexity', async ({ page }) => {
const pages = [
'/trainer/dashboard/',
'/trainer/profile/',
'/trainer/events/'
];
for (const pagePath of pages) {
const metrics = await measurePagePerformance(page, `${BASE_URL}${pagePath}`);
console.log(`DOM nodes on ${pagePath}: ${metrics.nodeCount}`);
expect(metrics.nodeCount).toBeLessThan(PERFORMANCE_TARGETS.maxDomNodes);
expect(metrics.depth).toBeLessThan(20); // Reasonable DOM depth
}
});
test('should optimize form rendering performance', async ({ page }) => {
await page.goto(`${BASE_URL}/trainer/events/create/`);
const formMetrics = await page.evaluate(() => {
const forms = document.querySelectorAll('form');
const inputs = document.querySelectorAll('input, select, textarea');
return {
formCount: forms.length,
inputCount: inputs.length,
renderTime: performance.now()
};
});
console.log('Form complexity:', formMetrics);
// Forms should render efficiently
expect(formMetrics.inputCount).toBeLessThan(100); // Reasonable form complexity
expect(formMetrics.renderTime).toBeLessThan(1000); // Quick render time
});
});
});
// Export performance test configuration
module.exports = {
testDir: __dirname,
timeout: TEST_TIMEOUT,
retries: 2, // Performance tests may need retries
workers: 1, // Sequential for accurate performance measurement
use: {
baseURL: BASE_URL,
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure'
}
};