Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives: publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style column wall with hostile-content-safe rendering, live control room with Auto Director, bridged chat governance with polls and quorum, distributed sandboxed transcode workers, encrypted content-addressed archive, monthly analytics. MIT licensed. See README.md and docs/.
This commit is contained in:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
75
apps/web/scripts/visual-qa.mjs
Normal file
75
apps/web/scripts/visual-qa.mjs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { chromium } from "playwright";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const BASE = process.env.QA_BASE ?? "http://localhost:3111";
|
||||
const OUT = "runs/qa";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
|
||||
|
||||
async function shot(name, { fullPage = false } = {}) {
|
||||
await page.waitForTimeout(350);
|
||||
await page.screenshot({ path: `${OUT}/${name}.png`, fullPage });
|
||||
console.log(`✓ ${name}`);
|
||||
}
|
||||
|
||||
console.log("console errors will be collected at the end");
|
||||
const errors = [];
|
||||
page.on("console", (msg) => {
|
||||
if (msg.type() === "error") errors.push(msg.text().slice(0, 200));
|
||||
});
|
||||
page.on("pageerror", (err) => errors.push(`PAGEERROR: ${String(err).slice(0, 200)}`));
|
||||
|
||||
await page.goto(`${BASE}/streams`);
|
||||
await shot("01-streams");
|
||||
|
||||
await page.keyboard.press("j");
|
||||
await page.keyboard.press("j");
|
||||
await page.keyboard.press("j");
|
||||
await shot("02-streams-selected");
|
||||
|
||||
await page.keyboard.press("r");
|
||||
await shot("03-streams-repost-toast");
|
||||
|
||||
await page.keyboard.press("q");
|
||||
await shot("04-streams-quote-popover");
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await page.keyboard.press("j");
|
||||
await page.keyboard.press("k");
|
||||
await page.keyboard.press("k");
|
||||
await shot("05-streams-keyboard-moved");
|
||||
|
||||
await page.goto(`${BASE}/compose`);
|
||||
await page.locator(".ProseMirror").click();
|
||||
await page.keyboard.type("Shipping the visual QA pass — capability-gated amplify, keyboard-first streams.");
|
||||
await shot("06-compose-typed");
|
||||
|
||||
await page.goto(`${BASE}/connections`);
|
||||
await shot("07-connections");
|
||||
|
||||
await page.goto(`${BASE}/inbox`);
|
||||
await shot("08-inbox");
|
||||
|
||||
await page.goto(`${BASE}/team`);
|
||||
await shot("09-team");
|
||||
|
||||
await page.goto(`${BASE}/audit`);
|
||||
await shot("10-audit");
|
||||
await page.locator("tr", { hasText: "credential.rotated" }).first().click();
|
||||
await shot("11-audit-diff-open");
|
||||
|
||||
await page.goto(`${BASE}/onboarding`);
|
||||
await shot("12-onboarding-step1");
|
||||
await page.getByPlaceholder("Studio, Newsroom, Acme Social…").fill("Studio");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await shot("13-onboarding-step2");
|
||||
|
||||
await page.goto(`${BASE}/streams`);
|
||||
await page.keyboard.press("Meta+k");
|
||||
await shot("14-command-palette");
|
||||
|
||||
await browser.close();
|
||||
console.log(`\nconsole errors: ${errors.length}`);
|
||||
for (const e of errors.slice(0, 10)) console.log(` - ${e}`);
|
||||
Loading…
Add table
Add a link
Reference in a new issue