nxtgauge-frontend-solid/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts

307 lines
12 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string | null> {
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<TestUser> {
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();
});
});