303 lines
13 KiB
JavaScript
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);
|
|
}
|
|
})();
|