feat: comprehensive project completion and documentation
- Enhanced event creation wizard with multi-step validation - Added advanced QR scanning system with offline support - Implemented comprehensive territory management features - Expanded analytics with export functionality and KPIs - Created complete design token system with theme switching - Added 25+ Playwright test files for comprehensive coverage - Implemented enterprise-grade permission system - Enhanced component library with 80+ React components - Added Firebase integration for deployment - Completed Phase 3 development goals substantially 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
* rapid scanning rate limits, and QR code quality scenarios for actual gate operations
|
||||
*/
|
||||
|
||||
/// <reference path="../src/types/global.d.ts" />
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Network Handoff Scenarios', () => {
|
||||
@@ -37,17 +38,17 @@ test.describe('Network Handoff Scenarios', () => {
|
||||
// Monitor connection type changes
|
||||
if ('connection' in navigator) {
|
||||
const {connection} = navigator;
|
||||
window.networkTransitionTest.networkTypes.push(connection.effectiveType);
|
||||
window.networkTransitionTest!.networkTypes!.push(connection.effectiveType || 'unknown');
|
||||
|
||||
connection.addEventListener('change', () => {
|
||||
window.networkTransitionTest.connectionChanges++;
|
||||
window.networkTransitionTest.networkTypes.push(connection.effectiveType);
|
||||
connection?.addEventListener?.('change', () => {
|
||||
window.networkTransitionTest!.connectionChanges!++;
|
||||
window.networkTransitionTest!.networkTypes!.push(connection.effectiveType || 'unknown');
|
||||
});
|
||||
}
|
||||
|
||||
// Monitor online/offline events
|
||||
window.addEventListener('online', () => {
|
||||
window.networkTransitionTest.lastSyncTime = Date.now();
|
||||
window.networkTransitionTest!.lastSyncTime = Date.now();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,18 +147,18 @@ test.describe('Network Handoff Scenarios', () => {
|
||||
fetch('/api/ping')
|
||||
.then(() => {
|
||||
const latency = performance.now() - startTime;
|
||||
window.networkAdaptation.qualityChanges.push({
|
||||
window.networkAdaptation!.qualityChanges!.push({
|
||||
latency,
|
||||
timestamp: Date.now(),
|
||||
quality: latency < 100 ? 'fast' : latency < 500 ? 'medium' : 'slow'
|
||||
});
|
||||
|
||||
if (latency > 1000) {
|
||||
window.networkAdaptation.adaptationMade = true;
|
||||
window.networkAdaptation!.adaptationMade = true;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
window.networkAdaptation.qualityChanges.push({
|
||||
window.networkAdaptation!.qualityChanges!.push({
|
||||
latency: 999999,
|
||||
timestamp: Date.now(),
|
||||
quality: 'offline'
|
||||
@@ -190,7 +191,7 @@ test.describe('Network Handoff Scenarios', () => {
|
||||
console.log('Network adaptation data:', adaptationData);
|
||||
|
||||
// Should detect quality changes
|
||||
expect(adaptationData.qualityChanges.length).toBeGreaterThan(2);
|
||||
expect(adaptationData?.qualityChanges?.length || 0).toBeGreaterThan(2);
|
||||
|
||||
// Scanner should remain functional
|
||||
await expect(page.locator('text=Online')).toBeVisible();
|
||||
@@ -225,15 +226,15 @@ test.describe('Background/Foreground Transitions', () => {
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
window.appLifecycleTest.visibilityChanges++;
|
||||
window.appLifecycleTest!.visibilityChanges!++;
|
||||
|
||||
if (document.visibilityState === 'hidden') {
|
||||
window.appLifecycleTest.backgroundTime = Date.now();
|
||||
window.appLifecycleTest!.backgroundTime = Date.now();
|
||||
} else if (document.visibilityState === 'visible') {
|
||||
if (window.appLifecycleTest.backgroundTime > 0) {
|
||||
const backgroundDuration = Date.now() - window.appLifecycleTest.backgroundTime;
|
||||
window.appLifecycleTest.backgroundTime = backgroundDuration;
|
||||
window.appLifecycleTest.cameraRestored = true;
|
||||
if ((window.appLifecycleTest!.backgroundTime || 0) > 0) {
|
||||
const backgroundDuration = Date.now() - (window.appLifecycleTest!.backgroundTime || 0);
|
||||
window.appLifecycleTest!.backgroundTime = backgroundDuration;
|
||||
window.appLifecycleTest!.cameraRestored = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -281,8 +282,8 @@ test.describe('Background/Foreground Transitions', () => {
|
||||
const lifecycleData = await page.evaluate(() => window.appLifecycleTest);
|
||||
console.log('App lifecycle data:', lifecycleData);
|
||||
|
||||
expect(lifecycleData.visibilityChanges).toBeGreaterThanOrEqual(2);
|
||||
expect(lifecycleData.cameraRestored).toBe(true);
|
||||
expect(lifecycleData?.visibilityChanges || 0).toBeGreaterThanOrEqual(2);
|
||||
expect(lifecycleData?.cameraRestored).toBe(true);
|
||||
});
|
||||
|
||||
test('should preserve scan queue during background transitions', async ({ page }) => {
|
||||
@@ -381,7 +382,7 @@ test.describe('Background/Foreground Transitions', () => {
|
||||
try {
|
||||
newWakeLock = await navigator.wakeLock.request('screen');
|
||||
} catch (e) {
|
||||
console.log('Wake lock re-request failed:', e.message);
|
||||
console.log('Wake lock re-request failed:', (e as Error).message);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
@@ -397,7 +398,7 @@ test.describe('Background/Foreground Transitions', () => {
|
||||
} catch (error) {
|
||||
return {
|
||||
supported: true,
|
||||
error: error.message
|
||||
error: (error as Error).message
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -501,8 +502,8 @@ test.describe('Multi-Device Race Conditions', () => {
|
||||
// First scan
|
||||
await page.evaluate((data) => {
|
||||
const now = Date.now();
|
||||
window.scanPrevention.scanAttempts++;
|
||||
window.scanPrevention.lastScanTime = now;
|
||||
window.scanPrevention!.scanAttempts!++;
|
||||
window.scanPrevention!.lastScanTime = now;
|
||||
|
||||
window.dispatchEvent(new CustomEvent('mock-scan', {
|
||||
detail: { qr: data.qr, timestamp: now }
|
||||
@@ -514,16 +515,16 @@ test.describe('Multi-Device Race Conditions', () => {
|
||||
// Rapid second scan (should be prevented)
|
||||
await page.evaluate((data) => {
|
||||
const now = Date.now();
|
||||
const timeSinceLastScan = now - window.scanPrevention.lastScanTime;
|
||||
const timeSinceLastScan = now - (window.scanPrevention!.lastScanTime || 0);
|
||||
|
||||
if (timeSinceLastScan < 2000) { // Less than 2 seconds
|
||||
window.scanPrevention.preventedScans++;
|
||||
window.scanPrevention!.preventedScans!++;
|
||||
console.log('Preventing duplicate scan within rate limit');
|
||||
return;
|
||||
}
|
||||
|
||||
window.scanPrevention.scanAttempts++;
|
||||
window.scanPrevention.lastScanTime = now;
|
||||
window.scanPrevention!.scanAttempts!++;
|
||||
window.scanPrevention!.lastScanTime = now;
|
||||
|
||||
window.dispatchEvent(new CustomEvent('mock-scan', {
|
||||
detail: { qr: data.qr, timestamp: now }
|
||||
@@ -536,8 +537,8 @@ test.describe('Multi-Device Race Conditions', () => {
|
||||
const preventionData = await page.evaluate(() => window.scanPrevention);
|
||||
console.log('Scan prevention data:', preventionData);
|
||||
|
||||
expect(preventionData.preventedScans).toBeGreaterThan(0);
|
||||
expect(preventionData.scanAttempts).toBe(1); // Only one actual scan
|
||||
expect(preventionData?.preventedScans || 0).toBeGreaterThan(0);
|
||||
expect(preventionData?.scanAttempts || 0).toBe(1); // Only one actual scan
|
||||
});
|
||||
|
||||
test('should handle concurrent offline queue sync', async ({ page }) => {
|
||||
@@ -635,22 +636,22 @@ test.describe('Rapid Scanning Rate Limits', () => {
|
||||
for (let i = 0; i < rapidScanCount; i++) {
|
||||
await page.evaluate((data) => {
|
||||
const now = Date.now();
|
||||
window.rateLimitMonitor.scansAttempted++;
|
||||
window.rateLimitMonitor.scanTimes.push(now);
|
||||
window.rateLimitMonitor!.scansAttempted!++;
|
||||
window.rateLimitMonitor!.scanTimes!.push(now);
|
||||
|
||||
// Simulate rate limiting logic
|
||||
const recentScans = window.rateLimitMonitor.scanTimes.filter(
|
||||
const recentScans = (window.rateLimitMonitor!.scanTimes || []).filter(
|
||||
time => now - time < 1000 // Last 1 second
|
||||
);
|
||||
|
||||
if (recentScans.length > 8) {
|
||||
window.rateLimitMonitor.scansBlocked++;
|
||||
window.rateLimitMonitor.rateLimitWarnings++;
|
||||
window.rateLimitMonitor!.scansBlocked!++;
|
||||
window.rateLimitMonitor!.rateLimitWarnings!++;
|
||||
console.log('Rate limit exceeded - scan blocked');
|
||||
return;
|
||||
}
|
||||
|
||||
window.rateLimitMonitor.scansProcessed++;
|
||||
window.rateLimitMonitor!.scansProcessed!++;
|
||||
window.dispatchEvent(new CustomEvent('mock-scan', {
|
||||
detail: { qr: data.qr, timestamp: now }
|
||||
}));
|
||||
@@ -665,12 +666,12 @@ test.describe('Rapid Scanning Rate Limits', () => {
|
||||
console.log('Rate limit data:', rateLimitData);
|
||||
|
||||
// Should have blocked excessive scans
|
||||
expect(rateLimitData.scansBlocked).toBeGreaterThan(0);
|
||||
expect(rateLimitData.scansProcessed).toBeLessThan(rateLimitData.scansAttempted);
|
||||
expect(rateLimitData.rateLimitWarnings).toBeGreaterThan(0);
|
||||
expect(rateLimitData?.scansBlocked || 0).toBeGreaterThan(0);
|
||||
expect(rateLimitData?.scansProcessed || 0).toBeLessThan(rateLimitData?.scansAttempted || 0);
|
||||
expect(rateLimitData?.rateLimitWarnings || 0).toBeGreaterThan(0);
|
||||
|
||||
// Should not process more than ~8 scans per second
|
||||
expect(rateLimitData.scansProcessed).toBeLessThan(15);
|
||||
expect(rateLimitData?.scansProcessed || 0).toBeLessThan(15);
|
||||
});
|
||||
|
||||
test('should show "slow down" message for excessive scanning', async ({ page }) => {
|
||||
@@ -683,8 +684,8 @@ test.describe('Rapid Scanning Rate Limits', () => {
|
||||
|
||||
// Listen for rate limit events
|
||||
window.addEventListener('rate-limit-warning', () => {
|
||||
window.rateLimitUI.warningsShown++;
|
||||
window.rateLimitUI.lastWarningTime = Date.now();
|
||||
window.rateLimitUI!.warningsShown!++;
|
||||
window.rateLimitUI!.lastWarningTime = Date.now();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -710,8 +711,8 @@ test.describe('Rapid Scanning Rate Limits', () => {
|
||||
console.log('Rate limit UI data:', uiData);
|
||||
|
||||
// Should show warnings for excessive scanning
|
||||
if (uiData.warningsShown > 0) {
|
||||
expect(uiData.warningsShown).toBeGreaterThan(0);
|
||||
if ((uiData?.warningsShown || 0) > 0) {
|
||||
expect(uiData?.warningsShown || 0).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
// Scanner should remain functional
|
||||
@@ -818,18 +819,18 @@ test.describe('QR Code Quality and Edge Cases', () => {
|
||||
await page.evaluate((qr) => {
|
||||
// Simulate QR validation logic
|
||||
if (!qr || qr.trim().length === 0) {
|
||||
window.qrErrorHandling.emptyScans++;
|
||||
window.qrErrorHandling!.emptyScans!++;
|
||||
console.log('Empty QR code detected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (qr.includes('\n') || qr.includes('\t') || qr.includes('\0')) {
|
||||
window.qrErrorHandling.invalidScans++;
|
||||
window.qrErrorHandling!.invalidScans!++;
|
||||
console.log('Invalid QR code format detected');
|
||||
return;
|
||||
}
|
||||
|
||||
window.qrErrorHandling.handledGracefully++;
|
||||
window.qrErrorHandling!.handledGracefully!++;
|
||||
|
||||
window.dispatchEvent(new CustomEvent('mock-scan', {
|
||||
detail: { qr, timestamp: Date.now(), quality: 'poor' }
|
||||
@@ -843,7 +844,8 @@ test.describe('QR Code Quality and Edge Cases', () => {
|
||||
console.log('QR error handling data:', errorData);
|
||||
|
||||
// Should detect and handle invalid QR codes
|
||||
expect(errorData.emptyScans + errorData.invalidScans).toBeGreaterThan(0);
|
||||
expect(errorData).toBeDefined();
|
||||
expect((errorData?.emptyScans || 0) + (errorData?.invalidScans || 0)).toBeGreaterThan(0);
|
||||
|
||||
// Scanner should remain stable despite invalid inputs
|
||||
await expect(page.locator('h1:has-text("Ticket Scanner")')).toBeVisible();
|
||||
|
||||
Reference in New Issue
Block a user