import { test, expect, chromium, Page, Browser, BrowserContext } from "@playwright/test"; import { randomUUID } from "crypto"; import * as fs from "fs"; import * as path from "path"; const SCREENSHOT_DIR = "./test-results"; const VIDEO_DIR = "./test-videos"; // Ensure directories exist if (!fs.existsSync(SCREENSHOT_DIR)) fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); if (!fs.existsSync(VIDEO_DIR)) fs.mkdirSync(VIDEO_DIR, { recursive: true }); export interface TestUser { email: string; password: string; firstName: string; lastName: string; intent: string; userId?: string; accessToken?: string; } export interface CompanyUser extends TestUser { companyName: string; } export interface JobSeekerUser extends TestUser { education?: string; skills?: string[]; resumePath?: string; } async function generateTestEmail(): Promise { return `test_${randomUUID().slice(0, 8)}@test.com`; } async function apiRegister(user: TestUser): Promise<{ user_id: string }> { const response = await fetch("http://localhost:9100/api/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(user), }); return response.json(); } async function apiVerifyOtp(): Promise { const response = await fetch("http://localhost:9100/api/auth/verify-email", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ otp: "123456" }), }); return response.ok; } async function apiLogin(email: string, password: string): Promise<{ access_token: string }> { const response = await fetch("http://localhost:9100/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); return response.json(); } async function setRedisOtp(userId: string): Promise { const { execSync } = await import("child_process"); try { execSync(`redis-cli SETEX "otp:code:123456" 900 "${userId}"`, { encoding: "utf8" }); } catch (e) { console.log("āš ļø Could not set OTP in Redis:", e); } } async function takeScreenshot(page: Page, name: string): Promise { const filePath = path.join(SCREENSHOT_DIR, `${name}.png`); await page.screenshot({ path: filePath, fullPage: true }); console.log(`šŸ“ø Screenshot: ${name}`); } export async function registerCompany(): Promise { const email = await generateTestEmail(); const companyName = `Test Company ${randomUUID().slice(0, 6)}`; const user: TestUser = { email, password: "TestPassword123!", firstName: "John", lastName: "Doe", intent: "company", }; console.log("\nšŸ“ Step 1: Registering company via API..."); const regData = await apiRegister(user); user.userId = regData.user_id; console.log(" āœ… Registered, user_id:", regData.user_id); console.log("\nšŸ” Step 2: Setting test OTP in Redis..."); await setRedisOtp(regData.user_id); console.log(" āœ… Set test OTP: 123456"); console.log("\nāœ… Step 3: Verifying OTP via API..."); const verified = await apiVerifyOtp(); if (!verified) throw new Error("OTP verification failed"); console.log(" āœ… OTP verified!"); console.log("\nšŸ”‘ Step 4: Logging in via API..."); const loginData = await apiLogin(email, user.password); user.accessToken = loginData.access_token; console.log(" āœ… Logged in via API!"); return { ...user, companyName }; } export async function registerJobSeeker(): Promise { const email = await generateTestEmail(); const user: TestUser = { email, password: "TestPassword123!", firstName: "Jane", lastName: "Smith", intent: "job_seeker", }; console.log("\nšŸ“ Step 1: Registering job seeker via API..."); const regData = await apiRegister(user); user.userId = regData.user_id; console.log(" āœ… Registered, user_id:", regData.user_id); console.log("\nšŸ” Step 2: Setting test OTP in Redis..."); await setRedisOtp(regData.user_id); console.log(" āœ… Set test OTP: 123456"); console.log("\nāœ… Step 3: Verifying OTP via API..."); const verified = await apiVerifyOtp(); if (!verified) throw new Error("OTP verification failed"); console.log(" āœ… OTP verified!"); console.log("\nšŸ”‘ Step 4: Logging in via API..."); const loginData = await apiLogin(email, user.password); user.accessToken = loginData.access_token; console.log(" āœ… Logged in via API!"); return user; } export async function loginViaBrowser( context: BrowserContext, email: string, password: string ): Promise { const page = await context.newPage(); console.log("\n🌐 Opening login page..."); await page.goto("http://localhost:3000/login"); await page.waitForLoadState("networkidle"); await takeScreenshot(page, "01-login-page"); console.log("āœļø Filling login form..."); await page.fill('input[type="email"], input[name="email"]', email); await page.fill('input[type="password"], input[name="password"]', password); await takeScreenshot(page, "02-login-filled"); console.log("šŸ” Handling CAPTCHA (manual entry required)..."); console.log(" Please enter CAPTCHA in the browser window..."); await page.waitForFunction( () => { const btn = document.querySelector(".auth-submit-btn, button[type='submit']"); return btn && !(btn as HTMLButtonElement).disabled; }, { timeout: 60000 } ); await page.click(".auth-submit-btn, button[type='submit']"); await page.waitForTimeout(3000); await takeScreenshot(page, "03-after-login"); return page; } export async function setSessionToken(page: Page, token: string): Promise { await page.evaluate( (t) => { window.sessionStorage.setItem("nxtgauge_access_token", t); window.sessionStorage.setItem("nxtgauge_frontend_access_token", t); }, token ); console.log(" āœ… Session token set"); } export async function navigateToDashboard(page: Page, role?: string): Promise { const url = role ? `http://localhost:3000/dashboard?role=${role}` : "http://localhost:3000/dashboard"; await page.goto(url); await page.waitForLoadState("networkidle"); await page.waitForTimeout(2000); await takeScreenshot(page, "04-dashboard"); console.log(" āœ… Navigated to dashboard"); } export async function fillCompanyProfile(page: Page, companyName: string): Promise { console.log("\nšŸ¢ Filling company profile..."); await page.goto("http://localhost:3000/dashboard/profile?role=COMPANY"); await page.waitForLoadState("networkidle"); await page.waitForTimeout(2000); await takeScreenshot(page, "05-company-profile"); const nameInput = await page.locator('input[name="companyName"], input[name="name"]').first(); if (await nameInput.isVisible().catch(() => false)) { await nameInput.fill(companyName); console.log(" āœ… Company name filled"); } const websiteInput = await page.locator('input[name="website"], input[placeholder*="website"]').first(); if (await websiteInput.isVisible().catch(() => false)) { await websiteInput.fill("https://testcompany.com"); console.log(" āœ… Website filled"); } const phoneInput = await page.locator('input[name="phone"], input[name="companyPhone"]').first(); if (await phoneInput.isVisible().catch(() => false)) { await phoneInput.fill("+91 9876543210"); console.log(" āœ… Phone filled"); } await takeScreenshot(page, "06-company-profile-filled"); const submitBtn = await page.locator('button[type="submit"], button:has-text("Submit")').first(); if (await submitBtn.isVisible().catch(() => false)) { await submitBtn.click(); await page.waitForTimeout(3000); console.log(" āœ… Profile submitted"); await takeScreenshot(page, "07-company-profile-submitted"); } } export async function fillJobSeekerProfile(page: Page): Promise { console.log("\nšŸ‘¤ Filling job seeker profile..."); await page.goto("http://localhost:3000/dashboard/profile?role=JOB_SEEKER"); await page.waitForLoadState("networkidle"); await page.waitForTimeout(2000); await takeScreenshot(page, "08-jobseeker-profile"); const firstNameInput = await page.locator('input[name="firstName"], input[name="first_name"]').first(); if (await firstNameInput.isVisible().catch(() => false)) { await firstNameInput.fill("Jane"); console.log(" āœ… First name filled"); } const lastNameInput = await page.locator('input[name="lastName"], input[name="last_name"]').first(); if (await lastNameInput.isVisible().catch(() => false)) { await lastNameInput.fill("Smith"); console.log(" āœ… Last name filled"); } const bioInput = await page.locator('textarea[name="bio"], textarea[name="about"]').first(); if (await bioInput.isVisible().catch(() => false)) { await bioInput.fill("Experienced software developer with 5+ years in the industry."); console.log(" āœ… Bio filled"); } await takeScreenshot(page, "09-jobseeker-profile-filled"); const submitBtn = await page.locator('button[type="submit"], button:has-text("Submit")').first(); if (await submitBtn.isVisible().catch(() => false)) { await submitBtn.click(); await page.waitForTimeout(3000); console.log(" āœ… Profile submitted"); await takeScreenshot(page, "10-jobseeker-profile-submitted"); } } export async function adminLogin(context: BrowserContext): Promise { console.log("\nšŸ” Opening admin panel..."); const adminPage = await context.newPage(); await adminPage.goto("http://localhost:3001/login"); await adminPage.waitForLoadState("networkidle"); await takeScreenshot(adminPage, "11-admin-login"); console.log("āœļø Filling admin credentials..."); await adminPage.fill('input[type="email"], input[name="email"]', "admin@nxtgauge.com"); await adminPage.fill('input[type="password"], input[name="password"]', "Admin@nxtgauge1"); await adminPage.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign In")'); await adminPage.waitForTimeout(3000); await takeScreenshot(adminPage, "12-admin-dashboard"); console.log(" āœ… Admin logged in"); return adminPage; } export async function findUserInAdminVerification( adminPage: Page, email: string ): Promise { console.log(`\nšŸ” Searching for ${email} in admin verification...`); const verificationLinks = [ "text=Verifications", 'a:has-text("Verifications")', '[href*="verification"]', "text=Pending", ]; for (const selector of verificationLinks) { const link = adminPage.locator(selector).first(); if (await link.isVisible().catch(() => false)) { await link.click(); await adminPage.waitForTimeout(2000); break; } } await takeScreenshot(adminPage, "13-verification-list"); const searchInput = adminPage.locator('input[placeholder*="search" i], input[name="search"]').first(); if (await searchInput.isVisible().catch(() => false)) { await searchInput.fill(email); await adminPage.waitForTimeout(2000); await takeScreenshot(adminPage, "14-search-results"); } const content = await adminPage.locator("body").innerText(); return content.includes(email); } test.describe("Nxtgauge E2E Verification Flows", () => { test.setTimeout(600000); test("Company Registration → Profile → Admin Verification", async () => { const browser = await chromium.launch({ headless: false, slowMo: 100 }); const context = await browser.newContext({ viewport: { width: 1400, height: 900 }, recordVideo: { dir: VIDEO_DIR, size: { width: 1400, height: 900 } }, }); try { const company = await registerCompany(); console.log("\n🌐 MANUAL STEP: Open browser and:"); console.log(" 1. Go to http://localhost:3000/login"); console.log(" 2. Login with:"); console.log(" Email:", company.email); console.log(" Password:", company.password); console.log(" 3. Complete CAPTCHA"); console.log(" 4. Fill company profile at /dashboard/profile?role=COMPANY"); console.log(" 5. Upload business documents"); console.log(" 6. Submit for verification"); console.log(" Then press Enter to continue to admin verification..."); await context.close(); await browser.close(); console.log("\nā³ Waiting 120 seconds for manual browser steps..."); await new Promise((r) => setTimeout(r, 120000)); } catch (error) { console.error("āŒ Test failed:", error); throw error; } }); test("Job Seeker Registration → Profile → Admin Verification", async () => { const browser = await chromium.launch({ headless: false, slowMo: 100 }); const context = await browser.newContext({ viewport: { width: 1400, height: 900 }, recordVideo: { dir: VIDEO_DIR, size: { width: 1400, height: 900 } }, }); try { const jobSeeker = await registerJobSeeker(); console.log("\n🌐 MANUAL STEP: Open browser and:"); console.log(" 1. Go to http://localhost:3000/login"); console.log(" 2. Login with:"); console.log(" Email:", jobSeeker.email); console.log(" Password:", jobSeeker.password); console.log(" 3. Complete CAPTCHA"); console.log(" 4. Fill job seeker profile at /dashboard/profile?role=JOB_SEEKER"); console.log(" 5. Add education, skills, resume"); console.log(" 6. Submit for verification"); console.log(" Then press Enter to continue to admin verification..."); await context.close(); await browser.close(); console.log("\nā³ Waiting 120 seconds for manual browser steps..."); await new Promise((r) => setTimeout(r, 120000)); } catch (error) { console.error("āŒ Test failed:", error); throw error; } }); });