159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
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,
|
||
);
|
||
}); |