/** * 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 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); } })();