/** * QR Code System End-to-End Tests * Tests QR validation, manual entry, and scanner integration */ import { test, expect } from '@playwright/test'; test.describe('QR Code System', () => { test.beforeEach(async ({ page }) => { // Navigate to scanner page with event ID await page.goto('/scanner?eventId=test-event-123'); // Wait for scanner to initialize await page.waitForSelector('[data-testid="scanner-interface"]', { timeout: 10000 }); }); test('should display manual entry button in scanner interface', async ({ page }) => { // Check for manual entry button (hash icon) const manualEntryButton = page.locator('button[title="Manual Entry"]'); await expect(manualEntryButton).toBeVisible(); // Verify button has hash icon await expect(manualEntryButton.locator('svg')).toBeVisible(); }); test('should open manual entry modal when hash button clicked', async ({ page }) => { // Click manual entry button await page.click('button[title="Manual Entry"]'); // Verify modal opens await expect(page.locator('h2:text("Manual Entry")')).toBeVisible(); // Verify keypad is present await expect(page.locator('button:text("1")')).toBeVisible(); await expect(page.locator('button:text("2")')).toBeVisible(); await expect(page.locator('button:text("3")')).toBeVisible(); }); test('manual entry modal should have proper keyboard navigation', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Test numeric input await page.keyboard.press('1'); await page.keyboard.press('2'); await page.keyboard.press('3'); await page.keyboard.press('4'); await page.keyboard.press('A'); await page.keyboard.press('B'); await page.keyboard.press('C'); await page.keyboard.press('D'); // Verify code display shows entered characters const codeDisplay = page.locator('div').filter({ hasText: /1234-ABCD/ }).first(); await expect(codeDisplay).toBeVisible(); }); test('should validate backup codes properly', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Enter invalid code (too short) await page.keyboard.press('1'); await page.keyboard.press('2'); await page.keyboard.press('3'); // Submit button should be disabled const submitButton = page.locator('button:text("Submit")'); await expect(submitButton).toBeDisabled(); // Complete valid code await page.keyboard.press('4'); await page.keyboard.press('A'); await page.keyboard.press('B'); await page.keyboard.press('C'); await page.keyboard.press('D'); // Submit button should be enabled await expect(submitButton).toBeEnabled(); }); test('should handle manual entry submission', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Enter a complete backup code await page.keyboard.press('1'); await page.keyboard.press('2'); await page.keyboard.press('3'); await page.keyboard.press('4'); await page.keyboard.press('A'); await page.keyboard.press('B'); await page.keyboard.press('C'); await page.keyboard.press('D'); // Submit the code await page.click('button:text("Submit")'); // Modal should close and scan result should appear await expect(page.locator('h2:text("Manual Entry")')).toBeHidden(); // Should show scan result (either success or error) await expect(page.locator('[class*="ring-2"]')).toBeVisible({ timeout: 5000 }); }); test('should close modal on escape key', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Verify modal is open await expect(page.locator('h2:text("Manual Entry")')).toBeVisible(); // Press escape await page.keyboard.press('Escape'); // Modal should close await expect(page.locator('h2:text("Manual Entry")')).toBeHidden(); }); test('should show letter keys when toggle is clicked', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Initially letters should be hidden await expect(page.locator('button:text("A")')).toBeHidden(); // Click show letters toggle await page.click('button:text("Show Letters A-F")'); // Letters should now be visible await expect(page.locator('button:text("A")')).toBeVisible(); await expect(page.locator('button:text("B")')).toBeVisible(); await expect(page.locator('button:text("F")')).toBeVisible(); // Toggle text should change await expect(page.locator('button:text("Hide Letters A-F")')).toBeVisible(); }); test('should clear code when clear button clicked', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Enter some characters await page.keyboard.press('1'); await page.keyboard.press('2'); await page.keyboard.press('3'); // Verify characters are displayed await expect(page.locator('div').filter({ hasText: /123/ }).first()).toBeVisible(); // Click clear button await page.click('button:text("Clear")'); // Code should be cleared (showing underscores) await expect(page.locator('div').filter({ hasText: /____-____/ }).first()).toBeVisible(); }); test('should delete characters when delete button clicked', async ({ page }) => { await page.click('button[title="Manual Entry"]'); // Enter some characters await page.keyboard.press('1'); await page.keyboard.press('2'); await page.keyboard.press('3'); await page.keyboard.press('4'); // Verify 4 characters await expect(page.locator('div').filter({ hasText: /1234/ }).first()).toBeVisible(); // Click delete button await page.locator('button').filter({ has: page.locator('svg') }).first().click(); // Should show 3 characters await expect(page.locator('div').filter({ hasText: /123_/ }).first()).toBeVisible(); }); test('should have instructions updated for manual entry', async ({ page }) => { // Check that scanner instructions mention manual entry await expect(page.locator('text=Use # button for manual entry when QR is unreadable')).toBeVisible(); }); test('should work with touch events for mobile devices', async ({ page, isMobile }) => { if (!isMobile) { test.skip('Mobile-only test'); } await page.click('button[title="Manual Entry"]'); // Test touch interactions on keypad buttons await page.tap('button:text("1")'); await page.tap('button:text("2")'); await page.tap('button:text("3")'); await page.tap('button:text("4")'); // Verify touch input works await expect(page.locator('div').filter({ hasText: /1234/ }).first()).toBeVisible(); }); test('should handle offline mode with manual entry', async ({ page }) => { // Simulate offline mode await page.route('**/*', (route) => { if (route.request().url().includes('api')) { route.abort('failed'); } else { route.continue(); } }); await page.click('button[title="Manual Entry"]'); // Enter valid backup code const backupCode = '1234ABCD'; for (const char of backupCode) { await page.keyboard.press(char); } // Submit should work even offline await page.click('button:text("Submit")'); // Should show offline acceptance or queued status await expect( page.locator('text=Accepted (offline mode), text=Queued for verification').first() ).toBeVisible({ timeout: 5000 }); }); });