nxtgauge-frontend-solid/tests/e2e/job-seeker-e2e-full.spec.ts

270 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 } 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/job-seeker-e2e";
if (!fs.existsSync(SCREENSHOT_DIR)) {
fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
}
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;
}
}
test.describe("Job Seeker E2E Full Flow", () => {
test("complete job seeker registration → OTP → login → dashboard → profile → verification", async () => {
const testEmail = `e2ejobseeker${randomUUID().slice(0, 8)}@test.com`;
const testPassword = "TestPassword123!";
console.log("📧 Email:", testEmail);
const browser = await chromium.launch({ headless: false, slowMo: 30 });
const context = await browser.newContext({ viewport: { width: 1400, height: 900 } });
// ==================== STEP 1: REGISTER VIA API ====================
console.log("\n📝 STEP 1: Register via API");
let regData: any;
try {
const regResponse = await fetch("http://localhost:9100/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: testEmail, first_name: "Jane", last_name: "Smith", password: testPassword, intent: "job_seeker" })
});
regData = await regResponse.json();
expect(regData.user_id).toBeTruthy();
console.log(" ✅ PASS: Registration successful, user_id:", regData.user_id);
} catch (e: any) {
console.log(" ❌ FAIL: Registration failed -", e.message);
throw e;
}
// ==================== STEP 2: OTP VIA REDIS ====================
console.log("\n🔐 STEP 2: OTP via Redis");
await new Promise(r => setTimeout(r, 500));
let otpCode: string | null = null;
try {
otpCode = await getOTPFromRedis(regData.user_id);
expect(otpCode).toBeTruthy();
console.log(" ✅ PASS: OTP retrieved from Redis:", otpCode);
} catch (e: any) {
console.log(" ❌ FAIL: Could not get OTP -", e.message);
throw e;
}
// ==================== STEP 3: VERIFY OTP VIA API ====================
console.log("\n✅ STEP 3: Verify OTP via API");
try {
const verifyResponse = await fetch("http://localhost:9100/api/auth/verify-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user_id: regData.user_id, otp: otpCode })
});
const verifyData = await verifyResponse.json();
expect(verifyResponse.ok).toBe(true);
console.log(" ✅ PASS: OTP verified! Response:", JSON.stringify(verifyData));
} catch (e: any) {
console.log(" ❌ FAIL: OTP verification failed -", e.message);
throw e;
}
// ==================== STEP 4: LOGIN VIA API ====================
console.log("\n🔑 STEP 4: Login via API");
let accessToken = "";
try {
const loginResponse = await fetch("http://localhost:9100/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: testEmail, password: testPassword })
});
const loginData = await loginResponse.json();
accessToken = loginData.access_token || "";
expect(accessToken).toBeTruthy();
console.log(" ✅ PASS: Login successful, token length:", accessToken.length);
} catch (e: any) {
console.log(" ❌ FAIL: Login failed -", e.message);
throw e;
}
// ==================== STEP 5: DASHBOARD ====================
console.log("\n🌐 STEP 5: Navigate to dashboard?role=JOB_SEEKER");
const page = await context.newPage();
await page.addInitScript(({ token, email, userId }) => {
localStorage.setItem("nxtgauge_access_token", token);
localStorage.setItem("nxtgauge_user", JSON.stringify({
email, roleKey: "JOB_SEEKER", role: "JOB_SEEKER", active_role: "JOB_SEEKER",
selectedProfessionalRole: "JOB_SEEKER", name: "Jane Smith", fullName: "Jane Smith", id: userId
}));
localStorage.setItem("nxtgauge_auth_user", JSON.stringify({
email, roleKey: "JOB_SEEKER", role: "JOB_SEEKER", active_role: "JOB_SEEKER",
selectedProfessionalRole: "JOB_SEEKER", name: "Jane Smith", fullName: "Jane Smith", id: userId
}));
sessionStorage.setItem("nxtgauge_access_token", token);
}, { token: accessToken, email: testEmail, userId: regData.user_id });
try {
await page.goto("http://localhost:3000/dashboard?role=JOB_SEEKER", { waitUntil: "networkidle", timeout: 15000 });
await page.waitForTimeout(3000);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step05_dashboard.png`, fullPage: true });
console.log(" ✅ PASS: Dashboard loaded");
} catch (e: any) {
console.log(" ❌ FAIL: Dashboard load failed -", e.message);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step05_dashboard_FAIL.png`, fullPage: true });
throw e;
}
// ==================== STEP 6: PROFILE FORM ====================
console.log("\n📋 STEP 6: Navigate to profile form (click 'My Profile')");
try {
const profileBtn = page.getByRole("button", { name: /my profile/i });
const isVisible = await profileBtn.isVisible().catch(() => false);
if (isVisible) {
await profileBtn.click();
await page.waitForTimeout(3000);
} else {
console.log(" ⚠️ My Profile button not visible, trying direct navigation");
await page.goto("http://localhost:3000/dashboard/profile?role=JOB_SEEKER", { waitUntil: "networkidle" });
await page.waitForTimeout(3000);
}
await page.screenshot({ path: `${SCREENSHOT_DIR}/step06_profile_form.png`, fullPage: true });
console.log(" ✅ PASS: Profile form displayed");
} catch (e: any) {
console.log(" ⚠️ WARN: Profile navigation -", e.message);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step06_profile_form.png`, fullPage: true });
}
// ==================== STEP 6B: FILL PROFILE FORM ====================
console.log("\n📝 STEP 6b: Fill job seeker profile fields");
try {
const inputs = page.locator("input");
const count = await inputs.count();
console.log(" Total inputs found:", count);
// Log all inputs with their attributes
for (let i = 0; i < Math.min(count, 15); i++) {
const input = inputs.nth(i);
const isVisible = await input.isVisible().catch(() => false);
const isDisabled = await input.isDisabled().catch(() => false);
if (isVisible && !isDisabled) {
const placeholder = await input.getAttribute("placeholder").catch(() => "");
const id = await input.getAttribute("id").catch(() => "");
const name = await input.getAttribute("name").catch(() => "");
const type = await input.getAttribute("type").catch(() => "");
console.log(` Input ${i}: type="${type}" placeholder="${placeholder}" id="${id}" name="${name}"`);
}
}
// Try to find and fill common job seeker fields
const nameFields = ["first_name", "firstName", "First Name", "first-name"];
const lastNameFields = ["last_name", "lastName", "Last Name", "last-name"];
const emailField = page.locator('input[name="email"], input[id="email"]').first();
const phoneField = page.locator('input[name="phone"], input[id="phone"], input[placeholder*="phone" i]').first();
const locationField = page.locator('input[name="location"], input[id="location"], input[placeholder*="location" i]').first();
if (await emailField.isVisible().catch(() => false)) {
console.log(" Email field already has:", await emailField.inputValue().catch(() => ""));
}
// Fill phone
if (await phoneField.isVisible().catch(() => false)) {
await phoneField.fill("9876543210");
console.log(" ✅ Filled phone");
}
// Fill location
if (await locationField.isVisible().catch(() => false)) {
await locationField.fill("Chennai");
console.log(" ✅ Filled location");
}
// Try textareas too
const textareas = page.locator("textarea");
const textareaCount = await textareas.count();
console.log(" Total textareas found:", textareaCount);
for (let i = 0; i < textareaCount; i++) {
const ta = textareas.nth(i);
const isVisible = await ta.isVisible().catch(() => false);
if (isVisible) {
const placeholder = await ta.getAttribute("placeholder").catch(() => "");
const name = await ta.getAttribute("name").catch(() => "");
console.log(` Textarea ${i}: placeholder="${placeholder}" name="${name}"`);
}
}
await page.screenshot({ path: `${SCREENSHOT_DIR}/step06b_profile_inputs.png`, fullPage: true });
console.log(" ✅ Profile form analyzed");
} catch (e: any) {
console.log(" ⚠️ WARN: Could not fill profile -", e.message);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step06b_profile_fill_FAIL.png`, fullPage: true });
}
// ==================== STEP 7: SUBMIT VERIFICATION ====================
console.log("\n📤 STEP 7: Submit verification");
try {
const submitBtn = page.getByRole("button", { name: /submit for verification/i });
const btnVisible = await submitBtn.isVisible().catch(() => false);
if (btnVisible) {
const isDisabled = await submitBtn.isDisabled().catch(() => true);
if (isDisabled) {
console.log(" ⚠️ INFO: Submit button disabled - profile needs more fields filled");
await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_submit_disabled.png`, fullPage: true });
// Try Documents tab
const docsTab = page.getByRole("tab", { name: /documents/i }).first();
if (await docsTab.isVisible().catch(() => false)) {
await docsTab.click();
await page.waitForTimeout(2000);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_documents_tab.png`, fullPage: true });
console.log(" ✅ Switched to Documents tab");
}
} else {
await submitBtn.click();
await page.waitForTimeout(3000);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_verification_submitted.png`, fullPage: true });
console.log(" ✅ PASS: Verification submitted!");
}
} else {
console.log(" ⚠️ INFO: Submit button not found - checking verification status");
const statusText = await page.locator("body").innerText().catch(() => "");
if (statusText.includes("Submitted") || statusText.includes("verification")) {
console.log(" ✅ Already on verification page or already submitted");
}
await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_submit_notfound.png`, fullPage: true });
}
} catch (e: any) {
console.log(" ⚠️ WARN: Verification submit -", e.message);
await page.screenshot({ path: `${SCREENSHOT_DIR}/step07_verification_FAIL.png`, fullPage: true });
}
await page.screenshot({ path: `${SCREENSHOT_DIR}/step99_final.png`, fullPage: true });
console.log("\n========== TEST COMPLETE ==========");
console.log("📸 Screenshots:", SCREENSHOT_DIR);
console.log("📧 Test email:", testEmail);
console.log("🔑 Password:", testPassword);
console.log("👤 Name: Jane Smith");
await new Promise(r => setTimeout(r, 1000));
await browser.close();
});
});