File size: 3,548 Bytes
88d2f2a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// V2: Per-browser trigger test — waits for hydration via networkidle (best
// effort), captures the POST response, and uses waitForURL with a 90s
// budget. Sequential per browser to avoid hammering the backend.
const fs = require("fs");
const path = require("path");
const { chromium, firefox, webkit } = require("playwright");

const BASE_URL = process.env.BASE_URL || "http://localhost:3001";
const OUT_DIR = path.resolve(__dirname, "..", "..", "outputs");
const SHOT_DIR = path.join(OUT_DIR, "screenshots");

const ENGINES = [
  { name: "chromium", launcher: chromium },
  { name: "firefox", launcher: firefox },
  { name: "webkit", launcher: webkit },
];

async function run(engine) {
  const browser = await engine.launcher.launch({ headless: true });
  const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 }, colorScheme: "dark" });
  const page = await ctx.newPage();
  const errors = [];
  const triggerPosts = [];
  page.on("console", (m) => m.type() === "error" && errors.push(m.text()));
  page.on("pageerror", (e) => errors.push(`pageerror: ${e.message}`));
  page.on("requestfinished", async (req) => {
    if (req.method() === "POST" && /trigger\/event/.test(req.url())) {
      try {
        const resp = await req.response();
        const status = resp?.status();
        let body = null;
        try { body = await resp?.text(); } catch (_) {}
        triggerPosts.push({ url: req.url(), status, body: body?.slice(0, 200) });
      } catch (e) { triggerPosts.push({ url: req.url(), error: e.message }); }
    }
  });

  const out = {
    browser: engine.name, postCount: 0, postStatus: null, postBody: null,
    waitForURLSucceeded: false, finalUrl: null, msToNavigate: null,
    spinnerSeen: false, errors: [],
  };

  try {
    await page.goto(`${BASE_URL}/`, { waitUntil: "domcontentloaded", timeout: 30000 });
    // Wait for hydration: either networkidle within 5s or 1500ms grace if SSE keeps it open.
    await page.waitForLoadState("networkidle", { timeout: 5000 }).catch(async () => {
      await page.waitForTimeout(1500);
    });
    const btn = page.getByRole("button", { name: /trigger.*live demo/i });
    await btn.waitFor({ state: "visible", timeout: 10000 });
    const t0 = Date.now();
    await btn.click();
    try { await page.waitForSelector(".animate-spin", { timeout: 2500 }); out.spinnerSeen = true; } catch (_) {}
    try {
      await page.waitForURL(/\/events\/\d+/, { timeout: 90000 });
      out.waitForURLSucceeded = true;
      out.finalUrl = page.url();
      out.msToNavigate = Date.now() - t0;
    } catch (err) {
      out.errors.push(`waitForURL: ${err.message.split("\n")[0]}`);
    }
    out.postCount = triggerPosts.length;
    if (triggerPosts.length) {
      out.postStatus = triggerPosts[0].status;
      out.postBody = triggerPosts[0].body;
    }
    const shot = path.join(SHOT_DIR, `xbrowser_${engine.name}_trigger_v2.png`);
    await page.screenshot({ path: shot });
    out.screenshot = path.relative(OUT_DIR, shot);
  } catch (err) {
    out.errors.push(err.message);
  } finally {
    out.errors.push(...errors.slice(0, 3));
    await ctx.close();
    await browser.close();
  }
  return out;
}

(async () => {
  const all = [];
  for (const engine of ENGINES) {
    console.log(`-- ${engine.name} --`);
    const r = await run(engine);
    console.log(JSON.stringify(r));
    all.push(r);
  }
  const out = path.join(OUT_DIR, "cross_browser_trigger_v2.json");
  fs.writeFileSync(out, JSON.stringify(all, null, 2));
  console.log(`Wrote ${out}`);
})();