import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { StepId } from 'packages/domain/value-objects/StepId'; import { PlaywrightAutomationAdapter, } from 'packages/infrastructure/adapters/automation'; import { IRACING_SELECTORS, IRACING_TIMEOUTS, } from 'packages/infrastructure/adapters/automation/dom/IRacingSelectors'; import { PinoLogAdapter } from 'packages/infrastructure/adapters/logging/PinoLogAdapter'; const shouldRun = process.env.HOSTED_REAL_E2E === '1'; const describeMaybe = shouldRun ? describe : describe.skip; describeMaybe('Real-site hosted session smoke – login and wizard entry (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); }, 180_000); afterAll(async () => { if (adapter) { await adapter.disconnect(); } }); it( 'logs in, reaches Hosted Racing, and opens Create Race wizard', async () => { const step1Result = await adapter.executeStep(StepId.create(1), {}); expect(step1Result.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); const hostedTab = page! .locator(IRACING_SELECTORS.hostedRacing.hostedTab) .first(); await hostedTab.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); const step2Result = await adapter.executeStep(StepId.create(2), {}); expect(step2Result.success).toBe(true); const modalSelector = IRACING_SELECTORS.hostedRacing.createRaceModal; const modal = page!.locator(modalSelector).first(); await modal.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); const newRaceButton = page! .locator(IRACING_SELECTORS.hostedRacing.newRaceButton) .first(); await newRaceButton.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); await newRaceButton.click({ timeout: IRACING_TIMEOUTS.elementWait }); const raceInfoContainer = page! .locator(IRACING_SELECTORS.wizard.stepContainers.raceInformation) .first(); await raceInfoContainer.waitFor({ state: 'attached', timeout: IRACING_TIMEOUTS.elementWait, }); const modalContent = await page! .locator(IRACING_SELECTORS.wizard.modalContent) .first() .count(); expect( modalContent, 'Race creation wizard modal content should be present', ).toBeGreaterThan(0); }, 300_000, ); it( 'detects login guard and does not attempt Create a Race when not authenticated', async () => { const step1Result = await adapter.executeStep(StepId.create(1), {}); expect(step1Result.success).toBe(true); const page = adapter.getPage(); expect(page).not.toBeNull(); const currentUrl = page!.url(); expect(currentUrl).not.toEqual('about:blank'); expect(currentUrl.toLowerCase()).toContain('iracing'); expect(currentUrl.toLowerCase()).toSatisfy((u: string) => u.includes('oauth.iracing.com') || u.includes('members.iracing.com') || u.includes('/login'), ); const emailInput = page! .locator(IRACING_SELECTORS.login.emailInput) .first(); const passwordInput = page! .locator(IRACING_SELECTORS.login.passwordInput) .first(); const hasEmail = (await emailInput.count()) > 0; const hasPassword = (await passwordInput.count()) > 0; if (!hasEmail && !hasPassword) { return; } await emailInput.waitFor({ state: 'visible', timeout: IRACING_TIMEOUTS.elementWait, }); await passwordInput.waitFor({ state: 'visible', timeout: IRACING_TIMEOUTS.elementWait, }); const createRaceButton = page! .locator(IRACING_SELECTORS.hostedRacing.createRaceButton) .first(); const createRaceCount = await createRaceButton.count(); expect(createRaceCount).toBe(0); }, 300_000, ); });