testPagesKeepAndRenderAhead

tcmts.cli.test.e2e.react.testPagesKeepAndRenderAhead · #axckxolx · term

The react library's Pages: the page left stays mounted, hidden, so going back shows it as it was; and a page a link to is pointed at is rendered ahead, hidden, before it's followed

import { CHROME, hydrated, withPage } from "#codebase/tcmts/cli/test/browser";
import { okIn, plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness";
import { KEPT_PAGES } from "#codebase/tcmts/cli/test/e2e/react";
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import fs from "node:fs";
import path from "node:path";

export const testPagesKeepAndRenderAhead = () =>
  withChildren(async (keep) => {
    if (!CHROME) return console.log("skipped: no Chrome");
    const dir = tempDir("kept pages");
    const tcm = (...args: string[]) => okIn(dir, ...args);
    tcm("init");
    fs.rmSync(path.join(dir, "scratch.ts"));
    tcm("lib", "add", "tcm", "react", "--at", "kept.lib.react");
    fs.writeFileSync(path.join(dir, "kept.tsx"), KEPT_PAGES);
    tcm("add", "kept.tsx");
    const url = await new Promise<string>((resolve, reject) => {
      const child = keep(
        spawn(process.execPath, [tcmBin(), "start", "kept.App", "--port", "0"], {
          cwd: dir,
          env: plainEnv(),
        }),
      );
      let out = "";
      child.stdout.setEncoding("utf8").on("data", (d: string) => {
        out += d;
        const m = /url\s+(http:\/\/\S+)/.exec(out);
        if (m) resolve(m[1]!);
      });
      child.on("exit", (code) => reject(new Error(`tcm start exited ${code}:\n${out}`)));
    });
    await withPage(url, async (p) => {
      assert.ok(await hydrated(p));
      // what's shown: the sections not hidden; and all of them, hidden ones too
      const shown = `[...document.querySelectorAll("section")].filter((s) => s.offsetParent !== null).map((s) => s.textContent).join(",")`;
      const all = `[...document.querySelectorAll("section")].map((s) => s.querySelector("h1").textContent).sort().join(",")`;
      const link = (text: string) =>
        `[...document.querySelectorAll("a")].find((a) => a.textContent === ${JSON.stringify(text)})`;
      await p.evaluate(`document.querySelector("button").click()`);
      await p.waitFor<string>(shown, (t) => t === "home1");
      await p.evaluate(`document.querySelector("button").click()`);
      assert.equal(await p.waitFor<string>(shown, (t) => t === "home2"), "home2");
      await p.evaluate(`${link("one")}.click()`);
      assert.equal(await p.waitFor<string>(shown, (t) => t === "one0"), "one0");
      assert.equal(await p.evaluate<string>(all), "home,one", "the page left, kept");
      await p.evaluate("history.back()");
      assert.equal(
        await p.waitFor<string>(shown, (t) => t.startsWith("home")),
        "home2",
        "as it was",
      );
      // pointed at: rendered ahead, hidden
      await p.evaluate(
        `${link("two")}.dispatchEvent(new PointerEvent("pointerover", { bubbles: true }))`,
      );
      assert.equal(
        await p.waitFor<string>(all, (t) => t.includes("two")),
        "home,one,two",
        "rendered ahead",
      );
      assert.equal(await p.evaluate<string>(shown), "home2", "and not shown");
      await p.evaluate(`${link("two")}.click()`);
      assert.equal(await p.waitFor<string>(shown, (t) => t === "two0"), "two0");
      assert.deepEqual(p.errors, []);
    });
  })

uses