Files
AgendaPro/e2e-test.mjs
T
AgendaPro Dev e8d2435cc2 AgendaPro v1.0 - multi-tenant SaaS, mobile calendar, public booking
Multi-tenant scheduling SaaS (AgendaPro-equivalent):
- Backend: Node + Express + node:sqlite, multi-tenant (admin/owner/employee),
  versioned migrations, 4 templates (estetica-spa, barberia, clinica, blank).
- Frontend: React 18 + TS + Vite + Tailwind, FullCalendar drag-and-drop,
  Recharts dashboard, mobile-first (day/list views on mobile).
- Features: calendar+services+employees+clients+tickets, dashboard with
  best employee/service, top tickets, top/frequent clients, commissions,
  cancellation policy + no-show tracking, public online booking (/b/:slug),
  cash register (cierre de caja), reminders/notifications center.
- Verification: 65/65 e2e API, 58/58 visual (0 overflow, 0 console errors),
  typecheck clean, vite build OK.
2026-07-25 13:45:53 -06:00

61 lines
3.8 KiB
JavaScript

const BASE = "http://localhost:5173/api";
let pass = 0, fail = 0;
async function req(method, path, body, token) {
const headers = { "Content-Type": "application/json" };
if (token) headers.authorization = `Bearer ${token}`;
const res = await fetch(`${BASE}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined });
const text = await res.text();
let json; try { json = JSON.parse(text); } catch { json = text; }
return { status: res.status, json };
}
function check(name, cond, extra = "") {
if (cond) { pass++; console.log(` ✓ ${name}`); }
else { fail++; console.log(` ✗ ${name} ${extra}`); }
}
// /me now works (the bug we fixed) — login first to get a real token
const { json: login } = await req("POST", "/auth/login", { email: "[email protected]", password: "demo1234" });
check("login owner", !!login.token && login.user?.role === "owner", JSON.stringify(login).slice(0, 100));
const t = login.token;
const { json: me } = await req("GET", "/auth/me", null, t);
check("/api/auth/me returns user", me.user?.email === "[email protected]", JSON.stringify(me).slice(0, 100));
const checks = [
["business", "/business", (j) => j.business?.name === "Lumière Estética & Spa"],
["employees+stats", "/employees", (j) => j.employees?.length === 6 && !!j.employees[0].stats],
["services", "/services", (j) => j.services?.length === 12 && Array.isArray(j.services[0].employee_ids)],
["clients+stats", "/clients", (j) => j.clients?.length > 0 && j.clients[0].stats?.visits !== undefined],
["appointments joined", "/appointments?limit=5", (j) => j.appointments?.[0]?.service && j.appointments[0].client],
["dashboard overview", "/dashboard/overview", (j) => j.revenue_month > 0],
["best-employees", "/dashboard/best-employees", (j) => j.employees?.length === 6],
["best-services", "/dashboard/best-services", (j) => j.services?.length > 0],
["top-tickets", "/dashboard/top-tickets", (j) => j.tickets?.length > 0],
["top-clients", "/dashboard/top-clients", (j) => j.clients?.length > 0],
["frequent-clients", "/dashboard/frequent-clients", (j) => j.clients?.length > 0],
["revenue-trend", "/dashboard/revenue-trend?days=30", (j) => j.points?.length === 30],
["revenue-by-category", "/dashboard/revenue-by-category", (j) => j.slices?.length > 0],
["tickets list", "/dashboard/tickets", (j) => j.tickets?.length > 0],
];
for (const [name, path, cond] of checks) {
const { json, status } = await req("GET", path, null, t);
check(name, status === 200 && cond(json), `status=${status}`);
}
// Lifecycle
const { json: created } = await req("POST", "/appointments", { service_id: 1, client_id: 1, start_at: "2026-09-15T11:00:00Z" }, t);
check("create appointment", !!created.appointment?.id);
const { json: completed } = await req("POST", `/appointments/${created.appointment.id}/complete`, { tip: 50, payment_method: "cash" }, t);
check("complete -> ticket", completed.appointment?.status === "completed");
const { json: empLogin } = await req("POST", "/auth/login", { email: "[email protected]", password: "demo1234" });
const { json: empAppt } = await req("POST", "/appointments", { service_id: 5, client_id: 2, start_at: "2026-09-20T10:00:00Z" }, empLogin.token);
check("employee auto-assign", empAppt.appointment?.employee_id === empLogin.user?.employee_id);
const { status: badLogin } = await req("POST", "/auth/login", { email: "[email protected]", password: "wrong" });
check("bad login 401", badLogin === 401);
const { status: noAuth } = await req("GET", "/employees");
check("no auth 401", noAuth === 401);
const { status: empForb } = await req("GET", "/dashboard/overview", null, empLogin.token);
check("employee blocked dashboard 403", empForb === 403);
console.log(`\n${pass}/${pass + fail} passed${fail ? `, ${fail} FAILED` : ""}`);
process.exit(fail ? 1 : 0);