Files
meshcore-hub/e2e/utils/helpers.ts
T
Louis King 83a936cd33 feat(routes): allow operators to manage routes
Operators can now create, edit, and delete routes (previously admin-only).
A user may never scope a route above their own role tier: an operator
creating/editing an admin-visibility route is rejected (403 on the
visibility value, 404 on touching an existing higher-visibility route),
preventing them from creating routes they could then never see or modify.

- routes.py: RequireAdmin -> RequireOperatorOrAdmin on create/update/delete;
  add visibility-cap enforcement helpers reusing the existing
  resolve_user_role / VISIBILITY_LEVELS ladder
- web/app.py: proxy access map admits operator for routes POST/PUT/DELETE
- Routes.tsx: canManage gate (admin||operator) on Add/Edit/Delete; visibility
  <select> filters options by caller tier so operators never see 'admin'
- tests: operator-tier coverage (create/update/delete at/below/above level),
  proxy access-map assertion, vitest role-gating + filtered select
- e2e: mint operator session + routes-operator spec
- docs: routes.md + auth.md operator/visibility-cap notes
2026-07-24 20:59:25 +01:00

41 lines
1.1 KiB
TypeScript

import path from "node:path";
import { fileURLToPath } from "node:url";
import { expect, type Page } from "@playwright/test";
const AUTH_DIR = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"..",
".auth",
);
export const ADMIN_STATE = path.join(AUTH_DIR, "admin.json");
export const MEMBER_STATE = path.join(AUTH_DIR, "member.json");
export const OPERATOR_STATE = path.join(AUTH_DIR, "operator.json");
export async function expectListLoaded(page: Page): Promise<void> {
await expect(page.getByTestId("list-row").first()).toBeVisible();
}
export async function openFilters(page: Page): Promise<void> {
const toggle = page.locator("#filter-toggle");
if (!(await toggle.isChecked())) {
await toggle.click();
}
}
export async function countApiCalls(
page: Page,
urlFragment: string,
durationMs: number,
): Promise<number> {
let count = 0;
const onRequest = (request: { url: () => string }): void => {
if (request.url().includes(urlFragment)) {
count += 1;
}
};
page.on("request", onRequest);
await page.waitForTimeout(durationMs);
page.off("request", onRequest);
return count;
}