Files
emaui/apps/e2e/src/onboarding.spec.ts
2026-08-03 12:49:48 +03:00

103 lines
3.5 KiB
TypeScript

import { test, expect } from '@playwright/test';
import {
Applicant,
newApplicant,
signUp,
verifyOtpIfPrompted,
} from './support/applicant';
import { deleteApplicant, sql } from './support/db';
/**
* Signing up, and being asked what you operate as before anything else.
*
* The gate is the whole point: the catalogue is filtered by the answer and the
* API refuses an application for a mode the profile does not hold, so an
* applicant who skipped this would reach a dashboard that offers them nothing
* and explains nothing.
*/
test.describe('signup and onboarding', () => {
let applicant: Applicant;
test.beforeEach(() => {
applicant = newApplicant('onboard');
});
test.afterEach(() => {
deleteApplicant(applicant.email);
});
test('a new applicant is asked for their operations before reaching the dashboard', async ({
page,
}) => {
const offset = await signUp(page, applicant);
await verifyOtpIfPrompted(page, offset);
// The account exists in IAM ...
const rows = sql(
`SELECT email FROM iam.users WHERE email = '${applicant.email}'`,
);
expect(rows).toHaveLength(1);
// ... and the first thing behind the session is the question, not the app.
await expect(page).toHaveURL(/\/onboarding\/operations/, { timeout: 30_000 });
await expect(
page.getByRole('heading', { name: /what do you operate as/i }),
).toBeVisible();
// The dashboard stays out of reach until it is answered.
await page.goto('/dashboard');
await expect(page).toHaveURL(/\/onboarding\/operations/);
// Answer it.
await page
.getByRole('checkbox', { name: /freight forwarder license/i })
.first()
.check();
await page.getByRole('button', { name: /save operations/i }).click();
await expect(page).toHaveURL(/\/dashboard/, { timeout: 30_000 });
// And it stuck: stored against the profile, keyed by the IAM user id.
const declared = sql(`
SELECT lt.key
FROM profile_operator_types pot
JOIN profiles p ON p.id = pot.profile_id
JOIN iam.users u ON u.id = p.user_id
JOIN license_types lt ON lt.id = pot.license_type_id
WHERE u.email = '${applicant.email}' AND pot.deleted_at IS NULL
`);
expect(declared.map((r) => r[0])).toEqual(['FREIGHT_FORWARDER']);
});
test('the dashboard leads with a get-started panel when there is nothing to show', async ({
page,
}) => {
const offset = await signUp(page, applicant);
await verifyOtpIfPrompted(page, offset);
await expect(page).toHaveURL(/\/onboarding\/operations/, { timeout: 30_000 });
await page
.getByRole('checkbox', { name: /freight forwarder license/i })
.first()
.check();
await page.getByRole('button', { name: /save operations/i }).click();
await expect(page).toHaveURL(/\/dashboard/, { timeout: 30_000 });
// Nothing filed and nothing held: one panel, not two empty sections.
await expect(page.getByRole('heading', { name: 'Get started' })).toBeVisible();
await expect(
page.getByRole('heading', { name: 'My applications' }),
).toHaveCount(0);
await expect(page.getByRole('heading', { name: 'My licences' })).toHaveCount(
0,
);
// The catalogue is directly beneath it, filtered to what they declared.
await expect(
page.getByRole('heading', { name: /apply for a licence/i }),
).toBeVisible();
await expect(page.getByText('Freight Forwarder License').first()).toBeVisible();
await expect(page.getByText('Shipping Agent License')).toHaveCount(0);
});
});