Files
emaui/apps/e2e/src/support/workflow.ts
Fistum 1931e69594 Add error context and media files for seafarer registration tests
- Created error context markdown files for failed tests in seafarer registration, detailing validation errors related to profile details.
- Added binary files (PNG, ZIP, WEBM) for test results, including snapshots and traces for debugging purposes.
- Updated test cases to include new error context and media files for better analysis of failures.
2026-08-18 09:20:36 +00:00

100 lines
3.4 KiB
TypeScript

import { APIRequestContext, request } from '@playwright/test';
import { E2E } from '../../playwright.config';
import { OFFICER } from './officer';
/**
* The workflow endpoints, called as the officer.
*
* Used where a test's subject is what an approval *does* — the seafarer
* number, the activated record, the two child applications — rather than which
* buttons produce it. Driving six sections of wizard and a review screen to
* reach a completion effect would make those tests about forms.
*
* Nothing here writes to the database directly. The completion effect runs
* inside the API's approval transaction, so a test that faked the status would
* assert against an approval that never happened.
*/
/** Routes served by the applicant-facing controller rather than the review one. */
const APPLICANT_STEPS = new Set(['submit', 'resubmit']);
async function officerContext(): Promise<APIRequestContext> {
const context = await request.newContext({ baseURL: E2E.apiUrl });
const response = await context.post('/auth/login', {
data: { email: OFFICER.email, password: OFFICER.password },
});
if (!response.ok()) {
throw new Error(
`Officer login failed (${response.status()}): ${await response.text()}`,
);
}
const body = await response.json();
const token = body?.token ?? body?.accessToken ?? body?.access_token;
if (!token) {
throw new Error(`No access token in login response: ${JSON.stringify(body)}`);
}
await context.dispose();
return request.newContext({
baseURL: E2E.apiUrl,
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
});
}
export interface WorkflowStep {
/** Route under the review controller, e.g. `claim`, `final-approve`. */
path: string;
data?: Record<string, unknown>;
/** Set when a step is expected to be refused — the refusal is the assertion. */
expectFailure?: boolean;
}
/**
* Runs a sequence of workflow calls against one application.
*
* Returns each step's status code so a caller can assert on a refusal as
* readily as on a success — "an officer may not evaluate a registration" is a
* result worth checking, not an error to swallow.
*/
export async function runWorkflow(
applicationId: string,
steps: WorkflowStep[],
): Promise<number[]> {
const api = await officerContext();
const codes: number[] = [];
try {
for (const step of steps) {
// Applicant-side actions (`submit`, `resubmit`) live on the
// applications controller; everything an officer does is on the review
// controller. Routing by step keeps callers from having to know.
const base = APPLICANT_STEPS.has(step.path)
? 'license-applications'
: 'license-application-review';
const response = await api.post(
`/${base}/${applicationId}/${step.path}`,
{ data: step.data ?? {} },
);
codes.push(response.status());
if (!step.expectFailure && !response.ok()) {
throw new Error(
`Step "${step.path}" failed (${response.status()}): ${await response.text()}`,
);
}
}
} finally {
await api.dispose();
}
return codes;
}
/** Claim then final-approve — the whole officer path for a registration. */
export async function approveRegistration(applicationId: string): Promise<void> {
await runWorkflow(applicationId, [
{ path: 'claim' },
{ path: 'final-approve', data: { remark: 'E2E approval' } },
]);
}