This commit is contained in:
2025-12-01 17:27:56 +01:00
parent e7ada8aa23
commit 98a09a3f2b
41 changed files with 2341 additions and 1525 deletions

View File

@@ -0,0 +1,162 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { promises as fs } from 'fs';
import path from 'path';
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 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 any[];
const sidebarItem =
items.find(
(i) =>
i.i === 'wizard-sidebar-link-set-session-information' &&
typeof i.t === 'string',
) ?? null;
if (sidebarItem) {
fixtureSidebarText = sidebarItem.t as string;
}
} 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,
);
});