d2b4f82736
- artifacts/tx-os/src/App.tsx: serve the schedule at /meetings; add an ExecutiveMeetingsRedirect that rewrites /executive-meetings (and any /executive-meetings/* sub-path) to /meetings while preserving the query string and hash, so existing PDFs, emails, and bookmarks keep working with one transparent hop. - scripts/src/seed.ts: seed the apps row with route="/meetings" and update the expectedBuiltinRoutes drift guard to match. Add an idempotent UPDATE-by-slug after the apps insert so already-deployed rows (including any drift like /executive-meetings or /mms) get reconciled to /meetings on the next seed — safe because the slug is in BUILTIN_APP_SLUGS, meaning the SPA owns the route. - artifacts/tx-os/tests/meetings-route-redirect.spec.mjs: new spec verifying that /meetings loads, /executive-meetings?date=...#... is redirected with query+hash intact, and /api/apps now reports route="/meetings" for the executive-meetings slug. Out of scope (intentionally left untouched per task): API paths under /api/executive-meetings/*, React Query keys, DB tables, the apps slug "executive-meetings", page/component file names, the "Meetings" display name, and the executive_meetings:* permission name.
73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
import { test, expect } from "@playwright/test";
|
|
|
|
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(),
|
|
]);
|
|
}
|
|
|
|
test("/meetings loads the schedule directly", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
try {
|
|
window.localStorage.setItem("tx-lang", "en");
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
});
|
|
await loginViaUi(page, "admin", "admin123");
|
|
await page.goto("/meetings");
|
|
await expect(page).toHaveURL(/\/meetings(?:\?|#|$)/);
|
|
await expect(page.getByTestId("em-edit-mode-toggle")).toBeVisible();
|
|
});
|
|
|
|
test("/executive-meetings redirects to /meetings (back-compat for old links)", async ({
|
|
page,
|
|
}) => {
|
|
await page.addInitScript(() => {
|
|
try {
|
|
window.localStorage.setItem("tx-lang", "en");
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
});
|
|
await loginViaUi(page, "admin", "admin123");
|
|
await page.goto("/executive-meetings?date=2025-01-15#schedule");
|
|
await expect(page).toHaveURL(/\/meetings\?date=2025-01-15#schedule$/);
|
|
await expect(page.getByTestId("em-edit-mode-toggle")).toBeVisible();
|
|
});
|
|
|
|
test("the apps list returns the new /meetings route for the executive-meetings slug", async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
// Home tile launches via openApp(app) which navigates to app.route
|
|
// straight from the API. Hitting the apps API directly proves the
|
|
// seed migration ran and the home grid will follow automatically.
|
|
await page.addInitScript(() => {
|
|
try {
|
|
window.localStorage.setItem("tx-lang", "en");
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
});
|
|
await loginViaUi(page, "admin", "admin123");
|
|
const cookies = await page.context().cookies();
|
|
const cookieHeader = cookies
|
|
.map((c) => `${c.name}=${c.value}`)
|
|
.join("; ");
|
|
const res = await request.get("/api/apps", {
|
|
headers: { Cookie: cookieHeader },
|
|
});
|
|
expect(res.ok()).toBeTruthy();
|
|
const apps = await res.json();
|
|
const meetings = apps.find((a) => a.slug === "executive-meetings");
|
|
expect(meetings).toBeTruthy();
|
|
expect(meetings.route).toBe("/meetings");
|
|
});
|