147 lines
5.1 KiB
TypeScript
147 lines
5.1 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { DIContainer } from '../../../apps/companion/main/di-container';
|
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
|
import { PlaywrightAutomationAdapter } from 'core/automation/infrastructure//automation';
|
|
|
|
describe('Companion UI - hosted workflow via fixture-backed real stack', () => {
|
|
let container: DIContainer;
|
|
let adapter: PlaywrightAutomationAdapter;
|
|
let sessionId: string;
|
|
let originalEnv: string | undefined;
|
|
let originalFixtureFlag: string | undefined;
|
|
|
|
beforeAll(async () => {
|
|
originalEnv = process.env.NODE_ENV;
|
|
originalFixtureFlag = process.env.COMPANION_FIXTURE_HOSTED;
|
|
Object.defineProperty(process.env, 'NODE_ENV', {
|
|
value: 'test',
|
|
writable: true,
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
process.env.COMPANION_FIXTURE_HOSTED = '1';
|
|
|
|
DIContainer.resetInstance();
|
|
container = DIContainer.getInstance();
|
|
|
|
const connection = await container.initializeBrowserConnection();
|
|
expect(connection.success).toBe(true);
|
|
|
|
const browserAutomation = container.getBrowserAutomation();
|
|
expect(browserAutomation).toBeInstanceOf(PlaywrightAutomationAdapter);
|
|
adapter = browserAutomation as PlaywrightAutomationAdapter;
|
|
expect(adapter.isConnected()).toBe(true);
|
|
expect(adapter.getPage()).not.toBeNull();
|
|
}, 120000);
|
|
|
|
afterAll(async () => {
|
|
await container.shutdown();
|
|
Object.defineProperty(process.env, 'NODE_ENV', {
|
|
value: originalEnv,
|
|
writable: true,
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
process.env.COMPANION_FIXTURE_HOSTED = originalFixtureFlag;
|
|
});
|
|
|
|
async function waitForFinalSession(deadlineMs: number) {
|
|
const repo = container.getSessionRepository();
|
|
const deadline = Date.now() + deadlineMs;
|
|
let finalSession = null;
|
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
while (true) {
|
|
const sessions = await repo.findAll();
|
|
finalSession = sessions[0] ?? null;
|
|
|
|
if (finalSession && (finalSession.state.isStoppedAtStep18() || finalSession.state.isCompleted())) {
|
|
break;
|
|
}
|
|
|
|
if (Date.now() > deadline) {
|
|
throw new Error('Timed out waiting for hosted workflow to complete via companion DI stack');
|
|
}
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
}
|
|
|
|
return finalSession;
|
|
}
|
|
|
|
it(
|
|
'drives AutomationEngineAdapter via DI over fixtures and shows overlay progress',
|
|
async () => {
|
|
const startUseCase = container.getStartAutomationUseCase();
|
|
const repo = container.getSessionRepository();
|
|
|
|
const config: HostedSessionConfig = {
|
|
sessionName: 'Companion E2E - fixture hosted workflow',
|
|
serverName: 'Companion Fixture Server',
|
|
password: 'companion',
|
|
adminPassword: 'admin-companion',
|
|
maxDrivers: 20,
|
|
trackId: 'spa',
|
|
carIds: ['dallara-f3'],
|
|
weatherType: 'dynamic',
|
|
timeOfDay: 'afternoon',
|
|
sessionDuration: 60,
|
|
practiceLength: 10,
|
|
qualifyingLength: 10,
|
|
warmupLength: 5,
|
|
raceLength: 30,
|
|
startType: 'standing',
|
|
restarts: 'single-file',
|
|
damageModel: 'realistic',
|
|
trackState: 'auto'
|
|
};
|
|
|
|
const dto = await startUseCase.execute(config);
|
|
expect(dto.state).toBe('PENDING');
|
|
expect(dto.currentStep).toBe(1);
|
|
sessionId = dto.sessionId;
|
|
|
|
const session = await repo.findById(sessionId);
|
|
expect(session).not.toBeNull();
|
|
expect(session!.state.isPending()).toBe(true);
|
|
|
|
await adapter.navigateToPage('http://localhost:3456/');
|
|
const engine = container.getAutomationEngine();
|
|
await engine.executeStep(StepId.create(1), config);
|
|
|
|
const page = adapter.getPage();
|
|
expect(page).not.toBeNull();
|
|
|
|
await page!.waitForSelector('#gridpilot-overlay', { state: 'attached', timeout: 30000 });
|
|
const startingText = await page!.textContent('#gridpilot-action');
|
|
expect(startingText ?? '').not.toEqual('');
|
|
|
|
let reachedStep7OrBeyond = false;
|
|
|
|
const deadlineForProgress = Date.now() + 60000;
|
|
while (Date.now() < deadlineForProgress) {
|
|
const updated = await repo.findById(sessionId);
|
|
if (updated && updated.currentStep.value >= 7) {
|
|
reachedStep7OrBeyond = true;
|
|
break;
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
}
|
|
|
|
expect(reachedStep7OrBeyond).toBe(true);
|
|
|
|
const overlayStepText = await page!.textContent('#gridpilot-step-text');
|
|
const overlayBody = (overlayStepText ?? '').trim().toLowerCase();
|
|
expect(overlayBody.length).toBeGreaterThan(0);
|
|
|
|
const finalSession = await waitForFinalSession(60000);
|
|
expect(finalSession.state.isStoppedAtStep18() || finalSession.state.isCompleted()).toBe(true);
|
|
expect(finalSession.errorMessage).toBeUndefined();
|
|
|
|
const progressState = finalSession.state.value;
|
|
expect(['STOPPED_AT_STEP_18', 'COMPLETED']).toContain(progressState);
|
|
},
|
|
180000
|
|
);
|
|
}); |