aboutsummaryrefslogtreecommitdiff
path: root/test/cdp/commands-test.ts
blob: 738189d14bfdea21d187260f7b63b039f22c83e1 (plain)
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
import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { COMMANDS, decodeResult } from "../../src/cdp/commands";

describe("cdp command table", () => {
  test("decodes a well-formed response", () => {
    const r = decodeResult("Target.attachToTarget", { sessionId: "s1" });
    assert.equal(r.success, true);
    if (r.success) assert.equal(r.data.sessionId, "s1");
  });

  test("rejects a response with a wrong field type", () => {
    const r = decodeResult("Browser.getWindowForTarget", { windowId: "not-a-number" });
    assert.equal(r.success, false);
    if (!r.success) {
      assert.equal(r.error.kind, "invalid_response");
      assert.equal(r.error.method, "Browser.getWindowForTarget");
      assert.match(r.error.message, /must be number/);
    }
  });

  test("rejects a response missing a required field", () => {
    const r = decodeResult("Target.attachToTarget", {});
    assert.equal(r.success, false);
  });

  test("rejects a non-object response", () => {
    const r = decodeResult("Target.attachToTarget", null);
    assert.equal(r.success, false);
  });

  test("every table entry has both params and result schemas", () => {
    for (const [method, spec] of Object.entries(COMMANDS)) {
      assert.ok(spec.params, `${method} missing params schema`);
      assert.ok(spec.result, `${method} missing result schema`);
      assert.ok(spec.validate, `${method} missing compiled validator`);
    }
  });

  test("an unknown method decodes to an error rather than throwing", () => {
    // Types prevent this, but a daemon could echo an unexpected method name.
    const r = decodeResult("Nope.method" as "Page.enable", {});
    assert.equal(r.success, false);
  });

  test("accepts an empty Network.setExtraHTTPHeaders response", () => {
    const r = decodeResult("Network.setExtraHTTPHeaders", {});
    assert.equal(r.success, true);
  });
});

describe("Accessibility.getFullAXTree schema", () => {
  test("accepts a realistic Chrome AX node with unmodelled extra fields", () => {
    const r = decodeResult("Accessibility.getFullAXTree", {
      nodes: [
        {
          nodeId: "1",
          ignored: false,
          role: { type: "role", value: "RootWebArea" },
          chromeRole: { type: "internalRole", value: 144 },
          name: { type: "computedString", value: "Example", sources: [{ type: "relatedElement" }] },
          properties: [
            { name: "focusable", value: { type: "booleanOrUndefined", value: true } },
            { name: "level", value: { type: "integer", value: 2 } },
          ],
          childIds: ["2", "3"],
          backendDOMNodeId: 7,
          frameId: "F1",
        },
        { nodeId: "2", parentId: "1", ignored: true, ignoredReasons: [{ name: "notRendered" }] },
      ],
    });
    assert.equal(r.success, true);
    if (r.success) {
      assert.equal(r.data.nodes.length, 2);
      assert.equal(r.data.nodes[0]?.role?.value, "RootWebArea");
      assert.equal(r.data.nodes[0]?.backendDOMNodeId, 7);
      assert.deepEqual(r.data.nodes[0]?.childIds, ["2", "3"]);
    }
  });

  test("accepts an empty node list", () => {
    const r = decodeResult("Accessibility.getFullAXTree", { nodes: [] });
    assert.equal(r.success, true);
  });

  test("rejects a node whose nodeId is not a string", () => {
    const r = decodeResult("Accessibility.getFullAXTree", { nodes: [{ nodeId: 5 }] });
    assert.equal(r.success, false);
    if (!r.success) assert.equal(r.error.kind, "invalid_response");
  });

  test("rejects childIds that are not strings", () => {
    const r = decodeResult("Accessibility.getFullAXTree", { nodes: [{ nodeId: "1", childIds: [2] }] });
    assert.equal(r.success, false);
  });

  test("rejects a missing nodes array", () => {
    const r = decodeResult("Accessibility.getFullAXTree", {});
    assert.equal(r.success, false);
  });

  test("keeps an AxValue's value untyped so any Chrome value type decodes", () => {
    const r = decodeResult("Accessibility.getFullAXTree", {
      nodes: [{ nodeId: "1", value: { type: "number", value: 42 } }, { nodeId: "2", name: { type: "x", value: null } }],
    });
    assert.equal(r.success, true);
  });
});