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); }); });