89 lines
3.1 KiB
TypeScript
89 lines
3.1 KiB
TypeScript
import { expect, type Page, test } from '@playwright/test';
|
|
|
|
/**
|
|
* Product-workflow persistence e2e (Workstream 8). Proves persisted product
|
|
* workflows survive across requests (the "sees it after reload" guarantee),
|
|
* backed by the real backend rather than mock data: a director posts a FRAME
|
|
* entry and reads it back, and a staff member marks progress and reads it back.
|
|
*
|
|
* Requires the backend running with the database migrated + seeded, and the
|
|
* seed passwords in the environment.
|
|
*/
|
|
const USER_PASSWORD = 'flatlogicUser123!';
|
|
|
|
const BACKEND_API_URL =
|
|
process.env.VITE_BACKEND_API_URL || 'http://localhost:8080/api';
|
|
|
|
const DIRECTOR_EMAIL = 'director@flatlogic.com';
|
|
const TEACHER_EMAIL = 'teacher@flatlogic.com';
|
|
|
|
interface FrameRow {
|
|
readonly id: string;
|
|
readonly author: string;
|
|
}
|
|
interface ProgressRow {
|
|
readonly item_id: string;
|
|
readonly value: string | null;
|
|
}
|
|
|
|
async function login(page: Page, email: string, password: string): Promise<void> {
|
|
await page.goto('/login');
|
|
await page.getByPlaceholder('you@school.edu').fill(email);
|
|
await page.getByPlaceholder('Enter your password').fill(password);
|
|
await page.getByRole('button', { name: 'Sign In', exact: true }).click();
|
|
await page.waitForURL((url) => !url.pathname.startsWith('/login'), {
|
|
timeout: 10_000,
|
|
});
|
|
}
|
|
|
|
test.describe('Product-workflow persistence', () => {
|
|
test('a director posts a FRAME entry and reads it back', async ({ page }) => {
|
|
await login(page, DIRECTOR_EMAIL, USER_PASSWORD);
|
|
|
|
const author = `E2E Director ${Date.now()}`;
|
|
const createRes = await page.request.post(`${BACKEND_API_URL}/frame_entries`, {
|
|
data: {
|
|
data: {
|
|
week_of: '2026-06-01',
|
|
posted_date: '2026-06-02',
|
|
formal: 'Formal note',
|
|
recognition: 'Recognition note',
|
|
application: 'Application note',
|
|
management: 'Management note',
|
|
emotional: 'Emotional note',
|
|
author,
|
|
},
|
|
},
|
|
});
|
|
expect(createRes.ok()).toBe(true);
|
|
|
|
// Re-fetch (fresh request) and confirm the entry persisted.
|
|
const listRes = await page.request.get(`${BACKEND_API_URL}/frame_entries`);
|
|
expect(listRes.status()).toBe(200);
|
|
const body = (await listRes.json()) as { rows?: FrameRow[] };
|
|
expect((body.rows ?? []).some((row) => row.author === author)).toBe(true);
|
|
});
|
|
|
|
test('a staff member marks a sign learned and progress persists', async ({
|
|
page,
|
|
}) => {
|
|
await login(page, TEACHER_EMAIL, USER_PASSWORD);
|
|
|
|
const itemId = `sign-${Date.now()}`;
|
|
const upsertRes = await page.request.post(`${BACKEND_API_URL}/user_progress`, {
|
|
data: {
|
|
data: { progress_type: 'sign_learned', item_id: itemId, value: 'learned' },
|
|
},
|
|
});
|
|
expect(upsertRes.ok()).toBe(true);
|
|
|
|
const listRes = await page.request.get(
|
|
`${BACKEND_API_URL}/user_progress?progress_type=sign_learned&item_id=${itemId}`,
|
|
);
|
|
expect(listRes.status()).toBe(200);
|
|
const body = (await listRes.json()) as { rows?: ProgressRow[] };
|
|
const saved = (body.rows ?? []).find((row) => row.item_id === itemId);
|
|
expect(saved, 'the marked progress must persist').toBeTruthy();
|
|
});
|
|
});
|