bidaangmamamayan/tests/ui-check.mjs
Ryan 70a23bbd46 BidaAngMamayan: verified-publisher gate + In Pictures + standalone board band
Three changes per Daniel (2026-09-15):

1. VERIFIED PUBLISHERS ONLY — lib/sources.js gains VERIFIED_HOSTS
   (established PH newsrooms + state wire + international) and
   publisherVerified(url); lib/crawl.js now drops any story whose
   article URL host is not on the allowlist BEFORE the relevance gate
   (skippedUnverified counter surfaces in the crawl result + cron log).
   Tabloids/blogs/radio (bomboradyo, unboxph, bnc.bilyonaryo, dzrh,
   thechronicle, …) can no longer reach the desk. Quiet-day fallback
   wires expanded to Inquirer/Rappler/GMA/ABS-CBN/Interaksyon/Manila
   Bulletin (all verified).

2. IN PICTURES — a new prominent full-width band on the front page:
   7 real public-domain photos of Sen. BAM (official Senate/Facebook
   pages via Wikimedia Commons), each credited to its source with a
   link out to the Commons file page. lib/photos.js holds the curated,
   URL-verified set.

3. BOARD AS ITS OWN AREA — the 'Thank You, Senator BAM' community board
   is now a distinct gold-edged full-width band (front page + its own
   view), clearly separate from the news desks. The nav 'Thank You
   Board' button is gold (not a category tab) and jumps to the band.

Tests: publisher-gate unit checks (22/22), ui-check.mjs updated to
verify the In Pictures band (>=5 credited photos) + front-page board
band posting; e2e + board suites still green.
2026-09-16 15:04:10 +00:00

165 lines
8.4 KiB
JavaScript

#!/usr/bin/env node
// Playwright visual check for the BAM front page (headless Chromium).
// Seeds the bama_ui scratch DB with realistic stories first, then:
// - loads / (asserts no console/page errors, masthead present)
// - screenshots the front page
// - clicks a story card -> reader opens with body
// - switches to the board view, posts a thank-you, asserts it renders
// - switches to a desk view
import pw from "playwright";
import { MongoClient } from "mongodb";
const BASE = process.env.BASE || "http://127.0.0.1:4005";
const OUT = "/root/projects/bidaangmamamayan/tests/shots";
import { mkdirSync } from "fs";
mkdirSync(OUT, { recursive: true });
// ── seed (shape MUST match what lib/crawl.js stores, or the feed query
// `discoveredAt >= now-14d` returns nothing) ───────────────────────────────
const mc = new MongoClient("mongodb://127.0.0.1:27099/bama");
await mc.connect();
const db = mc.db("bama_ui");
await db.collection("stories").deleteMany({});
const now = Date.now();
const H = 3600e3;
const mk = (o) => ({
url: o.url, title: o.title, summary: o.description, image: null,
source: o.source, category: o.category, desk: "BAM Wire", feedKind: "query",
named: !!o.named, fallback: false,
published: new Date(o.published),
discoveredAt: new Date(o.published + H), // discovered an hour after publish
ingestedAt: new Date(o.published + H),
});
const rawSeed = [
{
url: "https://www.inquirer.net/bam/1", title: "Sen. Bam Aquino unveils college scholarship expansion for Pampanga students",
description: "Senator Bam Aquino on Tuesday unveiled the expanded scholarship program that will fund 50,000 college students across the country, calling education 'the great equalizer'.",
source: "Inquirer.net", published: now - 2 * H, named: true, category: "education",
},
{
url: "https://www.pna.gov.ph/bam/2", title: "Aquino echoes House call to probe VisMin power outages, rates",
description: "Senator Bam Aquino backed the congressional inquiry into rotational brownouts in the Visayas, urging regulators to audit system loss charges.",
source: "Philippine News Agency", published: now - 5 * H, named: true, category: "energy",
},
{
url: "https://rappler.com/bam/3", title: "Bam Aquino's esports bill clears Senate committee",
description: "The bill that would recognize esports as a sport and fund university esports programs passed the committee, with Sen. Bam Aquino as prime sponsor.",
source: "Rappler", published: now - 8 * H, named: true, category: "gaming",
},
{
url: "https://www.pna.gov.ph/bam/4", title: "Aquino joins senators in okays of barangay, SK terms bill",
description: "The Senate approved the bill setting five-year terms for barangay and SK officials on third reading.",
source: "Philippine News Agency", published: now - 20 * H, named: false, category: "politics",
},
{
url: "https://www.inquirer.net/bam/5", title: "Bam Aquino pushes student loan reform for higher education",
description: "The Pampanga senator said the reform would cap interest rates on student loans and extend repayment windows for fresh graduates.",
source: "Inquirer.net", published: now - 30 * H, named: true, category: "education",
},
{
url: "https://www.inquirer.net/bam/6", title: "Senate okays 5-year barangay, SK terms; 2026 polls moved to 2028",
description: "The upper house approved the measure on third reading.",
source: "Inquirer.net", published: now - 40 * H, named: false, category: "politics",
},
{
url: "https://rappler.com/bam/7", title: "Bam Aquino calls for fairer power rate adjustment mechanism",
description: "The senator said consumers should not shoulder transmission losses.",
source: "Rappler", published: now - 50 * H, named: true, category: "energy",
},
{
url: "https://www.inquirer.net/bam/8", title: "Gaming: Bamboo Aquino's e-sports arena bill heads to plenary",
description: "The measure will fund e-sports facilities in state colleges and universities nationwide.",
source: "Inquirer.net", published: now - 60 * H, named: false, category: "gaming",
},
];
const seed = rawSeed.map(mk);
await db.collection("stories").insertMany(seed);
await db.collection("board").deleteMany({});
console.log("seeded", seed.length, "stories");
// ── browser ──────────────────────────────────────────────────────────────
const browser = await pw.chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } });
const errors = [];
page.on("pageerror", (e) => errors.push("pageerror: " + e.message));
page.on("console", (m) => { if (m.type() === "error") errors.push("console: " + m.text()); });
await page.goto(BASE, { waitUntil: "domcontentloaded", timeout: 30000 });
await page.waitForTimeout(2500); // feed fetch + render
const masthead = await page.textContent("body");
if (!/BIDA ANG MAMAMAYAN/.test(masthead || "")) errors.push("masthead missing");
if (!/BAM/.test(masthead || "")) errors.push("BAM highlight missing");
await page.screenshot({ path: OUT + "/01-front.png" });
console.log("front page screenshot done");
// open the lead story
const lead = page.locator(".panel.lead");
if (await lead.count()) {
await lead.click();
await page.waitForTimeout(1500);
await page.screenshot({ path: OUT + "/02-reader.png" });
const readerText = await page.textContent("body");
if (!/SCHOLARSHIP|scholarship|PAMPANGA|Pampanga/i.test(readerText || "")) errors.push("reader did not open lead story");
await page.keyboard.press("Escape");
await page.waitForTimeout(400);
} else {
errors.push("no lead story card");
}
// In Pictures band — credited photos, its own area (not a desk)
const photosBand = page.locator(".photos-band");
if (await photosBand.count()) {
const photos = await photosBand.locator(".photo").count();
if (photos < 5) errors.push("expected >=5 credited photos, got " + photos);
const credText = await photosBand.textContent();
if (!/Public domain/i.test(credText)) errors.push("photo source credit missing");
if (!/official/i.test(credText)) errors.push("photo 'official' source label missing");
await photosBand.screenshot({ path: OUT + "/03a-in-pictures.png" }).catch(() => {});
} else {
errors.push("In Pictures band missing");
}
// the front-page BOARD band (its own area, not a desk) — post a thank-you
await page.locator("#board-band").scrollIntoViewIfNeeded().catch(() => {});
await page.waitForTimeout(300);
const boardBand = page.locator("#board-band");
if (await boardBand.count()) {
await page.fill("#bnick", "PampangaGamer");
await page.fill("#bmsg", "Thank you Senator BAM for the scholarship expansion — my whole batch is riding on it!");
await boardBand.locator("button.go").click();
await page.waitForTimeout(1200);
await boardBand.screenshot({ path: OUT + "/04-board-posted.png" }).catch(() => {});
const boardText = await boardBand.textContent();
if (!/PampangaGamer/.test(boardText || "")) errors.push("board band: posted nick not rendered");
if (!/Thank you Senator BAM for the scholarship/.test(boardText || "")) errors.push("board band: posted message not rendered");
} else {
errors.push("front-page board band missing");
}
// back to front, then a desk view — click the Education desk card (grid lives on front)
await page.getByRole("link", { name: "Front", exact: true }).click();
await page.waitForTimeout(400);
const eduCard = page.locator(".deskcard", { hasText: /Education/ }).first();
if (await eduCard.count()) {
await eduCard.click();
await page.waitForTimeout(600);
await page.screenshot({ path: OUT + "/05-desk-education.png" });
const deskBody = await page.textContent("main");
if (!/scholarship|Scholarship|student|Student/i.test(deskBody || "")) errors.push("education desk view shows no education story");
} else {
errors.push("education desk card missing from grid");
}
// desktop + mobile front
await page.getByRole("link", { name: "Front", exact: true }).click();
await page.waitForTimeout(500);
await page.setViewportSize({ width: 390, height: 844 });
await page.waitForTimeout(600);
await page.screenshot({ path: OUT + "/06-front-mobile.png" });
console.log(errors.length ? "UI ERRORS:\n" + errors.join("\n") : "UI CLEAN — no console/page errors");
await browser.close();
await mc.close();
process.exit(errors.length ? 1 : 0);