testPagesShellsAreKept

tcmts.cli.test.e2e.prod.testPagesShellsAreKept · #sxd26eli · term

tcm start renders a page's shell once and sends it at once: what renders the same every time (worked out from the code), with the fallbacks of what's live; then what's live, rendered for each request, streams in after it

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

export const testPagesShellsAreKept = () =>
  withChildren(async (keep) => {
    const dir = tempDir("shells");
    const tcm = (...args: string[]) => okIn(dir, ...args);
    tcm("init");
    tcm("lib", "add", "tcm", "server", "--at", "lib.server");
    fs.rmSync(path.join(dir, "scratch.ts"));
    tcm("lib", "add", "tcm", "react", "--at", "news.lib.react");
    fs.writeFileSync(path.join(dir, "news.tsx"), SHELL_SITE);
    tcm("add", "news.tsx");
    tcm("build", "news.App");
    const apps = path.join(dir, ".tcm", "apps");
    const built = fs.readdirSync(apps).find((f) => /^[a-z2-7]{52}$/.test(f))!;
    const manifest = JSON.parse(
      fs.readFileSync(path.join(apps, built, "tcm-app.json"), "utf8"),
    ) as { shells?: boolean };
    assert.equal(manifest.shells, true, "its browser code reads no clock: its shells are kept");
    let log = "";
    const url = await new Promise<string>((resolve, reject) => {
      const child = keep(
        spawn(process.execPath, [tcmBin(), "start", "news.App", "--port", "0"], {
          cwd: dir,
          env: plainEnv(),
        }),
      );
      const seen = (d: string) => {
        log += d;
        const m = /url\s+(http:\/\/\S+)/.exec(log);
        if (m) resolve(m[1]!);
      };
      child.stdout.setEncoding("utf8").on("data", seen);
      child.stderr.setEncoding("utf8").on("data", seen);
      child.on("exit", (code) => reject(new Error(`tcm start exited ${code}:\n${log}`)));
    });
    // a page, as it arrives: when what's in it first came
    const timed = async () => {
      const started = performance.now();
      const res = await fetch(url, { headers: { accept: "text/html" } });
      const reader = res.body!.getReader();
      const text = new TextDecoder();
      let html = "";
      const at: Record<string, number> = {};
      for (;;) {
        const { value, done } = await reader.read();
        if (done) break;
        html += text.decode(value, { stream: true });
        for (const mark of ["<h1>News</h1>", "counting", "views</p>"])
          if (at[mark] === undefined && html.includes(mark)) at[mark] = performance.now() - started;
      }
      return { html, at };
    };
    // its code, said at once, as Early Hints
    const hinted = await new Promise<string>((resolve, reject) => {
      const r = http.get(url, { headers: { accept: "text/html" } }, (res) => {
        res.resume();
        res.on("end", () => resolve(""));
      });
      r.on("information", (info) => {
        if (info.statusCode === 103) resolve(String(info.headers.link ?? ""));
      });
      r.on("error", reject);
    });
    assert.match(hinted, /<\/_app\/assets\/[^>]+\.js>; rel=modulepreload/);
    const count = (page: string) =>
      Number(/(\d+)(?:<!-- -->)? views<\/p>/.exec(page)?.[1] ?? "none");
    const once = await timed();
    const { html, at } = await timed();
    assert.ok(
      at["<h1>News</h1>"]! < 300,
      `the shell at once (${Math.round(at["<h1>News</h1>"]!)}ms)`,
    );
    // (marks that came in one piece came together)
    assert.ok(at["counting"]! - at["<h1>News</h1>"]! < 5, "with the live part's fallback");
    assert.ok(
      at["views</p>"]! - at["<h1>News</h1>"]! > 250,
      `and the live part after it, rendered for this request: ${JSON.stringify(at)}\n${log}`,
    );
    assert.ok(count(once.html) >= 1, once.html);
    assert.equal(
      count(html),
      count(once.html) + 1,
      `each request's own: ${count(once.html)}, then ${count(html)}`,
    );
    assert.match(html, /var __TCM_TREES__ = /);
    if (!CHROME) return console.log("skipped the browser: no Chrome");
    await withPage(url, async (p) => {
      assert.ok(await hydrated(p));
      assert.match(
        await p.waitFor<string>(`document.querySelector(".views")?.textContent ?? ""`, (t) =>
          /\d views/.test(t ?? ""),
        ),
        new RegExp(`${count(html) + 1} views`),
      );
      assert.equal(await p.evaluate(`document.querySelector("h1").textContent`), "News");
      assert.deepEqual(p.errors, [], "it hydrates as it was sent");
    });
  })

uses