mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 01:22:49 +00:00
100 lines
3.7 KiB
TypeScript
100 lines
3.7 KiB
TypeScript
import { Page, expect } from '@playwright/test';
|
|
import { logOffset, waitForOtp } from './api-log';
|
|
|
|
/**
|
|
* A new applicant, unique to this run.
|
|
*
|
|
* Every spec makes its own. Sharing one account between specs means the second
|
|
* run of the suite starts from a state the first run left behind — an
|
|
* applicant who already declared their operations, already holds a licence —
|
|
* and the assertions quietly stop meaning what they say.
|
|
*/
|
|
export interface Applicant {
|
|
email: string;
|
|
username: string;
|
|
phoneNumber: string;
|
|
password: string;
|
|
name: string;
|
|
}
|
|
|
|
export function newApplicant(label: string): Applicant {
|
|
const stamp = `${Date.now()}${Math.floor(Math.random() * 1000)}`;
|
|
return {
|
|
email: `e2e.${label}.${stamp}@example.test`,
|
|
username: `e2e${label}${stamp}`.slice(0, 28),
|
|
// Ethiopian mobile format; the last digits vary so two runs never collide.
|
|
phoneNumber: `+2519${stamp.slice(-8)}`,
|
|
password: 'E2ePassw0rd!',
|
|
name: `E2E ${label} ${stamp.slice(-4)}`,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fills and submits the signup form.
|
|
*
|
|
* Returns the log offset taken immediately before submitting, so the caller can
|
|
* pick up the one-time code that submission triggers without matching a stale
|
|
* one from an earlier test.
|
|
*/
|
|
export async function signUp(page: Page, applicant: Applicant): Promise<number> {
|
|
await page.goto('/signup');
|
|
|
|
await page.getByLabel('Name (English)').fill(applicant.name);
|
|
await page.getByLabel('Email address').fill(applicant.email);
|
|
await page.getByLabel('Username').fill(applicant.username);
|
|
await page.getByLabel('Phone number').fill(applicant.phoneNumber);
|
|
await page.getByLabel('Password', { exact: true }).fill(applicant.password);
|
|
await page.getByLabel('Confirm password').fill(applicant.password);
|
|
|
|
// The terms checkbox gates the submit button.
|
|
await page.getByRole('checkbox').check();
|
|
|
|
const offset = logOffset();
|
|
await page.getByRole('button', { name: /create account|sign up/i }).click();
|
|
return offset;
|
|
}
|
|
|
|
/**
|
|
* Completes phone verification by typing the real code.
|
|
*
|
|
* No-op when signup did not land on the OTP screen — whether it does depends
|
|
* on how the account was created, and a test that asserts the flow rather than
|
|
* the screen should not care.
|
|
*/
|
|
export async function verifyOtpIfPrompted(
|
|
page: Page,
|
|
sinceOffset: number,
|
|
): Promise<boolean> {
|
|
await page.waitForURL(/\/(otp-verify|onboarding|dashboard)/, { timeout: 30_000 });
|
|
if (!page.url().includes('/otp-verify')) return false;
|
|
|
|
const code = await waitForOtp(sinceOffset);
|
|
|
|
// Mantine's PinInput is one input per character and moves focus itself, so
|
|
// the code is typed rather than filled — filling each box individually
|
|
// fights the component's own focus handling.
|
|
const boxes = page.locator('input[inputmode], input[type="text"]');
|
|
await boxes.first().click();
|
|
await page.keyboard.type(code, { delay: 30 });
|
|
|
|
// `onComplete` submits on the last character; the button is the fallback for
|
|
// when it does not fire.
|
|
const submit = page.getByRole('button', { name: /^verify$/i });
|
|
await Promise.race([
|
|
page.waitForURL(/\/(onboarding|dashboard)/, { timeout: 10_000 }).catch(() => null),
|
|
submit.click({ timeout: 5_000 }).catch(() => null),
|
|
]);
|
|
return true;
|
|
}
|
|
|
|
/** Signs in an existing applicant through the login screen. */
|
|
export async function logIn(page: Page, applicant: Applicant): Promise<void> {
|
|
await page.goto('/login');
|
|
// The API takes an email here, not a username — sending a username answers
|
|
// "email should not be empty".
|
|
await page.getByLabel(/email/i).fill(applicant.email);
|
|
await page.getByLabel(/password/i).fill(applicant.password);
|
|
await page.getByRole('button', { name: /sign in|log in|login/i }).click();
|
|
await expect(page).toHaveURL(/\/(onboarding|dashboard|profile)/, { timeout: 30_000 });
|
|
}
|