import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { promises as fs } from 'fs'; import path from 'path'; import { StepId } from 'apps/companion/main/automation/domain/value-objects/StepId'; import { PlaywrightAutomationAdapter, } from 'core/automation/infrastructure//automation'; import { IRACING_SELECTORS, IRACING_TIMEOUTS, } from 'core/automation/infrastructure//automation/dom/IRacingSelectors'; import { PinoLogAdapter } from 'core/automation/infrastructure//logging/PinoLogAdapter'; const shouldRun = process.env.HOSTED_REAL_E2E === '1'; const describeMaybe = shouldRun ? describe : describe.skip; describeMaybe('Real-site hosted session – Race Information step (members.iracing.com)', () => { let adapter: PlaywrightAutomationAdapter; beforeAll(async () => { const logger = new PinoLogAdapter(); adapter = new PlaywrightAutomationAdapter( { headless: true, timeout: IRACING_TIMEOUTS.navigation, mode: 'real', baseUrl: '', userDataDir: '', }, logger, ); const result = await adapter.connect(false); expect(result.success).toBe(true); expect(adapter.isConnected()).toBe(true); const step1Result = await adapter.executeStep(StepId.create(1), {}); expect(step1Result.success).toBe(true); const step2Result = await adapter.executeStep(StepId.create(2), {}); expect(step2Result.success).toBe(true); const page = adapter.getPage(); expect(page).not.toBeNull(); const createRaceButton = page! .locator(IRACING_SELECTORS.hostedRacing.createRaceButton) .first(); await expect( createRaceButton.count(), 'Create Race button should exist on Hosted Racing page', ).resolves.toBeGreaterThan(0); await createRaceButton.click({ timeout: IRACING_TIMEOUTS.elementWait }); const raceInfoContainer = page! .locator(IRACING_SELECTORS.wizard.stepContainers.raceInformation) .first(); await raceInfoContainer.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); expect(await raceInfoContainer.count()).toBeGreaterThan(0); }, 300_000); afterAll(async () => { if (adapter) { await adapter.disconnect(); } }); it( 'shows Race Information sidebar text matching fixtures and keeps text inputs writable', async () => { const page = adapter.getPage(); expect(page).not.toBeNull(); const sidebarLink = page! .locator(IRACING_SELECTORS.wizard.sidebarLinks.raceInformation) .first(); await sidebarLink.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); const sidebarText = (await sidebarLink.innerText()).trim(); expect(sidebarText.length).toBeGreaterThan(0); let fixtureSidebarText: string | null = null; try { const fixturePath = path.join( process.cwd(), 'html-dumps-optimized', 'iracing-hosted-sessions', '03-race-information.json', ); const raw = await fs.readFile(fixturePath, 'utf8'); const items = JSON.parse(raw) as Array<{ i: string; t: string }>; const sidebarItem = items.find( (i) => i.i === 'wizard-sidebar-link-set-session-information' && typeof i.t === 'string', ) ?? null; if (sidebarItem) { fixtureSidebarText = sidebarItem.t; } } catch { fixtureSidebarText = null; } if (fixtureSidebarText) { const expected = fixtureSidebarText.toLowerCase(); const actual = sidebarText.toLowerCase(); expect( actual.includes('race') || actual.includes(expected.slice(0, 4)), ).toBe(true); } const config = { sessionName: 'GridPilot Real – Race Information', password: 'real-site-secret', description: 'Real-site Race Information writable fields check', }; const result = await adapter.executeStep(StepId.create(3), config); expect(result.success).toBe(true); const sessionNameInput = page! .locator(IRACING_SELECTORS.steps.sessionName) .first(); const passwordInput = page! .locator(IRACING_SELECTORS.steps.password) .first(); const descriptionInput = page! .locator(IRACING_SELECTORS.steps.description) .first(); await sessionNameInput.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); await passwordInput.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); await descriptionInput.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); const sessionNameValue = await sessionNameInput.inputValue(); const passwordValue = await passwordInput.inputValue(); const descriptionValue = await descriptionInput.inputValue(); expect(sessionNameValue).toBe(config.sessionName); expect(passwordValue).toBe(config.password); expect(descriptionValue).toBe(config.description); }, 300_000, ); });