104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { DIContainer } from '../../../..//apps/companion/main/di-container';
|
|
import type { HostedSessionConfig } from '@gridpilot/automation/domain/types/HostedSessionConfig';
|
|
import { StepId } from '@gridpilot/automation/domain/value-objects/StepId';
|
|
import { PlaywrightAutomationAdapter } from '../../../..//core/automation/infrastructure//automation';
|
|
|
|
describe('companion start automation - browser not connected at step 1', () => {
|
|
const originalEnv = { ...process.env };
|
|
let originalTestLauncher: unknown;
|
|
|
|
beforeEach(() => {
|
|
process.env = { ...originalEnv, NODE_ENV: 'production' };
|
|
|
|
originalTestLauncher = (PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
|
|
testLauncher?: unknown;
|
|
}).testLauncher;
|
|
|
|
const mockLauncher = {
|
|
launch: async (_opts: unknown) => ({
|
|
newContext: async () => ({
|
|
newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
|
|
close: async () => {},
|
|
}),
|
|
newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
|
|
close: async () => {},
|
|
}),
|
|
launchPersistentContext: async (_userDataDir: string, _opts: unknown) => ({
|
|
pages: () => [{ setDefaultTimeout: () => {}, close: async () => {} }],
|
|
newPage: async () => ({ setDefaultTimeout: () => {}, close: async () => {} }),
|
|
close: async () => {},
|
|
}),
|
|
};
|
|
|
|
(PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
|
|
testLauncher?: typeof mockLauncher;
|
|
}).testLauncher = mockLauncher;
|
|
|
|
DIContainer.resetInstance();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
const container = DIContainer.getInstance();
|
|
await container.shutdown();
|
|
DIContainer.resetInstance();
|
|
(PlaywrightAutomationAdapter as typeof PlaywrightAutomationAdapter & {
|
|
testLauncher?: unknown;
|
|
}).testLauncher = originalTestLauncher;
|
|
process.env = originalEnv;
|
|
});
|
|
|
|
it('marks the session as FAILED with Step 1 (LOGIN) browser-not-connected error', async () => {
|
|
const container = DIContainer.getInstance();
|
|
const startAutomationUseCase = container.getStartAutomationUseCase();
|
|
const sessionRepository = container.getSessionRepository();
|
|
const automationEngine = container.getAutomationEngine();
|
|
|
|
const connectionResult = await container.initializeBrowserConnection();
|
|
expect(connectionResult.success).toBe(true);
|
|
|
|
const browserAutomation = container.getBrowserAutomation();
|
|
if (typeof (browserAutomation as { disconnect?: () => Promise<void> }).disconnect === 'function') {
|
|
await (browserAutomation as { disconnect: () => Promise<void> }).disconnect();
|
|
}
|
|
|
|
const config: HostedSessionConfig = {
|
|
sessionName: 'Companion integration browser-not-connected',
|
|
trackId: 'test-track',
|
|
carIds: ['car-1'],
|
|
};
|
|
|
|
const dto = await startAutomationUseCase.execute(config);
|
|
|
|
await automationEngine.executeStep(StepId.create(1), config);
|
|
|
|
const session = await waitForFailedSession(sessionRepository, dto.sessionId);
|
|
expect(session).toBeDefined();
|
|
expect(session!.state!.value).toBe('FAILED');
|
|
const error = session!.errorMessage as string | undefined;
|
|
expect(error).toBeDefined();
|
|
expect(error).toContain('Step 1 (Navigate to Hosted Racing page)');
|
|
expect(error).toContain('Browser not connected');
|
|
});
|
|
});
|
|
|
|
async function waitForFailedSession(
|
|
sessionRepository: { findById: (id: string) => Promise<{ state?: { value?: string }; errorMessage?: unknown } | null> },
|
|
sessionId: string,
|
|
timeoutMs = 5000,
|
|
): Promise<{ state?: { value?: string }; errorMessage?: unknown } | null> {
|
|
const start = Date.now();
|
|
let last: any = null;
|
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
while (true) {
|
|
last = await sessionRepository.findById(sessionId);
|
|
if (last && last.state && last.state.value === 'FAILED') {
|
|
return last;
|
|
}
|
|
if (Date.now() - start >= timeoutMs) {
|
|
return last;
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
} |