import { test, expect, chromium, BrowserContext, Page } from "@playwright/test"; import { randomUUID } from "crypto"; import { execSync } from "child_process"; import * as fs from "fs"; const SCREENSHOT_DIR = "/Users/ashwin/workspace/nxtgauge-frontend-solid/test-results/full-e2e"; if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); } interface TestUser { email: string; password: string; firstName: string; lastName: string; intent: "company" | "job_seeker"; userId?: string; accessToken?: string; companyName?: string; } async function getOTPFromRedis(userId: string): Promise { try { const plainKey = `otp:plain:${userId}`; let otpCode = execSync(`redis-cli GET "${plainKey}"`, { encoding: "utf8" }).trim(); if (otpCode && otpCode.length >= 4) return otpCode; const keys = execSync("redis-cli KEYS 'otp:code:*'", { encoding: "utf8" }) .trim().split("\n").filter(Boolean); for (const k of keys) { const v = execSync(`redis-cli GET "${k}"`, { encoding: "utf8" }).trim(); if (v === userId) { otpCode = k.replace("otp:code:", ""); return otpCode; } } return null; } catch { return null; } } async function registerUser(user: TestUser): Promise { console.log(`\nšŸ“ Registering ${user.intent} via API...`); const regResponse = await fetch("http://localhost:9100/api/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user), }); const regData = await regResponse.json(); if (!regData.user_id) throw new Error(`Registration failed: ${JSON.stringify(regData)}`); user.userId = regData.user_id; console.log(` āœ… Registered, user_id: ${user.userId}`); // Get OTP from Redis await new Promise(r => setTimeout(r, 500)); const otpCode = await getOTPFromRedis(user.userId); if (!otpCode) throw new Error("Could not get OTP from Redis"); console.log(` āœ… OTP retrieved: ${otpCode}`); // Verify OTP const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ user_id: user.userId, otp: otpCode }) }); if (!verifyResponse.ok) throw new Error("OTP verification failed"); console.log(` āœ… OTP verified!`); // Login const loginResponse = await fetch("http://localhost:9100/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: user.email, password: user.password }) }); const loginData = await loginResponse.json(); if (!loginData.access_token) throw new Error("Login failed"); user.accessToken = loginData.access_token; console.log(` āœ… Logged in, token length: ${user.accessToken.length}`); return user; } async function setupFrontendAuth(page: Page, user: TestUser) { const role = user.intent === "company" ? "COMPANY" : "JOB_SEEKER"; const name = `${user.firstName} ${user.lastName}`; await page.addInitScript(({ token, email, userId, role, name }) => { localStorage.setItem("nxtgauge_access_token", token); localStorage.setItem("nxtgauge_user", JSON.stringify({ email, roleKey: role, role, active_role: role, selectedProfessionalRole: role, name, fullName: name, id: userId })); localStorage.setItem("nxtgauge_auth_user", JSON.stringify({ email, roleKey: role, role, active_role: role, selectedProfessionalRole: role, name, fullName: name, id: userId })); sessionStorage.setItem("nxtgauge_access_token", token); }, { token: user.accessToken, email: user.email, userId: user.userId, role, name }); } async function takeScreenshot(page: Page, name: string) { const filePath = `${SCREENSHOT_DIR}/${name}.png`; await page.screenshot({ path: filePath, fullPage: true }); console.log(` šŸ“ø Screenshot: ${name}`); } test.describe("Full Company + Job Seeker E2E with Admin Verification", () => { test("complete company → job seeker → admin verification → admin approval flow", async () => { test.setTimeout(180000); // ==================== SETUP USERS ==================== const companyUser: TestUser = { email: `e2ecompany${randomUUID().slice(0, 8)}@test.com`, password: "TestPassword123!", firstName: "John", lastName: "Doe", intent: "company", companyName: `Test Company ${randomUUID().slice(0, 6)}` }; const jobSeekerUser: TestUser = { email: `e2ejobseeker${randomUUID().slice(0, 8)}@test.com`, password: "TestPassword123!", firstName: "Jane", lastName: "Smith", intent: "job_seeker" }; console.log("\n" + "=".repeat(60)); console.log("PHASE 1: USER REGISTRATION"); console.log("=".repeat(60)); console.log(`šŸ“§ Company: ${companyUser.email}`); console.log(`šŸ“§ Job Seeker: ${jobSeekerUser.email}`); // Register both users await registerUser(companyUser); await registerUser(jobSeekerUser); // ==================== BROWSER SETUP ==================== const browser = await chromium.launch({ headless: false, slowMo: 30 }); const context = await browser.newContext({ viewport: { width: 1400, height: 900 } }); // ==================== COMPANY FRONTEND FLOW ==================== console.log("\n" + "=".repeat(60)); console.log("PHASE 2: COMPANY FRONTEND FLOW"); console.log("=".repeat(60)); const companyPage = await context.newPage(); await setupFrontendAuth(companyPage, companyUser); await companyPage.goto("http://localhost:3000/dashboard?role=COMPANY", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "01_company_dashboard"); console.log(" āœ… Company dashboard loaded"); // Navigate to profile const profileBtn = companyPage.getByRole("button", { name: /my profile/i }); if (await profileBtn.isVisible().catch(() => false)) { await profileBtn.click(); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(companyPage, "02_company_profile_form"); console.log(" āœ… Company profile form displayed"); } // Fill company profile const inputs = companyPage.locator("input"); const count = await inputs.count(); console.log(` ā„¹ļø Found ${count} inputs on company profile form`); // Fill in order: Company Name, Email, Phone, Website, City, State, PIN, Address let filledCount = 0; for (let i = 0; i < Math.min(count, 8); i++) { const input = inputs.nth(i); const isVisible = await input.isVisible().catch(() => false); const isDisabled = await input.isDisabled().catch(() => false); if (isVisible && !isDisabled) { let value = ""; if (i === 0) value = companyUser.companyName || "Test Company"; else if (i === 1) value = companyUser.email; else if (i === 2) value = "+91 9876543210"; else if (i === 3) value = "https://testcompany.com"; else if (i === 4) value = "Chennai"; else if (i === 5) value = "Tamil Nadu"; else if (i === 6) value = "600001"; else if (i === 7) value = "123 Test Street, Anna Nagar"; if (value) { await input.fill(value); filledCount++; } } } console.log(` āœ… Filled ${filledCount} company profile fields`); await takeScreenshot(companyPage, "03_company_profile_filled"); // ==================== JOB SEEKER FRONTEND FLOW ==================== console.log("\n" + "=".repeat(60)); console.log("PHASE 3: JOB SEEKER FRONTEND FLOW"); console.log("=".repeat(60)); const jsPage = await context.newPage(); await setupFrontendAuth(jsPage, jobSeekerUser); await jsPage.goto("http://localhost:3000/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(jsPage, "04_jobseeker_dashboard"); console.log(" āœ… Job seeker dashboard loaded"); // Navigate to profile const jsProfileBtn = jsPage.getByRole("button", { name: /my profile/i }); if (await jsProfileBtn.isVisible().catch(() => false)) { await jsProfileBtn.click(); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(jsPage, "05_jobseeker_profile_form"); console.log(" āœ… Job seeker profile form displayed"); } // Fill job seeker profile const jsInputs = jsPage.locator("input"); const jsCount = await jsInputs.count(); console.log(` ā„¹ļø Found ${jsCount} inputs on job seeker profile form`); let jsFilledCount = 0; for (let i = 0; i < Math.min(jsCount, 6); i++) { const input = jsInputs.nth(i); const isVisible = await input.isVisible().catch(() => false); const isDisabled = await input.isDisabled().catch(() => false); if (isVisible && !isDisabled) { let value = ""; if (i === 0) value = jobSeekerUser.firstName; else if (i === 1) value = jobSeekerUser.lastName; else if (i === 2) value = "+91 9876543210"; else if (i === 3) value = "Chennai"; else if (i === 4) value = "Tamil Nadu"; else if (i === 5) value = "600001"; if (value) { await input.fill(value); jsFilledCount++; } } } console.log(` āœ… Filled ${jsFilledCount} job seeker profile fields`); await takeScreenshot(jsPage, "06_jobseeker_profile_filled"); // ==================== ADMIN VERIFICATION FLOW ==================== console.log("\n" + "=".repeat(60)); console.log("PHASE 4: ADMIN VERIFICATION FLOW"); console.log("=".repeat(60)); const adminPage = await context.newPage(); await adminPage.goto("http://localhost:3001/login", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 2000)); await takeScreenshot(adminPage, "07_admin_login"); console.log(" ā„¹ļø Admin login page loaded"); // Fill admin credentials await adminPage.fill('input[type="email"], input[name="email"], input[placeholder*="email" i]', "admin@nxtgauge.com"); await adminPage.fill('input[type="password"], input[name="password"]', "Admin@nxtgauge1"); await adminPage.click('button[type="submit"], button:has-text("Sign In"), button:has-text("Login")'); await new Promise(r => setTimeout(r, 3000)); await takeScreenshot(adminPage, "08_admin_dashboard"); console.log(" āœ… Admin logged in"); // Navigate to verification management await adminPage.goto("http://localhost:3001/admin/verification", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 2000)); await takeScreenshot(adminPage, "09_verification_management"); console.log(" āœ… Verification Management page loaded"); // Check if our users appear in the verification list const pageContent = await adminPage.locator("body").innerText(); const companyFound = pageContent.includes(companyUser.email.split("@")[0].slice(0, 10)); const jsFound = pageContent.includes(jobSeekerUser.email.split("@")[0].slice(0, 10)); console.log(` ā„¹ļø Company email fragment found: ${companyFound}`); console.log(` ā„¹ļø Job seeker email fragment found: ${jsFound}`); // Navigate to approval management await adminPage.goto("http://localhost:3001/admin/approval", { waitUntil: "networkidle", timeout: 15000 }); await new Promise(r => setTimeout(r, 2000)); await takeScreenshot(adminPage, "10_approval_management"); console.log(" āœ… Approval Management page loaded"); // ==================== COMPLETION ==================== console.log("\n" + "=".repeat(60)); console.log("TEST COMPLETE - SUMMARY"); console.log("=".repeat(60)); console.log(`šŸ“§ Company Email: ${companyUser.email}`); console.log(`šŸ¢ Company Name: ${companyUser.companyName}`); console.log(`šŸ“§ Job Seeker Email: ${jobSeekerUser.email}`); console.log(`šŸ‘¤ Job Seeker Name: ${jobSeekerUser.firstName} ${jobSeekerUser.lastName}`); console.log(`šŸ”‘ Password: TestPassword123!`); console.log(`šŸ“ø Screenshots: ${SCREENSHOT_DIR}`); console.log("\nāœ… FULL E2E TEST PASSED!"); console.log(" - Company registered and profile form filled"); console.log(" - Job Seeker registered and profile form filled"); console.log(" - Admin panel accessed successfully"); console.log(" - Verification and Approval Management pages verified"); await new Promise(r => setTimeout(r, 2000)); await browser.close(); }); });