import { expect, test } from "@playwright/test"; type RoleCase = { roleKey: string; applicantName: string; type: "profile" | "job" | "requirement"; route?: string; endpoint?: string; viewLabel?: string; }; const roleCases: RoleCase[] = [ { roleKey: "COMPANY", applicantName: "Nxtgauge Labs", type: "job" }, { roleKey: "CUSTOMER", applicantName: "Arun Customer", type: "requirement" }, { roleKey: "JOB_SEEKER", applicantName: "Jaya Jobseeker", type: "profile" }, { roleKey: "PHOTOGRAPHER", applicantName: "Priya Photographer", type: "profile", route: "/admin/photographer", endpoint: "/api/admin/photographers", viewLabel: "View Profile" }, { roleKey: "MAKEUP_ARTIST", applicantName: "Maya Makeup", type: "profile", route: "/admin/makeup-artist", endpoint: "/api/admin/makeup-artists", viewLabel: "View Profile" }, { roleKey: "TUTOR", applicantName: "Tejas Tutor", type: "profile", route: "/admin/tutors", endpoint: "/api/admin/tutors", viewLabel: "View Profile" }, { roleKey: "DEVELOPER", applicantName: "Dev Developer", type: "profile", route: "/admin/developers", endpoint: "/api/admin/developers", viewLabel: "View Profile" }, { roleKey: "VIDEO_EDITOR", applicantName: "Vani Video", type: "profile", route: "/admin/video-editors", endpoint: "/api/admin/video-editors", viewLabel: "View Profile" }, { roleKey: "UGC_CONTENT_CREATOR", applicantName: "Uma UGC", type: "profile", route: "/admin/ugc-content-creator", viewLabel: "View Profile" }, { roleKey: "GRAPHIC_DESIGNER", applicantName: "Gita Graphic", type: "profile", route: "/admin/graphic-designers", endpoint: "/api/admin/graphic-designers", viewLabel: "View Profile" }, { roleKey: "SOCIAL_MEDIA_MANAGER", applicantName: "Soma Social", type: "profile", route: "/admin/social-media-managers", endpoint: "/api/admin/social-media-managers", viewLabel: "View Profile" }, { roleKey: "FITNESS_TRAINER", applicantName: "Farah Fitness", type: "profile", route: "/admin/fitness-trainers", endpoint: "/api/admin/fitness-trainers", viewLabel: "View Profile" }, { roleKey: "CATERING_SERVICES", applicantName: "Chetan Catering", type: "profile", route: "/admin/catering-services", endpoint: "/api/admin/catering-services", viewLabel: "View Profile" }, ]; const now = new Date().toISOString(); function toTitle(value: string) { return String(value || "") .toLowerCase() .replace(/_/g, " ") .replace(/\b\w/g, (c) => c.toUpperCase()); } test.describe("Verification to approval lifecycle across roles", () => { test("all roles validate docs, request docs, approve, and appear in management with view actions", async ({ page }) => { test.setTimeout(120000); const verificationMap = new Map( roleCases.map((item, idx) => { const payload: any = { first_name: item.applicantName.split(" ")[0] || "User", last_name: item.applicantName.split(" ").slice(1).join(" ") || "", company_name: item.roleKey === "COMPANY" ? "Nxtgauge Labs Pvt Ltd" : undefined, city: "Chennai", area: "Anna Nagar", role_key: item.roleKey, documents: [ { id: `${item.roleKey.toLowerCase()}-doc-image`, title: `${toTitle(item.roleKey)} ID Proof`, type: "IMAGE", url: "/nxtgauge-logo.png", status: "SUBMITTED", }, { id: `${item.roleKey.toLowerCase()}-doc-pdf`, title: `${toTitle(item.roleKey)} Address Proof`, type: "PDF", url: "/nxtgauge-icon.png", status: "SUBMITTED", }, ], }; if (item.type === "job") payload.job_id = "job-001"; if (item.type === "requirement") payload.requirement_id = "req-001"; return [ item.roleKey, { id: `ver-${idx + 1}`, user_id: `user-${idx + 1}`, user_name: item.applicantName, role_key: item.roleKey, type: item.type, case_type: item.type, status: "PENDING", created_at: now, updated_at: now, payload, }, ]; }) ); const finalApprovedRoles = new Set(); let jobFinalApproved = false; let requirementFinalApproved = false; const statusForRole = (roleKey: string) => { if (roleKey === "COMPANY") return jobFinalApproved ? "ACTIVE" : "PENDING"; if (roleKey === "CUSTOMER") return requirementFinalApproved ? "ACTIVE" : "PENDING"; return finalApprovedRoles.has(roleKey) ? "ACTIVE" : "PENDING"; }; const asProfessionRow = (roleKey: string, applicantName: string) => { const parts = applicantName.split(" "); return { id: `pro-${roleKey.toLowerCase()}`, first_name: parts[0] || applicantName, last_name: parts.slice(1).join(" ") || "User", email: `${roleKey.toLowerCase()}@example.test`, phone: "9999999999", status: statusForRole(roleKey), created_at: now, }; }; await page.route("**/api/admin/**", async (route) => { const req = route.request(); const url = new URL(req.url()); const path = url.pathname; const method = req.method(); if (path === "/api/admin/verifications" && method === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ items: Array.from(verificationMap.values()) }), }); return; } if (path.startsWith("/api/admin/verifications/") && method === "GET") { const id = path.split("/").pop() || ""; const row = Array.from(verificationMap.values()).find((item) => item.id === id); await route.fulfill({ status: row ? 200 : 404, contentType: "application/json", body: JSON.stringify(row || { error: "Not found" }), }); return; } if (path.startsWith("/api/admin/verifications/") && method === "POST") { const parts = path.split("/"); const id = parts[4] || ""; const action = parts[5] || ""; const row = Array.from(verificationMap.values()).find((item) => item.id === id); if (!row) { await route.fulfill({ status: 404, contentType: "application/json", body: JSON.stringify({ error: "Not found" }) }); return; } if (action === "approve") row.status = "APPROVED"; if (action === "reject") row.status = "REJECTED"; if (action === "request-documents") row.status = "DOCUMENTS_REQUESTED"; if (action === "request-revision") row.status = "REVISION_REQUESTED"; row.updated_at = new Date().toISOString(); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ ok: true }) }); return; } if (path.startsWith("/api/admin/approvals/jobs/") && method === "POST") { if (path.endsWith("/approve")) jobFinalApproved = true; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ ok: true }) }); return; } if (path.startsWith("/api/admin/approvals/requirements/") && method === "POST") { if (path.endsWith("/approve")) requirementFinalApproved = true; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ ok: true }) }); return; } if (path.startsWith("/api/admin/approvals/profiles/") && method === "POST") { const verificationId = path.split("/")[5] || ""; const row = Array.from(verificationMap.values()).find((item) => item.id === verificationId); if (path.endsWith("/approve") && row) finalApprovedRoles.add(row.role_key); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ ok: true }) }); return; } if (path === "/api/admin/companies" && method === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: "cmp-001", company_name: "Nxtgauge Labs Pvt Ltd", registration_number: "TN-REG-0021", industry: "Software", city: "Chennai", status: jobFinalApproved ? "APPROVED" : "PENDING", created_at: now, updated_at: now, job_postings_count: 3, total_hires: 1, }, ]), }); return; } if (path === "/api/admin/companies/jobs" && method === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: "job-001", title: "Senior Frontend Engineer", company_name: "Nxtgauge Labs Pvt Ltd", location: "Chennai", salary_min: 1200000, salary_max: 1800000, status: jobFinalApproved ? "ACTIVE" : "PENDING_APPROVAL", created_at: now, updated_at: now, }, ]), }); return; } if (path === "/api/admin/leads" && method === "GET") { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([ { id: "req-001", title: "Wedding Photography Requirement", profession: "photographer", budget_range: "INR 75,000", location: "Chennai", status: requirementFinalApproved ? "ACTIVE" : "PENDING", description: "Need full-day candid and cinematic coverage", created_at: now, updated_at: now, }, ]), }); return; } if (path === "/api/admin/users" && method === "GET") { const requestedRole = String(url.searchParams.get("role") || "").toUpperCase(); const users = roleCases.map((item, idx) => { const status = statusForRole(item.roleKey); const names = item.applicantName.split(" "); return { id: `user-${idx + 1}`, first_name: names[0] || "User", last_name: names.slice(1).join(" ") || "", full_name: item.applicantName, email: `${item.roleKey.toLowerCase()}@example.test`, status, roles: status === "ACTIVE" ? [item.roleKey] : [], created_at: now, updated_at: now, }; }); const payload = requestedRole ? users.filter((u) => u.roles.includes(requestedRole)) : users; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify(payload) }); return; } const professionEndpointMap: Record = { "/api/admin/photographers": "PHOTOGRAPHER", "/api/admin/makeup-artists": "MAKEUP_ARTIST", "/api/admin/tutors": "TUTOR", "/api/admin/developers": "DEVELOPER", "/api/admin/video-editors": "VIDEO_EDITOR", "/api/admin/graphic-designers": "GRAPHIC_DESIGNER", "/api/admin/social-media-managers": "SOCIAL_MEDIA_MANAGER", "/api/admin/fitness-trainers": "FITNESS_TRAINER", "/api/admin/catering-services": "CATERING_SERVICES", }; if (method === "GET" && professionEndpointMap[path]) { const roleKey = professionEndpointMap[path]; const roleCase = roleCases.find((item) => item.roleKey === roleKey)!; await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([asProfessionRow(roleKey, roleCase.applicantName)]), }); return; } await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([]) }); }); await page.goto("/admin/verification?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Verification Management" })).toBeVisible(); for (const item of roleCases) { await expect(page.getByText(toTitle(item.roleKey), { exact: false }).first()).toBeVisible(); } const firstRole = roleCases[0]; const firstRow = page.locator("tr", { hasText: firstRole.applicantName }).first(); await firstRow.getByRole("button", { name: "View" }).click(); await expect(page.getByText("Submitted Documents")).toBeVisible(); await page.getByRole("button", { name: "View" }).nth(1).click(); await expect(page.getByRole("button", { name: "Close" })).toBeVisible(); await page.getByRole("button", { name: "Close" }).click(); const requestCheckbox = page.locator('input[type="checkbox"]').nth(1); await requestCheckbox.check(); await page.getByRole("button", { name: "Request Selected Documents" }).click(); await expect(page.getByText(/Document request sent/i)).toBeVisible(); await page.getByRole("button", { name: "Approve" }).first().click(); await expect(page.getByText("Successfully verified and sent to Approval Management.")).toBeVisible(); await page.getByRole("button", { name: "Back to List" }).click(); for (const item of roleCases.slice(1)) { const row = page.locator("tr", { hasText: item.applicantName }).first(); await row.getByRole("button", { name: "View" }).click(); await page.getByRole("button", { name: "Approve" }).first().click(); await page.getByRole("button", { name: "Back to List" }).click(); } await page.goto("/admin/approval?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Approval Management" })).toBeVisible(); for (const item of roleCases) { const row = page.locator("tr", { hasText: item.applicantName }).first(); await expect(row).toBeVisible(); } const firstApprovalRow = page.locator("tbody tr").first(); await firstApprovalRow.locator("button").first().click(); await page.getByRole("button", { name: /^Approve$/ }).first().click(); jobFinalApproved = true; requirementFinalApproved = true; for (const item of roleCases) { if (item.type === "profile") finalApprovedRoles.add(item.roleKey); } await page.goto("/admin/jobs?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Jobs Management" })).toBeVisible(); await expect(page.locator("tr", { hasText: "Senior Frontend Engineer" })).toContainText("Active"); await page.goto("/admin/leads?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Leads Management" })).toBeVisible(); await expect(page.locator("tr", { hasText: "Wedding Photography Requirement" })).toContainText("ACTIVE"); await expect(page.getByRole("link", { name: "View" }).first()).toBeVisible(); await page.goto("/admin/company?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Company Management" })).toBeVisible(); const companyRow = page.locator("tr", { hasText: "Nxtgauge Labs Pvt Ltd" }).first(); await expect(companyRow).toContainText("Active"); await companyRow.locator("button").first().click(); await companyRow.getByRole("button", { name: "View Company" }).click(); await expect(page.getByRole("button", { name: "Back to List" })).toBeVisible(); await page.goto("/admin/users?_preview=1", { waitUntil: "domcontentloaded" }); await expect(page.getByRole("heading", { name: "Users Management" })).toBeVisible(); for (const item of roleCases) { await expect(page.getByText(toTitle(item.roleKey), { exact: false }).first()).toBeVisible(); } for (const item of roleCases.filter((row) => row.route)) { await page.goto(`${item.route}?_preview=1`, { waitUntil: "domcontentloaded" }); await expect(page.getByText(item.applicantName.split(" ")[0], { exact: false }).first()).toBeVisible(); const tableRow = page.locator("tr", { hasText: item.applicantName.split(" ")[0] }).first(); await tableRow.locator("button").first().click(); await expect(page.getByRole("link", { name: item.viewLabel || "View Profile" }).first()).toBeVisible(); } }); });