all repos — sz-chrome-remote-cdp @ dcb79fc62bdd4d9ec0cbdb2c97d3eb2b2dacdf22

pi-browser-harness, patched to drive a Chrome on another host over CDP, with castle_cdp_search web search

test/daemon/queue-test.ts (view raw)

 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
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { makeEventQueue, rejectAllPending, sendWithTimeout, type Pending } from "../../src/daemon/queue";

describe("event queue iteration", () => {
  test("yields pushed events in order", async () => {
    const q = makeEventQueue();
    q.push({ method: "Page.loadEventFired", params: {} });
    q.push({ method: "Page.frameNavigated", params: {} });
    const seen: string[] = [];
    for await (const ev of q.iter) {
      seen.push(ev.method);
      if (seen.length === 2) break;
    }
    assert.deepEqual(seen, ["Page.loadEventFired", "Page.frameNavigated"]);
  });

  test("ending the queue terminates the iteration after draining the buffer", async () => {
    const q = makeEventQueue();
    q.push({ method: "Page.loadEventFired", params: {} });
    q.end();
    const seen: string[] = [];
    for await (const ev of q.iter) seen.push(ev.method);
    assert.deepEqual(seen, ["Page.loadEventFired"]);
  });

  test("a waiter pending at end() is released", async () => {
    const q = makeEventQueue();
    const done = (async () => {
      const seen: string[] = [];
      for await (const ev of q.iter) seen.push(ev.method);
      return seen;
    })();
    await Promise.resolve();
    q.end();
    assert.deepEqual(await done, []);
  });

  test("push after end() is dropped", async () => {
    const q = makeEventQueue();
    q.end();
    q.push({ method: "Page.loadEventFired", params: {} });
    const seen: string[] = [];
    for await (const ev of q.iter) seen.push(ev.method);
    assert.deepEqual(seen, []);
  });

  test("a waiter is handed the next pushed event directly", async () => {
    const q = makeEventQueue();
    const it = q.iter[Symbol.asyncIterator]();
    const pending = it.next();
    await Promise.resolve();
    q.push({ method: "Runtime.consoleAPICalled", params: { a: 1 } });
    const r = await pending;
    assert.equal(r.done, false);
    assert.deepEqual(r.value, { method: "Runtime.consoleAPICalled", params: { a: 1 } });
  });

  test("the terminal result reports done with an undefined value", async () => {
    const q = makeEventQueue();
    q.end();
    const it = q.iter[Symbol.asyncIterator]();
    const r = await it.next();
    assert.equal(r.done, true);
    assert.equal(r.value, undefined);
  });

  test("sessionId is preserved on yielded events", async () => {
    const q = makeEventQueue();
    q.push({ method: "Page.loadEventFired", params: {}, sessionId: "s1" });
    q.end();
    const seen: Array<string | undefined> = [];
    for await (const ev of q.iter) seen.push(ev.sessionId);
    assert.deepEqual(seen, ["s1"]);
  });
});

describe("pending request bookkeeping", () => {
  test("rejectAllPending settles every entry with transport_closed and clears the map", () => {
    const pending = new Map<number, Pending>();
    const settled: string[] = [];
    for (const id of [1, 2]) {
      pending.set(id, {
        resolve: (r) => settled.push(r.success ? "ok" : r.error.kind),
        timer: setTimeout(() => {}, 10_000),
        method: `M.m${id}`,
      });
    }
    rejectAllPending(pending, "gone");
    assert.deepEqual(settled, ["transport_closed", "transport_closed"]);
    assert.equal(pending.size, 0);
  });

  test("sendWithTimeout resolves transport_closed when send throws synchronously", async () => {
    const pending = new Map<number, Pending>();
    const r = await sendWithTimeout(pending, 7, "M.m", 10_000, "Test", () => {
      throw new Error("socket gone");
    });
    assert.equal(r.success, false);
    if (!r.success) {
      assert.equal(r.error.kind, "transport_closed");
      assert.equal(r.error.message, "socket gone");
      assert.equal(r.error.method, "M.m");
    }
    assert.equal(pending.size, 0);
  });

  test("sendWithTimeout registers a pending entry the caller can resolve", async () => {
    const pending = new Map<number, Pending>();
    let sent = false;
    const p = sendWithTimeout(pending, 9, "M.m", 10_000, "Test", () => {
      sent = true;
    });
    assert.equal(sent, true);
    const entry = pending.get(9);
    assert.notEqual(entry, undefined);
    if (entry) {
      clearTimeout(entry.timer);
      entry.resolve({ success: true, data: { hello: "world" } });
    }
    const r = await p;
    assert.equal(r.success, true);
    if (r.success) assert.deepEqual(r.data, { hello: "world" });
  });

  test("sendWithTimeout resolves a timeout error and drops the pending entry", async () => {
    const pending = new Map<number, Pending>();
    const r = await sendWithTimeout(pending, 11, "M.slow", 1, "Test", () => {});
    assert.equal(r.success, false);
    if (!r.success) {
      assert.equal(r.error.kind, "timeout");
      assert.match(r.error.message, /Test timeout after 1ms: M\.slow/);
    }
    assert.equal(pending.size, 0);
  });
});