@melra/browser-runtime 0.3.0-alpha.10 → 0.3.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +2 -1
  2. package/dist/bounded.d.ts +27 -0
  3. package/dist/bounded.d.ts.map +1 -0
  4. package/dist/bounded.js +50 -0
  5. package/dist/bounded.js.map +1 -0
  6. package/dist/browser-connection.d.ts.map +1 -1
  7. package/dist/browser-connection.js +22 -7
  8. package/dist/browser-connection.js.map +1 -1
  9. package/dist/index.d.ts +73 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +307 -52
  12. package/dist/index.js.map +1 -1
  13. package/dist/pinning-proxy.d.ts +1 -1
  14. package/dist/pinning-proxy.d.ts.map +1 -1
  15. package/dist/pinning-proxy.js +1 -1
  16. package/dist/pinning-proxy.js.map +1 -1
  17. package/dist/stable-dom.d.ts +13 -1
  18. package/dist/stable-dom.d.ts.map +1 -1
  19. package/dist/stable-dom.js +22 -3
  20. package/dist/stable-dom.js.map +1 -1
  21. package/package.json +6 -5
  22. package/dist/browser-connection.test.d.ts +0 -2
  23. package/dist/browser-connection.test.d.ts.map +0 -1
  24. package/dist/browser-connection.test.js +0 -210
  25. package/dist/browser-connection.test.js.map +0 -1
  26. package/dist/dialogs.test.d.ts +0 -2
  27. package/dist/dialogs.test.d.ts.map +0 -1
  28. package/dist/dialogs.test.js +0 -165
  29. package/dist/dialogs.test.js.map +0 -1
  30. package/dist/frames.test.d.ts +0 -2
  31. package/dist/frames.test.d.ts.map +0 -1
  32. package/dist/frames.test.js +0 -157
  33. package/dist/frames.test.js.map +0 -1
  34. package/dist/history-tabs.test.d.ts +0 -2
  35. package/dist/history-tabs.test.d.ts.map +0 -1
  36. package/dist/history-tabs.test.js +0 -171
  37. package/dist/history-tabs.test.js.map +0 -1
  38. package/dist/input-fidelity.test.d.ts +0 -2
  39. package/dist/input-fidelity.test.d.ts.map +0 -1
  40. package/dist/input-fidelity.test.js +0 -260
  41. package/dist/input-fidelity.test.js.map +0 -1
  42. package/dist/network-policy.d.ts +0 -28
  43. package/dist/network-policy.d.ts.map +0 -1
  44. package/dist/network-policy.js +0 -121
  45. package/dist/network-policy.js.map +0 -1
  46. package/dist/network-policy.test.d.ts +0 -2
  47. package/dist/network-policy.test.d.ts.map +0 -1
  48. package/dist/network-policy.test.js +0 -39
  49. package/dist/network-policy.test.js.map +0 -1
  50. package/dist/pinning-proxy.test.d.ts +0 -2
  51. package/dist/pinning-proxy.test.d.ts.map +0 -1
  52. package/dist/pinning-proxy.test.js +0 -80
  53. package/dist/pinning-proxy.test.js.map +0 -1
  54. package/dist/popups-profile.test.d.ts +0 -2
  55. package/dist/popups-profile.test.d.ts.map +0 -1
  56. package/dist/popups-profile.test.js +0 -170
  57. package/dist/popups-profile.test.js.map +0 -1
  58. package/dist/see-act.test.d.ts +0 -2
  59. package/dist/see-act.test.d.ts.map +0 -1
  60. package/dist/see-act.test.js +0 -152
  61. package/dist/see-act.test.js.map +0 -1
  62. package/dist/selector.test.d.ts +0 -2
  63. package/dist/selector.test.d.ts.map +0 -1
  64. package/dist/selector.test.js +0 -34
  65. package/dist/selector.test.js.map +0 -1
  66. package/dist/stable-dom.test.d.ts +0 -2
  67. package/dist/stable-dom.test.d.ts.map +0 -1
  68. package/dist/stable-dom.test.js +0 -22
  69. package/dist/stable-dom.test.js.map +0 -1
@@ -1,170 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * Two gaps, both invisible to the caller. A click that spawned a window
12
- * returned plain success while the window sat there holding focus and a
13
- * session, and every run started from an empty profile, so a site that had
14
- * been logged into had to be logged into again on the next task.
15
- */
16
- const roots = [];
17
- const servers = [];
18
- afterEach(async () => {
19
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
20
- server.close(() => done());
21
- })));
22
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
23
- });
24
- /**
25
- * `/` opens a window on click; `/set` plants a cookie; `/read` reports whether
26
- * the cookie survived. One server covers both halves of this file.
27
- */
28
- async function serve() {
29
- const server = createServer((request, response) => {
30
- const path = (request.url ?? "/").split("?")[0];
31
- if (path === "/set") {
32
- response.writeHead(200, {
33
- "content-type": "text/html; charset=utf-8",
34
- "set-cookie": "melra=kept; Path=/; Max-Age=3600",
35
- });
36
- response.end("<!doctype html><title>set</title><body>set</body>");
37
- return;
38
- }
39
- if (path === "/read") {
40
- const seen = (request.headers.cookie ?? "").includes("melra=kept");
41
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
42
- response.end(`<!doctype html><title>read</title><body><div id="cookie">${seen ? "kept" : "gone"}</div></body>`);
43
- return;
44
- }
45
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
46
- response.end(`<!doctype html><title>opener</title><body>
47
- <button id="pop">Open</button>
48
- <script>
49
- document.getElementById('pop').addEventListener('click', () => {
50
- window.open('/read', '_blank');
51
- });
52
- </script>
53
- </body>`);
54
- });
55
- servers.push(server);
56
- await new Promise((done) => {
57
- server.listen(0, "127.0.0.1", done);
58
- });
59
- const address = server.address();
60
- if (address === null || typeof address === "string") {
61
- throw new Error("fixture_server_address_unavailable");
62
- }
63
- return `http://127.0.0.1:${address.port}`;
64
- }
65
- async function root(name) {
66
- const created = await mkdtemp(join(tmpdir(), `melra-${name}-`));
67
- roots.push(created);
68
- return created;
69
- }
70
- const runtime = (workspaceRoot, executablePath, extra = {}) => new BrowserRuntime({
71
- workspaceRoot,
72
- artifactDirectory: join(workspaceRoot, "artifacts"),
73
- executablePath,
74
- allowedDomains: ["127.0.0.1"],
75
- allowLocalhost: true,
76
- ...extra,
77
- });
78
- const op = (fields) => BrowserOperationSchema.parse({ kind: "browser", ...fields });
79
- describe("browser popups", () => {
80
- it("reports the window a page opened and closes it by default", async () => {
81
- const executablePath = await detectBrowserExecutable();
82
- if (executablePath === undefined)
83
- return;
84
- const url = await serve();
85
- const session = runtime(await root("popup-block"), executablePath);
86
- try {
87
- await session.execute(op({ action: "navigate", url }));
88
- const clicked = await session.execute(op({ action: "click", target: { selector: "#pop" } }));
89
- // The point: the caller is told what the click opened behind it.
90
- expect(clicked.popups).toEqual([
91
- expect.objectContaining({ url: `${url}/read`, blocked: true }),
92
- ]);
93
- // And it is gone by the time the next action runs, not eventually.
94
- const tabs = await session.execute(op({ action: "tabs" }));
95
- expect(tabs.tabs.length).toBe(1);
96
- }
97
- finally {
98
- await session.close();
99
- }
100
- }, 120_000);
101
- it("keeps the window as an addressable tab when popups are allowed", async () => {
102
- const executablePath = await detectBrowserExecutable();
103
- if (executablePath === undefined)
104
- return;
105
- const url = await serve();
106
- const session = runtime(await root("popup-allow"), executablePath, {
107
- popups: "allow",
108
- });
109
- try {
110
- await session.execute(op({ action: "navigate", url }));
111
- const clicked = await session.execute(op({ action: "click", target: { selector: "#pop" } }));
112
- expect(clicked.popups[0]?.blocked).toBe(false);
113
- const tabs = await session.execute(op({ action: "tabs" }));
114
- expect(tabs.tabs.length).toBe(2);
115
- }
116
- finally {
117
- await session.close();
118
- }
119
- }, 120_000);
120
- it("does not mistake a tab the caller asked for for a popup", async () => {
121
- const executablePath = await detectBrowserExecutable();
122
- if (executablePath === undefined)
123
- return;
124
- const url = await serve();
125
- const session = runtime(await root("popup-tab-new"), executablePath);
126
- try {
127
- await session.execute(op({ action: "navigate", url }));
128
- // Both arrive as the same `page` event. If this were treated as a popup,
129
- // `tab_new` would close the tab it just opened.
130
- const opened = await session.execute(op({ action: "tab_new", url: `${url}/read` }));
131
- expect(opened.popups).toBeUndefined();
132
- expect((await session.execute(op({ action: "tabs" }))).tabs).toHaveLength(2);
133
- }
134
- finally {
135
- await session.close();
136
- }
137
- }, 120_000);
138
- });
139
- describe("persistent browser profile", () => {
140
- it("keeps a cookie across two sessions, and drops it without a profile", async () => {
141
- const executablePath = await detectBrowserExecutable();
142
- if (executablePath === undefined)
143
- return;
144
- const url = await serve();
145
- const workspace = await root("profile");
146
- const userDataDir = join(workspace, "profile");
147
- const read = async (extra) => {
148
- const session = runtime(workspace, executablePath, extra);
149
- try {
150
- await session.execute(op({ action: "navigate", url: `${url}/read` }));
151
- const seen = await session.execute(op({ action: "inspect", target: { selector: "#cookie" } }));
152
- return String(seen.text ?? "").trim();
153
- }
154
- finally {
155
- await session.close();
156
- }
157
- };
158
- const first = runtime(workspace, executablePath, { userDataDir });
159
- try {
160
- await first.execute(op({ action: "navigate", url: `${url}/set` }));
161
- }
162
- finally {
163
- await first.close();
164
- }
165
- expect(await read({ userDataDir })).toBe("kept");
166
- // The default is still a throwaway profile, so nothing leaked into it.
167
- expect(await read({})).toBe("gone");
168
- }, 180_000);
169
- });
170
- //# sourceMappingURL=popups-profile.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"popups-profile.test.js","sourceRoot":"","sources":["../src/popups-profile.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=see-act.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"see-act.test.d.ts","sourceRoot":"","sources":["../src/see-act.test.ts"],"names":[],"mappings":""}
@@ -1,152 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * The see/act loop is the thing that was broken: a caller could read a snapshot
12
- * but had no way to turn what it saw into a target it could act on. These tests
13
- * drive a real page, so they fail if the emitted selector does not address the
14
- * element it was reported for.
15
- */
16
- const PAGE = `<!doctype html><html><head><title>See act</title></head><body>
17
- <form id="login">
18
- <input name="email" placeholder="Email">
19
- <input name="password" type="password">
20
- <button type="submit" data-testid="submit"> Sign in </button>
21
- </form>
22
- <div><span>Total</span><section id="cart"><p>2 items</p></section></div>
23
- <button disabled>Locked</button>
24
- </body></html>`;
25
- const roots = [];
26
- const servers = [];
27
- afterEach(async () => {
28
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
29
- server.close(() => done());
30
- })));
31
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
32
- });
33
- async function fixture() {
34
- const executablePath = await detectBrowserExecutable();
35
- if (executablePath === undefined)
36
- return undefined;
37
- const server = createServer((_request, response) => {
38
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
39
- response.end(PAGE);
40
- });
41
- servers.push(server);
42
- await new Promise((done) => {
43
- server.listen(0, "127.0.0.1", done);
44
- });
45
- const address = server.address();
46
- if (address === null || typeof address === "string") {
47
- throw new Error("fixture_server_address_unavailable");
48
- }
49
- const root = await mkdtemp(join(tmpdir(), "melra-see-act-"));
50
- roots.push(root);
51
- return {
52
- runtime: new BrowserRuntime({
53
- workspaceRoot: root,
54
- artifactDirectory: join(root, "artifacts"),
55
- executablePath,
56
- allowedDomains: ["127.0.0.1"],
57
- allowLocalhost: true,
58
- }),
59
- url: `http://127.0.0.1:${address.port}`,
60
- };
61
- }
62
- describe("browser see/act loop", () => {
63
- it("emits a selector that addresses the element it was reported for", async () => {
64
- const context = await fixture();
65
- if (context === undefined)
66
- return;
67
- const { runtime, url } = context;
68
- try {
69
- const observed = await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
70
- const elements = observed.elements;
71
- const submit = elements.find((item) => item.testId === "submit");
72
- expect(submit?.selector).toBe('[data-testid="submit"]');
73
- const email = elements.find((item) => item.attributeName === "email");
74
- // Anchored on the form's id rather than a path from <html>.
75
- expect(email?.selector).toBe("#login > input:nth-child(1)");
76
- expect(email?.placeholder).toBe("Email");
77
- expect(elements.find((item) => item.name === "Locked")?.disabled).toBe(true);
78
- expect(elements.every((item) => typeof item.index === "number")).toBe(true);
79
- // The loop closes only if the selector can be acted on.
80
- const typed = await runtime.execute(BrowserOperationSchema.parse({
81
- kind: "browser",
82
- action: "type",
83
- target: { selector: email.selector },
84
- value: "user@example.com",
85
- }));
86
- expect(typed.typed).toBe(true);
87
- const after = (await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "inspect" }))).elements.find((item) => item.attributeName === "email");
88
- expect(after?.value).toBe("user@example.com");
89
- }
90
- finally {
91
- await runtime.close();
92
- }
93
- }, 120_000);
94
- it("matches text through surrounding whitespace instead of failing exact", async () => {
95
- const context = await fixture();
96
- if (context === undefined)
97
- return;
98
- const { runtime, url } = context;
99
- try {
100
- await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
101
- // `<button> Sign in </button>` never matched `getByText("Sign in", {exact:true})`.
102
- const clicked = await runtime.execute(BrowserOperationSchema.parse({
103
- kind: "browser",
104
- action: "click",
105
- target: { text: "Sign in" },
106
- }));
107
- expect(clicked.clicked).toBe(true);
108
- }
109
- finally {
110
- await runtime.close();
111
- }
112
- }, 120_000);
113
- it("reports a target that matches nothing instead of timing out opaquely", async () => {
114
- const context = await fixture();
115
- if (context === undefined)
116
- return;
117
- const { runtime, url } = context;
118
- try {
119
- await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
120
- await expect(runtime.execute(BrowserOperationSchema.parse({
121
- kind: "browser",
122
- action: "click",
123
- target: { selector: "#nothing-here" },
124
- timeoutMs: 120_000,
125
- }))).rejects.toThrow("browser_target_not_found");
126
- }
127
- finally {
128
- await runtime.close();
129
- }
130
- }, 120_000);
131
- it("scopes extraction to a target rather than returning the whole page", async () => {
132
- const context = await fixture();
133
- if (context === undefined)
134
- return;
135
- const { runtime, url } = context;
136
- try {
137
- await runtime.execute(BrowserOperationSchema.parse({ kind: "browser", action: "navigate", url }));
138
- const extracted = await runtime.execute(BrowserOperationSchema.parse({
139
- kind: "browser",
140
- action: "inspect",
141
- target: { selector: "#cart" },
142
- }));
143
- expect(extracted.text).toBe("2 items");
144
- expect(extracted.html).toBe("<p>2 items</p>");
145
- expect(extracted.text).not.toContain("Total");
146
- }
147
- finally {
148
- await runtime.close();
149
- }
150
- }, 120_000);
151
- });
152
- //# sourceMappingURL=see-act.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"see-act.test.js","sourceRoot":"","sources":["../src/see-act.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=selector.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selector.test.d.ts","sourceRoot":"","sources":["../src/selector.test.ts"],"names":[],"mappings":""}
@@ -1,34 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { describe, expect, it } from "vitest";
4
- import { buildSelector } from "./selector.js";
5
- describe("buildSelector", () => {
6
- it("anchors on an id when the page provides one", () => {
7
- expect(buildSelector([
8
- { tag: "form", nth: 2, id: "login" },
9
- { tag: "input", nth: 3 },
10
- ])).toBe("#login > input:nth-child(3)");
11
- });
12
- it("prefers a test id over positional descent", () => {
13
- expect(buildSelector([
14
- { tag: "div", nth: 1, testId: "cart" },
15
- { tag: "button", nth: 2 },
16
- ])).toBe('[data-testid="cart"] > button:nth-child(2)');
17
- });
18
- it("falls back to a positional path when nothing is labelled", () => {
19
- expect(buildSelector([
20
- { tag: "html", nth: 1 },
21
- { tag: "body", nth: 2 },
22
- { tag: "a", nth: 5 },
23
- ])).toBe("html:nth-child(1) > body:nth-child(2) > a:nth-child(5)");
24
- });
25
- it("quotes an id that is not a bare CSS identifier", () => {
26
- // Frameworks emit ids like `:r1:` and `user.email`, which `#id` cannot spell.
27
- expect(buildSelector([{ tag: "input", nth: 1, id: ":r1:" }])).toBe('[id=":r1:"]');
28
- expect(buildSelector([{ tag: "input", nth: 1, id: 'a"b' }])).toBe('[id="a\\"b"]');
29
- });
30
- it("refuses an empty chain rather than emitting a selector matching everything", () => {
31
- expect(() => buildSelector([])).toThrow("browser_selector_requires_chain");
32
- });
33
- });
34
- //# sourceMappingURL=selector.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selector.test.js","sourceRoot":"","sources":["../src/selector.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CACJ,aAAa,CAAC;YACZ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE;YACpC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;SACzB,CAAC,CACH,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CACJ,aAAa,CAAC;YACZ,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE;YACtC,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE;SAC1B,CAAC,CACH,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CACJ,aAAa,CAAC;YACZ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;YACvB,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE;YACvB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE;SACrB,CAAC,CACH,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,8EAA8E;QAC9E,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAChE,aAAa,CACd,CAAC;QACF,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAC/D,cAAc,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=stable-dom.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stable-dom.test.d.ts","sourceRoot":"","sources":["../src/stable-dom.test.ts"],"names":[],"mappings":""}
@@ -1,22 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { describe, expect, it } from "vitest";
4
- import { normalizeDomStabilityOptions } from "./stable-dom.js";
5
- describe("DOM stability options", () => {
6
- it("applies bounded defaults", () => {
7
- expect(normalizeDomStabilityOptions()).toEqual({
8
- quietWindowMs: 180,
9
- timeoutMs: 1_500,
10
- });
11
- });
12
- it("keeps the timeout at or above the quiet window", () => {
13
- expect(normalizeDomStabilityOptions({
14
- quietWindowMs: 900,
15
- timeoutMs: 100,
16
- })).toEqual({
17
- quietWindowMs: 900,
18
- timeoutMs: 900,
19
- });
20
- });
21
- });
22
- //# sourceMappingURL=stable-dom.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stable-dom.test.js","sourceRoot":"","sources":["../src/stable-dom.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAE/D,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,4BAA4B,EAAE,CAAC,CAAC,OAAO,CAAC;YAC7C,aAAa,EAAE,GAAG;YAClB,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CACJ,4BAA4B,CAAC;YAC3B,aAAa,EAAE,GAAG;YAClB,SAAS,EAAE,GAAG;SACf,CAAC,CACH,CAAC,OAAO,CAAC;YACR,aAAa,EAAE,GAAG;YAClB,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}