pelagia-portal/App/tests/e2e/dashboard/po-status-badges.js
2026-05-18 23:18:58 +05:30

303 lines
13 KiB
JavaScript

/**
* TEST: Dashboard PO Status Badges — Color-coded PoStatusBadge component
*
* Bug fixed: Dashboard "Recent Orders" (Technical/Manning submitter) and
* "Recent Approved Orders" (Manager) tables were showing all status badges as
* hardcoded plain gray (bg-neutral-100 text-neutral-700). The fix replaces the
* inline span with <PoStatusBadge status={po.status} /> which applies variant
* classes based on status:
*
* DRAFT → outline (border-neutral-300 text-neutral-600)
* SUBMITTED → secondary (bg-neutral-100 text-neutral-700)
* MGR_REVIEW → secondary (bg-neutral-100 text-neutral-700)
* VENDOR_ID_PENDING → warning (bg-warning-100 text-warning-700)
* EDITS_REQUESTED → warning (bg-warning-100 text-warning-700)
* REJECTED → danger (bg-danger-100 text-danger-700)
* MGR_APPROVED → success (bg-success-100 text-success-700)
* SENT_FOR_PAYMENT → default (bg-primary-100 text-primary-700)
* PAID_DELIVERED → success (bg-success-100 text-success-700)
* CLOSED → secondary (bg-neutral-100 text-neutral-700)
*
* What this script checks:
* 1. Technical user dashboard shows "Recent Orders" table with multiple
* distinct badge variants (not all the same gray secondary).
* 2. At least one non-secondary badge is present (DRAFT outline, MGR_APPROVED
* success, or SENT_FOR_PAYMENT primary) — confirming PoStatusBadge is live.
* 3. Manager user dashboard shows "Recent Approved Orders" table with
* differentiated badges — at least success and default/primary variants present.
* 4. Screenshots saved for each dashboard for visual confirmation.
*
* Credentials (from prisma/seed.ts):
* Technical: tech@pelagia.local / tech1234
* Manager: manager@pelagia.local / manager1234
*
* File: tests/e2e/dashboard/po-status-badges.js
* Created: 2026-05-16
*/
const { chromium } = require('playwright');
const path = require('path');
const { login } = require('../helpers/auth');
const BASE_URL = 'http://localhost:3000';
// CSS class identifiers for each badge variant (from components/ui/badge.tsx)
const VARIANT_CLASSES = {
outline: 'border-neutral-300',
secondary: 'bg-neutral-100',
success: 'bg-success-100',
warning: 'bg-warning-100',
danger: 'bg-danger-100',
default: 'bg-primary-100',
};
/**
* Collect all badge texts and their variant classes from a table's Status column.
* Returns an array of { text, classes, variant } objects.
*/
async function collectBadges(page, tableHeading) {
// Find the table that follows the given heading text
const heading = page.locator(`h2:has-text("${tableHeading}")`);
await heading.waitFor({ timeout: 8000 });
// All span badges inside this table section
const badges = page.locator(`h2:has-text("${tableHeading}") ~ div span.rounded-full`);
const count = await badges.count();
console.log(` Found ${count} badges in "${tableHeading}" table`);
const results = [];
for (let i = 0; i < count; i++) {
const el = badges.nth(i);
const text = (await el.innerText()).trim();
const classes = await el.getAttribute('class') ?? '';
let variant = 'unknown';
for (const [v, cls] of Object.entries(VARIANT_CLASSES)) {
if (classes.includes(cls)) { variant = v; break; }
}
results.push({ text, classes, variant });
console.log(` Badge [${i}]: "${text}" → variant: ${variant}`);
}
return results;
}
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
let allPassed = true;
try {
// ── PART 1: Technical user dashboard ────────────────────────────────────
console.log('\n── PART 1: Technical User Dashboard ──');
await login(page, 'tech@pelagia.local', 'tech1234');
await page.goto(`${BASE_URL}/dashboard`);
await page.waitForLoadState('networkidle');
// Confirm "Recent Orders" heading is present
const recentOrdersHeading = page.locator('h2:has-text("Recent Orders")');
const headingVisible = await recentOrdersHeading.isVisible().catch(() => false);
if (!headingVisible) {
console.error('✗ "Recent Orders" heading not found on Technical dashboard');
allPassed = false;
} else {
console.log('✓ "Recent Orders" table is present');
}
// Take screenshot before badge analysis
const screenshotDirRel = path.join(__dirname, '..', '..', '..', 'test-screenshots');
await page.screenshot({
path: path.join(screenshotDirRel, 'dashboard-technical.png'),
fullPage: true,
});
console.log('✓ Screenshot saved: test-screenshots/dashboard-technical.png');
// Collect badges from the Recent Orders table
const techBadges = await collectBadges(page, 'Recent Orders');
if (techBadges.length === 0) {
console.error('✗ No status badges found in "Recent Orders" table');
allPassed = false;
} else {
// Check that not all badges have the same variant (old hardcoded behaviour would make them all 'secondary')
const variantSet = new Set(techBadges.map(b => b.variant));
console.log(` Distinct variants found: ${[...variantSet].join(', ')}`);
if (variantSet.size <= 1 && variantSet.has('secondary')) {
console.error('✗ All badges are "secondary" — PoStatusBadge is NOT being used (old hardcoded behavior)');
allPassed = false;
} else {
console.log('✓ Multiple badge variants detected — PoStatusBadge is active');
}
// Verify specific expected statuses map to correct non-secondary variants
// Seed data for tech@pelagia.local includes:
// PO-2026-00001 MGR_REVIEW → secondary (acceptable — just must not ALL be same)
// PO-2026-00002 DRAFT → outline
// PO-2026-00003 MGR_APPROVED→ success
// PO-2026-00005 SENT_FOR_PAYMENT → default (primary)
// PO-2026-00009 SUBMITTED → secondary
const draftBadge = techBadges.find(b => b.text === 'Draft');
if (draftBadge) {
if (draftBadge.variant === 'outline') {
console.log('✓ DRAFT badge has correct "outline" variant');
} else {
console.error(`✗ DRAFT badge has wrong variant: "${draftBadge.variant}" (expected "outline")`);
allPassed = false;
}
} else {
console.log(' No DRAFT badge in view (may be outside top-8 window — not a failure)');
}
const approvedBadge = techBadges.find(b => b.text === 'Approved');
if (approvedBadge) {
if (approvedBadge.variant === 'success') {
console.log('✓ MGR_APPROVED badge has correct "success" variant');
} else {
console.error(`✗ MGR_APPROVED badge has wrong variant: "${approvedBadge.variant}" (expected "success")`);
allPassed = false;
}
} else {
console.log(' No "Approved" badge in view');
}
const sentBadge = techBadges.find(b => b.text === 'Sent for Payment');
if (sentBadge) {
if (sentBadge.variant === 'default') {
console.log('✓ SENT_FOR_PAYMENT badge has correct "default" (primary) variant');
} else {
console.error(`✗ SENT_FOR_PAYMENT badge has wrong variant: "${sentBadge.variant}" (expected "default")`);
allPassed = false;
}
} else {
console.log(' No "Sent for Payment" badge in view');
}
const reviewBadge = techBadges.find(b => b.text === 'Under Review');
if (reviewBadge) {
if (reviewBadge.variant === 'secondary') {
console.log('✓ MGR_REVIEW badge has correct "secondary" variant');
} else {
console.error(`✗ MGR_REVIEW badge has wrong variant: "${reviewBadge.variant}" (expected "secondary")`);
allPassed = false;
}
}
}
// ── PART 2: Manager dashboard ────────────────────────────────────────────
console.log('\n── PART 2: Manager Dashboard ──');
// Log out by navigating to sign-out, then log in as manager
await page.goto(`${BASE_URL}/api/auth/signout`);
// NextAuth signout page — click the button
const signoutBtn = page.locator('button[type=submit]');
if (await signoutBtn.isVisible().catch(() => false)) {
await signoutBtn.click();
await page.waitForURL('**', { timeout: 8000 });
}
await login(page, 'manager@pelagia.local', 'manager1234');
await page.goto(`${BASE_URL}/dashboard`);
await page.waitForLoadState('networkidle');
// Confirm "Recent Approved Orders" heading is present
const approvedHeading = page.locator('h2:has-text("Recent Approved Orders")');
const approvedHeadingVisible = await approvedHeading.isVisible().catch(() => false);
if (!approvedHeadingVisible) {
console.error('✗ "Recent Approved Orders" heading not found on Manager dashboard');
allPassed = false;
} else {
console.log('✓ "Recent Approved Orders" table is present');
}
await page.screenshot({
path: path.join(screenshotDirRel, 'dashboard-manager.png'),
fullPage: true,
});
console.log('✓ Screenshot saved: test-screenshots/dashboard-manager.png');
const mgrBadges = await collectBadges(page, 'Recent Approved Orders');
if (mgrBadges.length === 0) {
console.error('✗ No status badges found in "Recent Approved Orders" table');
allPassed = false;
} else {
const mgrVariantSet = new Set(mgrBadges.map(b => b.variant));
console.log(` Manager distinct variants: ${[...mgrVariantSet].join(', ')}`);
// Manager table includes MGR_APPROVED (success), SENT_FOR_PAYMENT (default), PAID_DELIVERED (success)
// Old code hardcoded success green for all — so we need to see at least primary/default too
const hasSuccess = mgrVariantSet.has('success');
const hasDefault = mgrVariantSet.has('default');
// Seed: PO-00003 MGR_APPROVED (success), PO-00005 SENT_FOR_PAYMENT (default), PO-00006 PAID_DELIVERED (success)
if (!hasSuccess) {
console.error('✗ No "success" badges on manager dashboard — expected at least MGR_APPROVED or PAID_DELIVERED');
allPassed = false;
} else {
console.log('✓ "success" variant badges present (MGR_APPROVED / PAID_DELIVERED)');
}
if (!hasDefault) {
// SENT_FOR_PAYMENT may not be in top-8; only fail if ALL are success (regression to all-green)
const allSuccess = [...mgrVariantSet].every(v => v === 'success');
if (allSuccess) {
console.error('✗ All manager badges are "success" — old hardcoded behavior (all green) has regressed');
allPassed = false;
} else {
console.log(' No "default"/primary badge visible (SENT_FOR_PAYMENT may not be in top-8 — acceptable)');
}
} else {
console.log('✓ "default" (primary/blue) variant badge present (SENT_FOR_PAYMENT)');
}
// Verify specific badge texts map to correct variants
const mgrApprovedBadge = mgrBadges.find(b => b.text === 'Approved');
if (mgrApprovedBadge) {
if (mgrApprovedBadge.variant === 'success') {
console.log('✓ Manager: "Approved" badge is "success" variant (green)');
} else {
console.error(`✗ Manager: "Approved" badge has wrong variant "${mgrApprovedBadge.variant}" (expected "success")`);
allPassed = false;
}
}
const paidBadge = mgrBadges.find(b => b.text === 'Paid');
if (paidBadge) {
if (paidBadge.variant === 'success') {
console.log('✓ Manager: "Paid" badge is "success" variant (green)');
} else {
console.error(`✗ Manager: "Paid" badge has wrong variant "${paidBadge.variant}" (expected "success")`);
allPassed = false;
}
}
const sentPaymentBadge = mgrBadges.find(b => b.text === 'Sent for Payment');
if (sentPaymentBadge) {
if (sentPaymentBadge.variant === 'default') {
console.log('✓ Manager: "Sent for Payment" badge is "default" (primary/blue) variant');
} else {
console.error(`✗ Manager: "Sent for Payment" badge has wrong variant "${sentPaymentBadge.variant}" (expected "default")`);
allPassed = false;
}
}
}
} catch (err) {
console.error('✗ Unexpected error:', err.message);
allPassed = false;
} finally {
await browser.close();
}
if (allPassed) {
console.log('\n✓ All checks passed — dashboard/po-status-badges');
process.exit(0);
} else {
console.error('\n✗ One or more checks FAILED — dashboard/po-status-badges');
process.exit(1);
}
})();