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

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

test/domains/ref-stability-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
import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { createFakeClient, axNode } from "./fake-client";
import { interactiveDiff } from "../../src/domains/ref-resolve";
import { ok } from "../../src/util/result";

const SEEDED = [
  { ref: "e1", backendId: 101, sig: "textbox|Email||" },
  { ref: "e2", backendId: 102, sig: "textbox|Name||" },
  { ref: "e3", backendId: 103, sig: "button|Go||" },
];

const treeWithNewButtonFirst = ok({
  nodes: [
    axNode("1", "RootWebArea", "", { childIds: ["9", "2", "3", "4"] }),
    axNode("9", "button", "Dismiss", { parentId: "1", backendDOMNodeId: 999 }),
    axNode("2", "textbox", "Email", { parentId: "1", backendDOMNodeId: 101 }),
    axNode("3", "textbox", "Name", { parentId: "1", backendDOMNodeId: 102 }),
    axNode("4", "button", "Go", { parentId: "1", backendDOMNodeId: 103 }),
  ],
});

const treeUnchanged = ok({
  nodes: [
    axNode("1", "RootWebArea", "", { childIds: ["2", "3", "4"] }),
    axNode("2", "textbox", "Email", { parentId: "1", backendDOMNodeId: 101, value: { value: "a@b.com" } }),
    axNode("3", "textbox", "Name", { parentId: "1", backendDOMNodeId: 102 }),
    axNode("4", "button", "Go", { parentId: "1", backendDOMNodeId: 103 }),
  ],
});

describe("ref stability across a mutation", () => {
  test("an element appearing above the others does not steal their refs", async () => {
    const fake = await createFakeClient({
      refs: SEEDED,
      canned: { "Accessibility.getFullAXTree": treeWithNewButtonFirst },
    });
    await interactiveDiff(fake.client);
    assert.equal(fake.session.resolveRef("e1"), 101);
    assert.equal(fake.session.resolveRef("e2"), 102);
    assert.equal(fake.session.resolveRef("e3"), 103);
  });

  test("a newly appeared element gets a fresh ref rather than an existing one", async () => {
    const fake = await createFakeClient({
      refs: SEEDED,
      canned: { "Accessibility.getFullAXTree": treeWithNewButtonFirst },
    });
    const diff = await interactiveDiff(fake.client);
    assert.equal(fake.session.resolveRef("e4"), 999);
    assert.match(diff, /\*\[e4\] button "Dismiss"/);
  });

  test("refs are unchanged when the interactive set is unchanged", async () => {
    const fake = await createFakeClient({
      refs: SEEDED,
      canned: { "Accessibility.getFullAXTree": treeUnchanged },
    });
    const diff = await interactiveDiff(fake.client);
    assert.equal(fake.session.resolveRef("e1"), 101);
    assert.equal(fake.session.resolveRef("e2"), 102);
    assert.equal(fake.session.resolveRef("e3"), 103);
    assert.match(diff, /\[e1\] textbox "Email": value "" → "a@b.com"/);
  });

  test("every published ref maps to a distinct element", async () => {
    const fake = await createFakeClient({
      refs: SEEDED,
      canned: { "Accessibility.getFullAXTree": treeWithNewButtonFirst },
    });
    await interactiveDiff(fake.client);
    const seen = new Set<number>();
    for (const ref of ["e1", "e2", "e3", "e4"]) {
      const backendId = fake.session.resolveRef(ref);
      assert.ok(backendId !== undefined, `${ref} resolved to nothing`);
      assert.equal(seen.has(backendId), false, `${ref} duplicates another ref's element`);
      seen.add(backendId);
    }
  });
});