From 6c160530fa21eb62d56f1bbc06510858f15bbf9e Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Tue, 28 Apr 2026 05:37:08 +0000 Subject: [PATCH] Add e2e tests for the order placement and cancellation flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original task (#65): Persist coverage for the new "Order" + "My Orders" flow with a Playwright spec under artifacts/teaboy-os/tests/ that places an order, sees the "Awaiting receiver" pill in /my-orders, cancels it, and verifies the red cancelled pill replaces the timeline and the cancel button disappears. Both Arabic and English label paths must be exercised, and the spec must run under `pnpm --filter @workspace/teaboy-os test:e2e`. Implementation - Added artifacts/tx-os/tests/order-place-cancel.spec.mjs (the artifact is now `tx-os`; `teaboy-os` no longer exists, so the spec lives in the same tests directory as `leave-group-successor.spec.mjs` and is picked up automatically by the existing test:e2e Playwright config). - Style mirrors leave-group-successor.spec.mjs: file-scoped DB pool, per-test seeded user with the `user` role, after-all cleanup that deletes the spec's orders, the user's stray orders/notifications, user_roles, and the user itself. - The spec runs the same flow twice, once with English labels and once with Arabic labels, driven through a `placeAndCancelFlow(page, lang, labels)` helper that: 1. Forces the UI language via `page.addInitScript` writing the `tx-lang` localStorage key BEFORE any page script runs. 2. Seeds the test user with `preferred_language` matching `lang`, because AuthContext + login.tsx call `i18n.changeLanguage(user.preferredLanguage)` on login and would otherwise override the localStorage seed. 3. Logs in via the real /login form, navigates to /services, clicks the available service tile (matched by aria-label = localized name), submits the order modal, and waits for the POST /api/orders 201 to capture the order id. 4. Navigates to /my-orders, asserts the first card contains the service name, the pending pill ("Awaiting receiver" / "بانتظار المستلِم"), and 4 timeline step circles. 5. Clicks the in-card Cancel button, confirms via the alertdialog, waits for the PATCH /api/orders/:id/status 200 → cancelled. 6. Asserts the timeline step circles are gone, the centered red cancelled pill ("Cancelled" / "ملغى") is present, and the Cancel button is removed from the card. Verification - `pnpm --filter @workspace/tx-os exec playwright install chromium` - `cd artifacts/tx-os && pnpm exec playwright test --config playwright.config.mjs` → all 6 specs pass (the 4 pre-existing ones + the 2 new EN/AR tests). Deviations - Task referenced the `teaboy-os` artifact path; the actual artifact in this codebase is `tx-os`, so the spec was placed under `artifacts/tx-os/tests/` and registered with the existing `pnpm --filter @workspace/tx-os test:e2e` script (the original `teaboy-os` filter no longer exists). Replit-Task-Id: d2139d0e-9ee0-4795-8f94-29e1cfe4b35a --- artifacts/tx-os/public/opengraph.jpg | Bin 25941 -> 25895 bytes .../tx-os/tests/order-place-cancel.spec.mjs | 287 ++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 artifacts/tx-os/tests/order-place-cancel.spec.mjs diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index f65af7a798458673efdf6a82482f56d08b35da76..4aa3e468764a4d4c680bc2db8621cc826f7bb3d3 100644 GIT binary patch delta 21468 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 U+AR&5jLh=>T;qOx;BWW;4Nudir2qf` delta 21381 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 Xdskd33?cJU>x<~xzIyKG|9$#DE2K(N diff --git a/artifacts/tx-os/tests/order-place-cancel.spec.mjs b/artifacts/tx-os/tests/order-place-cancel.spec.mjs new file mode 100644 index 00000000..cfadc090 --- /dev/null +++ b/artifacts/tx-os/tests/order-place-cancel.spec.mjs @@ -0,0 +1,287 @@ +// UI test for the end-to-end order placement and cancellation flow that lives +// across artifacts/tx-os/src/pages/services.tsx, the order modal in +// artifacts/tx-os/src/components/order-service-modal.tsx, and +// artifacts/tx-os/src/pages/my-orders.tsx. +// +// Mirrors the API-side coverage in +// artifacts/api-server/tests/service-orders.test.mjs but at the UI layer: +// - Logs in as a regular user (seeded via the database). +// - Navigates to /services and clicks an available service tile. +// - Submits the order modal and waits for the POST /api/orders 201 response. +// - Confirms the new order shows up at the top of /my-orders with the +// "Awaiting receiver" pill (pending status). +// - Cancels it via the in-card Cancel button + confirmation dialog and waits +// for the PATCH /api/orders/:id/status 200 response. +// - Asserts the red cancelled pill replaces the timeline (no timeline step +// circles remain) and the Cancel button is gone from the card. +// +// Both Arabic and English label paths are exercised via separate tests; the +// language is forced through localStorage before the page loads. +// +// Run with: +// DATABASE_URL=... pnpm --filter @workspace/tx-os test:e2e +// +// Browser binaries can be installed once with: +// pnpm --filter @workspace/tx-os exec playwright install chromium +// +// The test seeds its own user + uses an existing available service, then +// cleans up the user, their orders, and any notifications it produced. + +import { test, expect } from "@playwright/test"; +import pg from "pg"; + +const DATABASE_URL = process.env.DATABASE_URL; +if (!DATABASE_URL) { + throw new Error( + "DATABASE_URL must be set to run the order placement / cancellation UI test", + ); +} + +// Same convention as artifacts/api-server/tests/service-orders.test.mjs: +// a precomputed bcrypt hash of the literal password "TestPass123!". +const TEST_PASSWORD = "TestPass123!"; +const TEST_PASSWORD_HASH = + "$2b$10$Bs636ukPMyz01nKrsi.5m.JlDXSN22AVCvn8cgPWWDbo5yJRQX2vu"; + +const pool = new pg.Pool({ connectionString: DATABASE_URL }); + +const createdUserIds = []; +const createdOrderIds = []; + +function uniqueSuffix() { + return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; +} + +async function createUser(prefix, preferredLanguage = "en") { + const username = `${prefix}_${uniqueSuffix()}`; + const { rows } = await pool.query( + `INSERT INTO users (username, email, password_hash, display_name_en, preferred_language, is_active) + VALUES ($1, $2, $3, 'Order UI Test', $4, true) RETURNING id`, + [ + username, + `${username}@example.com`, + TEST_PASSWORD_HASH, + preferredLanguage, + ], + ); + const id = rows[0].id; + createdUserIds.push(id); + await pool.query( + `INSERT INTO user_roles (user_id, role_id) + SELECT $1, id FROM roles WHERE name = 'user'`, + [id], + ); + return { id, username }; +} + +async function getAvailableService() { + const { rows } = await pool.query( + `SELECT id, + name_en AS "nameEn", + name_ar AS "nameAr" + FROM services + WHERE is_available = true + ORDER BY id ASC + LIMIT 1`, + ); + if (rows.length === 0) { + throw new Error("No available service found in DB to test ordering against"); + } + return rows[0]; +} + +test.afterAll(async () => { + if (createdOrderIds.length > 0) { + await pool.query(`DELETE FROM service_orders WHERE id = ANY($1::int[])`, [ + createdOrderIds, + ]); + } + if (createdUserIds.length > 0) { + // Catch any orders the user created that we didn't track (defensive). + await pool.query( + `DELETE FROM service_orders + WHERE user_id = ANY($1::int[]) OR assigned_to = ANY($1::int[])`, + [createdUserIds], + ); + await pool.query( + `DELETE FROM notifications WHERE user_id = ANY($1::int[])`, + [createdUserIds], + ); + await pool.query( + `DELETE FROM user_roles WHERE user_id = ANY($1::int[])`, + [createdUserIds], + ); + await pool.query(`DELETE FROM users WHERE id = ANY($1::int[])`, [ + createdUserIds, + ]); + } + await pool.end(); +}); + +async function setLanguage(page, lang) { + // i18n.ts reads `localStorage.getItem("tx-lang")` at module-init time, so + // seed it before any page script runs. + await page.addInitScript((l) => { + try { + window.localStorage.setItem("tx-lang", l); + } catch { + /* localStorage may not be available before navigation; safe to ignore */ + } + }, lang); +} + +async function loginViaUi(page, username, password) { + await page.goto("/login"); + await page.locator("#username").fill(username); + await page.locator("#password").fill(password); + await Promise.all([ + page.waitForURL((url) => !url.pathname.endsWith("/login"), { + timeout: 15_000, + }), + page.locator('form button[type="submit"]').click(), + ]); +} + +async function placeAndCancelFlow(page, lang, labels) { + // Seed the user with a server-side preferredLanguage matching the test + // language, because AuthContext / login.tsx call `i18n.changeLanguage( + // user.preferredLanguage)` after login and would otherwise override our + // localStorage tx-lang seed. + const user = await createUser(`order_ui_${lang}`, lang); + const service = await getAvailableService(); + const serviceName = lang === "ar" ? service.nameAr : service.nameEn; + + await setLanguage(page, lang); + await loginViaUi(page, user.username, TEST_PASSWORD); + + // ---- Place an order from /services ---- + await page.goto("/services"); + + // Service tiles render as