import { test, expect } from '@playwright/test'; import { ae_app_local_data_defaults } from './_helpers/ae_defaults'; import { testing_event_id, testing_account_id } from './_helpers/env'; const event_id = testing_event_id; const event_badge_id = 'UIJT-73-63-61'; // Per README test data test.describe('Cold-start: Event Badges List (IndexedDB empty)', () => { test.beforeEach(async ({ page }) => { page.on('pageerror', (err) => console.error(`BROWSER ERROR: ${err.message}`)); page.on('console', (msg) => { if (msg.type() === 'error' || msg.type() === 'warning') console.error(`BROWSER [${msg.type().toUpperCase()}]: ${msg.text()}`); }); // Set up localStorage with manager access await page.addInitScript( ({ defaults, event_id, account_id }) => { const testData = { ...defaults, account_id: account_id, manager_access: true, authenticated_access: true, trusted_access: true, edit_mode: false, person_id: 'HMQRNPIXQMK', // Per README test data user: { id: 'HMQRNPIXQMK' }, // Per README test data mod: { ...defaults.mod, events: { ...defaults.mod.events, event_id: event_id } } }; window.localStorage.setItem('ae_loc', JSON.stringify(testData)); }, { defaults: ae_app_local_data_defaults, event_id: event_id, account_id: testing_account_id } ); // Navigate and clear all Dexie databases to simulate cold start await page.goto('/'); await page.evaluate(() => { const dbs = [ 'ae_events_db', 'ae_journals_db', 'ae_posts_db', 'ae_archives_db', 'ae_core_db', 'ae_sponsorships_db' ]; return Promise.all( dbs.map((name) => new Promise((resolve) => { try { const req = indexedDB.deleteDatabase(name); req.onsuccess = () => resolve(true); req.onerror = () => resolve(false); req.onblocked = () => resolve(false); } catch (e) { resolve(false); } }) ) ); }); // Seed the event record in IDB so liveQuery finds it await page.evaluate(({ event_id }) => { return new Promise((resolve) => { try { const req = indexedDB.open('ae_events_db', 1); req.onupgradeneeded = (ev) => { const db = (ev.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains('event')) { db.createObjectStore('event', { keyPath: 'id' }); } }; req.onsuccess = () => { const db = req.result; const tx = db.transaction('event', 'readwrite'); const store = tx.objectStore('event'); store.put({ id: event_id, event_id: event_id, name: 'Cold Start Badge Test Event', cfg_json: {}, mod_pres_mgmt_json: {}, mod_badges_json: {}, mod_abstracts_json: {}, mod_exhibits_json: {}, mod_meetings_json: {} }); tx.oncomplete = () => { db.close(); resolve(); }; tx.onerror = () => { db.close(); resolve(); }; }; req.onerror = () => resolve(); } catch (e) { resolve(); } }); }, { event_id }); // Mock V3 API responses await page.route('**/v3/**', async (route) => { const req = route.request(); const url = req.url(); const method = req.method(); // Event GET if (url.includes(`/v3/crud/event/${event_id}`) && method === 'GET') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: { id: event_id, event_id: event_id, name: 'Cold Start Badge Test Event', cfg_json: {}, mod_pres_mgmt_json: {}, mod_badges_json: {}, mod_abstracts_json: {}, mod_exhibits_json: {}, mod_meetings_json: {} } }) }); } // Badge search/list - return enriched data like real API does if (url.includes(`/v3/crud/event/${event_id}/event_badge/search`) && method === 'POST') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [ { id: event_badge_id, event_badge_id: event_badge_id, event_id: event_id, event_badge_template_id: 'jgfixEpYp1B', // Per README test data full_name: 'Scott Idem', // Computed from given_name + family_name full_name_override: 'Scott Idem', given_name: 'Scott', family_name: 'Idem', email: 'scott@oneskyit.com', badge_type: 'attendee', badge_type_code: 'attendee', person_id: 'ffkKxiHpOEC', // Per README test data // Enriched fields from API joins person_given_name: 'Scott', person_family_name: 'Idem', person_email: 'scott@oneskyit.com', event_name: 'Demo One Sky IT Conference', badge_template_name: 'Dev Demo 202x', enable: true, hide: false, priority: 0, sort: 0, created_on: '2026-02-01T10:00:00Z', updated_on: '2026-02-20T15:30:00Z', tmp_sort_1: '0_0_0_2026-02-01T10:00:00Z', tmp_sort_2: '0_0_0__2026-02-01T10:00:00Z' } ] }) }); } // Badge template GET if (url.includes('/v3/crud/event_badge_template/') && method === 'GET') { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: { id: 'jgfixEpYp1B', event_badge_template_id: 'jgfixEpYp1B', // Per README test data event_id: event_id, name: 'Dev Demo 202x', header_path: '/images/demo-header.png', logo_path: '/images/demo-logo.png', show_qr_front: true, show_qr_back: true } }) }); } // Site domain search (app init) if (url.includes('site_domain/search')) { return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [{ id: 'td', site_id: 'ts' }] }) }); } // Default: empty response return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [] }) }); }); }); test('renders Badge list with enriched data on first load (empty IDB)', async ({ page }) => { await page.goto(`/events/${event_id}/badges`); // Wait for API call to complete await page.waitForResponse((r) => r.url().includes('event_badge/search') && r.status() === 200, { timeout: 10000 } ); // Verify search component loads (the badge page has a search form) const search_form = page.locator('form, [class*="search"], input[type="search"], input[placeholder*="search" i]'); await expect(search_form.first()).toBeVisible({ timeout: 10000 }); // Verify badge data displays (look for name or email in the page) await expect(page.locator('text=/Scott Idem/i')).toBeVisible({ timeout: 10000 }); // Verify no "undefined" or "NaN" appears anywhere on page const page_content = await page.textContent('body'); expect(page_content).not.toContain('undefined'); expect(page_content).not.toContain('NaN undefined'); }); test('verifies IndexedDB contains badge after cold-start load', async ({ page }) => { await page.goto(`/events/${event_id}/badges`); await page.waitForResponse((r) => r.url().includes('event_badge/search') && r.status() === 200, { timeout: 10000 } ); // Give the app time to process and save to IDB await page.waitForTimeout(1000); // Check IDB for badge record const idb_check = await page.evaluate(async ({ event_id, event_badge_id }) => { return new Promise((resolve) => { try { const req = indexedDB.open('ae_events_db'); req.onsuccess = () => { const db = req.result; const tx = db.transaction('badge', 'readonly'); const store = tx.objectStore('badge'); const get_req = store.get(event_badge_id); get_req.onsuccess = () => { const badge = get_req.result; db.close(); resolve({ found: !!badge, has_full_name: badge?.full_name_override === 'Scott Idem', has_email: badge?.email === 'scott@oneskyit.com', has_event_id: badge?.event_id === event_id }); }; get_req.onerror = () => { db.close(); resolve({ found: false }); }; }; req.onerror = () => resolve({ found: false }); } catch (e) { resolve({ found: false }); } }); }, { event_id, event_badge_id }) as any; expect(idb_check.found).toBe(true); expect(idb_check.has_full_name).toBe(true); expect(idb_check.has_email).toBe(true); expect(idb_check.has_event_id).toBe(true); }); test('handles badge template relationship correctly', async ({ page }) => { await page.goto(`/events/${event_id}/badges`); await page.waitForResponse((r) => r.url().includes('event_badge/search') && r.status() === 200, { timeout: 10000 } ); // Badge detail view test - navigate to specific badge await page.goto(`/events/${event_id}/badges/${event_badge_id}`); // Wait for badge view to load await page.waitForTimeout(2000); // Verify badge content renders without errors (check for name or key identifying info) const badge_page = await page.textContent('body'); expect(badge_page).toBeTruthy(); // Should not crash or show major errors expect(badge_page).not.toContain('Cannot read'); expect(badge_page).not.toContain('is not defined'); }); });