162 lines
5.1 KiB
TypeScript
162 lines
5.1 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||
import { promises as fs } from 'fs';
|
||
import path from 'path';
|
||
import { StepId } from '@gridpilot/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,
|
||
);
|
||
}); |