@aria-cli/tools 1.0.9 → 1.0.11
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.
- package/package.json +9 -5
- package/src/__tests__/web-fetch-download.test.ts +0 -433
- package/src/__tests__/web-tools.test.ts +0 -619
- package/src/ask-user-interaction.ts +0 -33
- package/src/cache/web-cache.ts +0 -110
- package/src/definitions/arion.ts +0 -118
- package/src/definitions/browser/browser.ts +0 -502
- package/src/definitions/browser/index.ts +0 -5
- package/src/definitions/browser/pw-downloads.ts +0 -142
- package/src/definitions/browser/pw-interactions.ts +0 -282
- package/src/definitions/browser/pw-responses.ts +0 -98
- package/src/definitions/browser/pw-session.ts +0 -405
- package/src/definitions/browser/pw-shared.ts +0 -85
- package/src/definitions/browser/pw-snapshot.ts +0 -383
- package/src/definitions/browser/pw-state.ts +0 -101
- package/src/definitions/browser/types.ts +0 -203
- package/src/definitions/code-intelligence.ts +0 -526
- package/src/definitions/core.ts +0 -118
- package/src/definitions/delegation.ts +0 -567
- package/src/definitions/deploy.ts +0 -73
- package/src/definitions/filesystem.ts +0 -217
- package/src/definitions/frg.ts +0 -67
- package/src/definitions/index.ts +0 -28
- package/src/definitions/memory.ts +0 -150
- package/src/definitions/messaging.ts +0 -734
- package/src/definitions/meta.ts +0 -392
- package/src/definitions/network.ts +0 -179
- package/src/definitions/outlook.ts +0 -318
- package/src/definitions/patch/apply-patch.ts +0 -235
- package/src/definitions/patch/fuzzy-match.ts +0 -217
- package/src/definitions/patch/index.ts +0 -1
- package/src/definitions/patch/patch-parser.ts +0 -297
- package/src/definitions/patch/sandbox-paths.ts +0 -129
- package/src/definitions/process/index.ts +0 -5
- package/src/definitions/process/process-registry.ts +0 -303
- package/src/definitions/process/process.ts +0 -456
- package/src/definitions/process/pty-keys.ts +0 -298
- package/src/definitions/process/session-slug.ts +0 -147
- package/src/definitions/quip.ts +0 -225
- package/src/definitions/search.ts +0 -67
- package/src/definitions/session-history.ts +0 -79
- package/src/definitions/shell.ts +0 -202
- package/src/definitions/slack.ts +0 -211
- package/src/definitions/web.ts +0 -119
- package/src/executors/apply-patch.ts +0 -1035
- package/src/executors/arion.ts +0 -199
- package/src/executors/code-intelligence.ts +0 -1179
- package/src/executors/deploy.ts +0 -1066
- package/src/executors/filesystem.ts +0 -1428
- package/src/executors/frg-freshness.ts +0 -743
- package/src/executors/frg.ts +0 -394
- package/src/executors/index.ts +0 -280
- package/src/executors/learning-meta.ts +0 -1367
- package/src/executors/lsp-client.ts +0 -355
- package/src/executors/memory.ts +0 -978
- package/src/executors/meta.ts +0 -293
- package/src/executors/process-registry.ts +0 -570
- package/src/executors/pty-session-store.ts +0 -43
- package/src/executors/pty.ts +0 -342
- package/src/executors/restart.ts +0 -133
- package/src/executors/search-freshness.ts +0 -249
- package/src/executors/search-types.ts +0 -98
- package/src/executors/search.ts +0 -89
- package/src/executors/self-diagnose.ts +0 -552
- package/src/executors/session-history.ts +0 -435
- package/src/executors/shell-safety.ts +0 -519
- package/src/executors/shell.ts +0 -1243
- package/src/executors/utils.ts +0 -40
- package/src/executors/web.ts +0 -786
- package/src/extraction/content-extraction.ts +0 -281
- package/src/extraction/index.ts +0 -5
- package/src/headless-control-contract.ts +0 -1149
- package/src/index.ts +0 -788
- package/src/local-control-http-auth.ts +0 -2
- package/src/mcp/client.ts +0 -218
- package/src/mcp/connection.ts +0 -568
- package/src/mcp/index.ts +0 -11
- package/src/mcp/jsonrpc.ts +0 -195
- package/src/mcp/types.ts +0 -199
- package/src/network-control-adapter.ts +0 -88
- package/src/network-runtime/address-types.ts +0 -218
- package/src/network-runtime/db-owner-fencing.ts +0 -91
- package/src/network-runtime/delivery-receipts.ts +0 -372
- package/src/network-runtime/direct-endpoint-authority.ts +0 -35
- package/src/network-runtime/index.ts +0 -316
- package/src/network-runtime/local-control-contract.ts +0 -784
- package/src/network-runtime/node-store-contract.ts +0 -46
- package/src/network-runtime/pair-route-contract.ts +0 -97
- package/src/network-runtime/peer-capabilities.ts +0 -48
- package/src/network-runtime/peer-principal-ref.ts +0 -20
- package/src/network-runtime/peer-state-machine.ts +0 -160
- package/src/network-runtime/protocol-schemas.ts +0 -265
- package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
- package/src/outlook/desktop-session.ts +0 -409
- package/src/policy.ts +0 -171
- package/src/providers/brave.ts +0 -80
- package/src/providers/duckduckgo.ts +0 -199
- package/src/providers/exa.ts +0 -85
- package/src/providers/firecrawl.ts +0 -77
- package/src/providers/index.ts +0 -8
- package/src/providers/jina.ts +0 -70
- package/src/providers/router.ts +0 -121
- package/src/providers/search-provider.ts +0 -74
- package/src/providers/tavily.ts +0 -74
- package/src/quip/desktop-session.ts +0 -435
- package/src/registry/index.ts +0 -1
- package/src/registry/registry.ts +0 -905
- package/src/runtime-socket-local-control-client.ts +0 -632
- package/src/security/dns-normalization.ts +0 -34
- package/src/security/dns-pinning.ts +0 -138
- package/src/security/external-content.ts +0 -129
- package/src/security/ssrf.ts +0 -207
- package/src/slack/desktop-session.ts +0 -493
- package/src/tool-factory.ts +0 -91
- package/src/types.ts +0 -1341
- package/src/utils/retry.ts +0 -163
- package/src/utils/safe-parse-json.ts +0 -176
- package/src/utils/url.ts +0 -20
- package/tests/benchmarks/registry.bench.ts +0 -57
- package/tests/cache/web-cache.test.ts +0 -147
- package/tests/critical-integration.test.ts +0 -1465
- package/tests/definitions/apply-patch.test.ts +0 -586
- package/tests/definitions/browser.test.ts +0 -495
- package/tests/definitions/delegation-pause-resume.test.ts +0 -758
- package/tests/definitions/execution.test.ts +0 -671
- package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
- package/tests/definitions/messaging.test.ts +0 -1468
- package/tests/definitions/outlook.test.ts +0 -30
- package/tests/definitions/process.test.ts +0 -469
- package/tests/definitions/slack.test.ts +0 -28
- package/tests/definitions/tool-inventory.test.ts +0 -218
- package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
- package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
- package/tests/executors/apply-patch.test.ts +0 -538
- package/tests/executors/arion.test.ts +0 -309
- package/tests/executors/conversation-primitives.test.ts +0 -250
- package/tests/executors/deploy.test.ts +0 -746
- package/tests/executors/filesystem-tools.test.ts +0 -357
- package/tests/executors/filesystem.test.ts +0 -959
- package/tests/executors/frg-freshness.test.ts +0 -136
- package/tests/executors/frg-merge.test.ts +0 -70
- package/tests/executors/frg-session-content.test.ts +0 -40
- package/tests/executors/frg.test.ts +0 -56
- package/tests/executors/memory-bugfixes.test.ts +0 -257
- package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
- package/tests/executors/memory.test.ts +0 -853
- package/tests/executors/meta-tools.test.ts +0 -411
- package/tests/executors/meta.test.ts +0 -683
- package/tests/executors/path-containment.test.ts +0 -51
- package/tests/executors/process-registry.test.ts +0 -505
- package/tests/executors/pty.test.ts +0 -664
- package/tests/executors/quest-security.test.ts +0 -249
- package/tests/executors/read-file-media.test.ts +0 -230
- package/tests/executors/recall-knowledge-schema.test.ts +0 -209
- package/tests/executors/recall-tags.test.ts +0 -278
- package/tests/executors/remember-null-safety.contract.test.ts +0 -41
- package/tests/executors/restart.test.ts +0 -67
- package/tests/executors/search-unified.test.ts +0 -381
- package/tests/executors/session-history.test.ts +0 -340
- package/tests/executors/session-transcript.test.ts +0 -561
- package/tests/executors/shell-abort.test.ts +0 -416
- package/tests/executors/shell-env-blocklist.test.ts +0 -648
- package/tests/executors/shell-env-process.test.ts +0 -245
- package/tests/executors/shell-process-registry.test.ts +0 -334
- package/tests/executors/shell-tools.test.ts +0 -393
- package/tests/executors/shell.test.ts +0 -690
- package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
- package/tests/executors/web-integration.test.ts +0 -633
- package/tests/executors/web-symlink.test.ts +0 -18
- package/tests/executors/web.test.ts +0 -1400
- package/tests/executors/write-stdin.test.ts +0 -145
- package/tests/extraction/content-extraction.test.ts +0 -153
- package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
- package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
- package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
- package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
- package/tests/helpers/async-waits.ts +0 -53
- package/tests/integration/headless-control-contract.integration.test.ts +0 -153
- package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
- package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
- package/tests/integration/quest-round-trip.test.ts +0 -303
- package/tests/integration/registry-executor-flow.test.ts +0 -85
- package/tests/integration.test.ts +0 -177
- package/tests/loading-tier.test.ts +0 -126
- package/tests/mcp/client-reconnect.test.ts +0 -267
- package/tests/mcp/connection.test.ts +0 -846
- package/tests/mcp/injectable-logger.test.ts +0 -83
- package/tests/mcp/jsonrpc.test.ts +0 -109
- package/tests/mcp/lifecycle.test.ts +0 -879
- package/tests/network-runtime/address-types.contract.test.ts +0 -143
- package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
- package/tests/network-runtime/local-control-contract.test.ts +0 -869
- package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
- package/tests/network-runtime/node-store-contract.test.ts +0 -11
- package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
- package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
- package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
- package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
- package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
- package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
- package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
- package/tests/parallel.test.ts +0 -71
- package/tests/policy.test.ts +0 -184
- package/tests/print-default-test-lane.ts +0 -14
- package/tests/print-test-lane-manifest.ts +0 -22
- package/tests/providers/brave.test.ts +0 -159
- package/tests/providers/duckduckgo.test.ts +0 -207
- package/tests/providers/exa.test.ts +0 -175
- package/tests/providers/firecrawl.test.ts +0 -168
- package/tests/providers/jina.test.ts +0 -144
- package/tests/providers/router.test.ts +0 -328
- package/tests/providers/tavily.test.ts +0 -165
- package/tests/registry/discovery.test.ts +0 -154
- package/tests/registry/injectable-logger.test.ts +0 -230
- package/tests/registry/input-validation.test.ts +0 -361
- package/tests/registry/interface-completeness.test.ts +0 -85
- package/tests/registry/mcp-integration.test.ts +0 -103
- package/tests/registry/mcp-read-only-hint.test.ts +0 -60
- package/tests/registry/memoria-discovery.test.ts +0 -390
- package/tests/registry/nested-validation.test.ts +0 -283
- package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
- package/tests/registry/registration-lifecycle.test.ts +0 -133
- package/tests/registry-validation.test.ts +0 -424
- package/tests/registry.test.ts +0 -460
- package/tests/security/dns-pinning.test.ts +0 -162
- package/tests/security/external-content.test.ts +0 -144
- package/tests/security/ssrf.test.ts +0 -118
- package/tests/shell-safety-integration.test.ts +0 -32
- package/tests/shell-safety.test.ts +0 -365
- package/tests/slack/desktop-session.test.ts +0 -50
- package/tests/test-lane-manifest.ts +0 -440
- package/tests/test-utils.ts +0 -27
- package/tests/tool-factory.test.ts +0 -188
- package/tests/utils/retry.test.ts +0 -231
- package/tests/utils/url.test.ts +0 -63
- package/tsconfig.cjs.json +0 -24
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -55
- package/vitest.e2e.config.ts +0 -24
- package/vitest.integration.config.ts +0 -24
- package/vitest.native.config.ts +0 -24
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @aria/tools - browser tool unit tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for snapshot parsing, role-ref system, shared utilities,
|
|
5
|
-
* and the browser tool definition. Playwright is mocked — no real browser.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
9
|
-
import {
|
|
10
|
-
buildRoleSnapshotFromAriaSnapshot,
|
|
11
|
-
getRoleSnapshotStats,
|
|
12
|
-
} from "../../src/definitions/browser/pw-snapshot.js";
|
|
13
|
-
import {
|
|
14
|
-
parseRoleRef,
|
|
15
|
-
requireRef,
|
|
16
|
-
normalizeTimeoutMs,
|
|
17
|
-
toAIFriendlyError,
|
|
18
|
-
} from "../../src/definitions/browser/pw-shared.js";
|
|
19
|
-
import {
|
|
20
|
-
closeBrowser,
|
|
21
|
-
getSession,
|
|
22
|
-
launchBrowser,
|
|
23
|
-
resolveChromiumLauncher,
|
|
24
|
-
setPlaywrightLoaderForTest,
|
|
25
|
-
} from "../../src/definitions/browser/pw-session.js";
|
|
26
|
-
|
|
27
|
-
// ── pw-snapshot ──────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
describe("browser", () => {
|
|
30
|
-
describe("pw-snapshot", () => {
|
|
31
|
-
it("builds role snapshot with refs from ariaSnapshot output", () => {
|
|
32
|
-
const ariaTree = [
|
|
33
|
-
"- navigation:",
|
|
34
|
-
' - link "Home"',
|
|
35
|
-
' - link "About"',
|
|
36
|
-
"- main:",
|
|
37
|
-
' - heading "Welcome" [level=1]',
|
|
38
|
-
' - button "Sign Up"',
|
|
39
|
-
' - textbox "Email"',
|
|
40
|
-
].join("\n");
|
|
41
|
-
|
|
42
|
-
const { snapshot, refs } = buildRoleSnapshotFromAriaSnapshot(ariaTree);
|
|
43
|
-
|
|
44
|
-
// Should have refs for interactive elements and named content elements
|
|
45
|
-
expect(Object.keys(refs).length).toBeGreaterThan(0);
|
|
46
|
-
|
|
47
|
-
// Should contain [ref=eN] markers
|
|
48
|
-
expect(snapshot).toContain("[ref=");
|
|
49
|
-
|
|
50
|
-
// Interactive elements should get refs
|
|
51
|
-
const refEntries = Object.values(refs);
|
|
52
|
-
const roles = refEntries.map((r) => r.role);
|
|
53
|
-
expect(roles).toContain("link");
|
|
54
|
-
expect(roles).toContain("button");
|
|
55
|
-
expect(roles).toContain("textbox");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("filters to interactive elements only", () => {
|
|
59
|
-
const ariaTree = [
|
|
60
|
-
"- navigation:",
|
|
61
|
-
' - link "Home"',
|
|
62
|
-
' - link "About"',
|
|
63
|
-
"- main:",
|
|
64
|
-
' - heading "Welcome" [level=1]',
|
|
65
|
-
' - paragraph "Some text"',
|
|
66
|
-
' - button "Click me"',
|
|
67
|
-
].join("\n");
|
|
68
|
-
|
|
69
|
-
const { refs, snapshot } = buildRoleSnapshotFromAriaSnapshot(ariaTree, {
|
|
70
|
-
interactive: true,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Only interactive roles should appear
|
|
74
|
-
const roles = Object.values(refs).map((r) => r.role);
|
|
75
|
-
expect(roles).toContain("link");
|
|
76
|
-
expect(roles).toContain("button");
|
|
77
|
-
// heading is a content role, not interactive
|
|
78
|
-
expect(roles).not.toContain("heading");
|
|
79
|
-
// paragraph is not a known role in any category
|
|
80
|
-
expect(roles).not.toContain("paragraph");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("deduplicates refs with nth disambiguation", () => {
|
|
84
|
-
const ariaTree = ['- button "Submit"', '- button "Submit"', '- button "Cancel"'].join("\n");
|
|
85
|
-
|
|
86
|
-
const { refs } = buildRoleSnapshotFromAriaSnapshot(ariaTree);
|
|
87
|
-
|
|
88
|
-
// There should be 3 refs total
|
|
89
|
-
const entries = Object.entries(refs);
|
|
90
|
-
expect(entries).toHaveLength(3);
|
|
91
|
-
|
|
92
|
-
// The duplicate "Submit" buttons should get nth values
|
|
93
|
-
const submitRefs = entries.filter(([, r]) => r.name === "Submit");
|
|
94
|
-
expect(submitRefs).toHaveLength(2);
|
|
95
|
-
|
|
96
|
-
// nth should be set for duplicates
|
|
97
|
-
const nthValues = submitRefs.map(([, r]) => r.nth);
|
|
98
|
-
expect(nthValues).toContain(0);
|
|
99
|
-
expect(nthValues).toContain(1);
|
|
100
|
-
|
|
101
|
-
// "Cancel" button has no duplicates, nth should be removed
|
|
102
|
-
const cancelRef = entries.find(([, r]) => r.name === "Cancel");
|
|
103
|
-
expect(cancelRef).toBeDefined();
|
|
104
|
-
expect(cancelRef![1].nth).toBeUndefined();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("calculates interactive vs total stats", () => {
|
|
108
|
-
const ariaTree = [
|
|
109
|
-
'- heading "Title"',
|
|
110
|
-
'- button "Click"',
|
|
111
|
-
'- link "Go"',
|
|
112
|
-
'- textbox "Input"',
|
|
113
|
-
].join("\n");
|
|
114
|
-
|
|
115
|
-
const { snapshot, refs } = buildRoleSnapshotFromAriaSnapshot(ariaTree);
|
|
116
|
-
const stats = getRoleSnapshotStats(snapshot, refs);
|
|
117
|
-
|
|
118
|
-
expect(stats.refs).toBe(Object.keys(refs).length);
|
|
119
|
-
// 3 interactive (button, link, textbox), 1 content (heading)
|
|
120
|
-
expect(stats.interactive).toBe(3);
|
|
121
|
-
expect(stats.lines).toBeGreaterThan(0);
|
|
122
|
-
expect(stats.chars).toBeGreaterThan(0);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("returns (empty) for blank input", () => {
|
|
126
|
-
const { snapshot } = buildRoleSnapshotFromAriaSnapshot("");
|
|
127
|
-
expect(snapshot).toBe("(empty)");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("returns (no interactive elements) for interactive mode with no interactive elements", () => {
|
|
131
|
-
const ariaTree = ['- heading "Title"', "- generic:"].join("\n");
|
|
132
|
-
|
|
133
|
-
const { snapshot } = buildRoleSnapshotFromAriaSnapshot(ariaTree, {
|
|
134
|
-
interactive: true,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
expect(snapshot).toBe("(no interactive elements)");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("respects maxDepth option", () => {
|
|
141
|
-
const ariaTree = [
|
|
142
|
-
"- main:",
|
|
143
|
-
" - navigation:",
|
|
144
|
-
' - link "Deep Link"',
|
|
145
|
-
' - button "Top Button"',
|
|
146
|
-
].join("\n");
|
|
147
|
-
|
|
148
|
-
const { refs } = buildRoleSnapshotFromAriaSnapshot(ariaTree, {
|
|
149
|
-
maxDepth: 1,
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Only depth 0 (main) and depth 1 (navigation, button) should be processed
|
|
153
|
-
const roles = Object.values(refs).map((r) => r.role);
|
|
154
|
-
expect(roles).toContain("button");
|
|
155
|
-
// Deep link at depth 2 should be excluded
|
|
156
|
-
expect(roles).not.toContain("link");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("compact mode removes empty structural elements", () => {
|
|
160
|
-
const ariaTree = ["- generic:", " - generic:", ' - button "Click"', "- group:"].join(
|
|
161
|
-
"\n",
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
const { snapshot } = buildRoleSnapshotFromAriaSnapshot(ariaTree, {
|
|
165
|
-
compact: true,
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// The empty "group:" at the end should be stripped (no children with refs)
|
|
169
|
-
// But the generic parent chain leading to button should remain
|
|
170
|
-
expect(snapshot).toContain("button");
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("ref counter generates sequential e1, e2, e3 ids", () => {
|
|
174
|
-
const ariaTree = ['- button "A"', '- button "B"', '- button "C"'].join("\n");
|
|
175
|
-
|
|
176
|
-
const { refs } = buildRoleSnapshotFromAriaSnapshot(ariaTree);
|
|
177
|
-
const keys = Object.keys(refs);
|
|
178
|
-
|
|
179
|
-
expect(keys).toContain("e1");
|
|
180
|
-
expect(keys).toContain("e2");
|
|
181
|
-
expect(keys).toContain("e3");
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// ── pw-shared ──────────────────────────────────────────────────────
|
|
186
|
-
|
|
187
|
-
describe("pw-shared", () => {
|
|
188
|
-
it('parses ref formats: "e1"', () => {
|
|
189
|
-
expect(parseRoleRef("e1")).toBe("e1");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('parses ref formats: "@e1"', () => {
|
|
193
|
-
expect(parseRoleRef("@e1")).toBe("e1");
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('parses ref formats: "ref=e1"', () => {
|
|
197
|
-
expect(parseRoleRef("ref=e1")).toBe("e1");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("parses multi-digit refs", () => {
|
|
201
|
-
expect(parseRoleRef("e123")).toBe("e123");
|
|
202
|
-
expect(parseRoleRef("@e42")).toBe("e42");
|
|
203
|
-
expect(parseRoleRef("ref=e999")).toBe("e999");
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it("handles whitespace around refs", () => {
|
|
207
|
-
expect(parseRoleRef(" e1 ")).toBe("e1");
|
|
208
|
-
expect(parseRoleRef(" @e2 ")).toBe("e2");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("rejects invalid ref formats", () => {
|
|
212
|
-
expect(parseRoleRef("")).toBeNull();
|
|
213
|
-
expect(parseRoleRef(" ")).toBeNull();
|
|
214
|
-
expect(parseRoleRef("abc")).toBeNull();
|
|
215
|
-
expect(parseRoleRef("e")).toBeNull();
|
|
216
|
-
expect(parseRoleRef("1")).toBeNull();
|
|
217
|
-
expect(parseRoleRef("button")).toBeNull();
|
|
218
|
-
expect(parseRoleRef("@abc")).toBeNull();
|
|
219
|
-
expect(parseRoleRef("ref=xyz")).toBeNull();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("requireRef throws on empty/invalid input", () => {
|
|
223
|
-
expect(() => requireRef("")).toThrow("ref is required");
|
|
224
|
-
expect(() => requireRef(undefined)).toThrow("ref is required");
|
|
225
|
-
expect(() => requireRef(null)).toThrow("ref is required");
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("requireRef normalizes valid refs", () => {
|
|
229
|
-
expect(requireRef("e5")).toBe("e5");
|
|
230
|
-
expect(requireRef("@e5")).toBe("e5");
|
|
231
|
-
expect(requireRef("ref=e5")).toBe("e5");
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("requireRef passes through non-standard refs as-is", () => {
|
|
235
|
-
// When the ref doesn't match eN pattern but is non-empty, it's passed through
|
|
236
|
-
const result = requireRef("custom-selector");
|
|
237
|
-
expect(result).toBe("custom-selector");
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("clamps timeout to 500-120000ms", () => {
|
|
241
|
-
expect(normalizeTimeoutMs(100, 5000)).toBe(500);
|
|
242
|
-
expect(normalizeTimeoutMs(200_000, 5000)).toBe(120_000);
|
|
243
|
-
expect(normalizeTimeoutMs(5000, 5000)).toBe(5000);
|
|
244
|
-
expect(normalizeTimeoutMs(undefined, 3000)).toBe(3000);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it("clamps timeout with boundary values", () => {
|
|
248
|
-
expect(normalizeTimeoutMs(500, 5000)).toBe(500);
|
|
249
|
-
expect(normalizeTimeoutMs(120_000, 5000)).toBe(120_000);
|
|
250
|
-
expect(normalizeTimeoutMs(0, 5000)).toBe(500);
|
|
251
|
-
expect(normalizeTimeoutMs(-1, 5000)).toBe(500);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
it("formats AI-friendly error for strict mode violation", () => {
|
|
255
|
-
const error = new Error("strict mode violation: getByRole resolved to 3 elements");
|
|
256
|
-
const friendly = toAIFriendlyError(error, "button[Submit]");
|
|
257
|
-
|
|
258
|
-
expect(friendly.message).toContain("3 elements");
|
|
259
|
-
expect(friendly.message).toContain("snapshot");
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("formats AI-friendly error for timeout/not visible", () => {
|
|
263
|
-
const error = new Error("Timeout 30000ms exceeded waiting for element to be visible");
|
|
264
|
-
const friendly = toAIFriendlyError(error, "e5");
|
|
265
|
-
|
|
266
|
-
expect(friendly.message).toContain("not found or not visible");
|
|
267
|
-
expect(friendly.message).toContain("snapshot");
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("formats AI-friendly error for pointer interception", () => {
|
|
271
|
-
const error = new Error("Element intercepts pointer events");
|
|
272
|
-
const friendly = toAIFriendlyError(error, "e3");
|
|
273
|
-
|
|
274
|
-
expect(friendly.message).toContain("not interactable");
|
|
275
|
-
expect(friendly.message).toContain("scrolling");
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it("passes through unrecognized errors unchanged", () => {
|
|
279
|
-
const error = new Error("some random error");
|
|
280
|
-
const friendly = toAIFriendlyError(error, "e1");
|
|
281
|
-
|
|
282
|
-
expect(friendly.message).toBe("some random error");
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it("handles non-Error objects", () => {
|
|
286
|
-
const friendly = toAIFriendlyError("string error", "e1");
|
|
287
|
-
expect(friendly).toBeInstanceOf(Error);
|
|
288
|
-
expect(friendly.message).toBe("string error");
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
describe("pw-session", () => {
|
|
293
|
-
beforeEach(async () => {
|
|
294
|
-
await closeBrowser();
|
|
295
|
-
setPlaywrightLoaderForTest(null);
|
|
296
|
-
vi.restoreAllMocks();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
afterEach(async () => {
|
|
300
|
-
await closeBrowser();
|
|
301
|
-
setPlaywrightLoaderForTest(null);
|
|
302
|
-
vi.restoreAllMocks();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
function installMockPlaywright(opts?: { failNavigationTo?: string }): {
|
|
306
|
-
goto: ReturnType<typeof vi.fn>;
|
|
307
|
-
} {
|
|
308
|
-
let currentUrl = "about:blank";
|
|
309
|
-
const goto = vi.fn(async (url: string) => {
|
|
310
|
-
if (opts?.failNavigationTo && url === opts.failNavigationTo) {
|
|
311
|
-
throw new Error("page.goto: Timeout 20000ms exceeded.");
|
|
312
|
-
}
|
|
313
|
-
currentUrl = url;
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
const page = {
|
|
317
|
-
goto,
|
|
318
|
-
url: () => currentUrl,
|
|
319
|
-
title: async () => "Mock page",
|
|
320
|
-
close: async () => undefined,
|
|
321
|
-
content: async () => "",
|
|
322
|
-
setViewportSize: async () => undefined,
|
|
323
|
-
screenshot: async () => Buffer.from(""),
|
|
324
|
-
evaluate: async () => undefined,
|
|
325
|
-
locator: () =>
|
|
326
|
-
({
|
|
327
|
-
ariaSnapshot: async () => "",
|
|
328
|
-
}) as unknown,
|
|
329
|
-
getByRole: () => ({}) as unknown,
|
|
330
|
-
keyboard: { press: async () => undefined },
|
|
331
|
-
mouse: { wheel: async () => undefined },
|
|
332
|
-
waitForTimeout: async () => undefined,
|
|
333
|
-
waitForSelector: async () => undefined,
|
|
334
|
-
waitForLoadState: async () => undefined,
|
|
335
|
-
on: () => undefined,
|
|
336
|
-
off: () => undefined,
|
|
337
|
-
isClosed: () => false,
|
|
338
|
-
frameLocator: () => ({}) as unknown,
|
|
339
|
-
} as any;
|
|
340
|
-
|
|
341
|
-
const context = {
|
|
342
|
-
pages: () => [page],
|
|
343
|
-
newPage: async () => page,
|
|
344
|
-
cookies: async () => [],
|
|
345
|
-
addCookies: async () => undefined,
|
|
346
|
-
clearCookies: async () => undefined,
|
|
347
|
-
setOffline: async () => undefined,
|
|
348
|
-
close: async () => undefined,
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
const browser = {
|
|
352
|
-
close: async () => undefined,
|
|
353
|
-
contexts: () => [context],
|
|
354
|
-
isConnected: () => true,
|
|
355
|
-
newContext: async () => context,
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
setPlaywrightLoaderForTest(async () => ({
|
|
359
|
-
chromium: {
|
|
360
|
-
launch: async () => browser,
|
|
361
|
-
},
|
|
362
|
-
}));
|
|
363
|
-
|
|
364
|
-
return { goto };
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
it("accepts playwright module with top-level chromium", () => {
|
|
368
|
-
const launch = async () => ({ close: async () => undefined });
|
|
369
|
-
const chromium = resolveChromiumLauncher({ chromium: { launch } });
|
|
370
|
-
expect(chromium.launch).toBe(launch);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
it("accepts playwright module with default.chromium", () => {
|
|
374
|
-
const launch = async () => ({ close: async () => undefined });
|
|
375
|
-
const chromium = resolveChromiumLauncher({
|
|
376
|
-
default: { chromium: { launch } },
|
|
377
|
-
});
|
|
378
|
-
expect(chromium.launch).toBe(launch);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
it("throws a descriptive error when chromium launcher is missing", () => {
|
|
382
|
-
expect(() => resolveChromiumLauncher({ default: {} })).toThrow(
|
|
383
|
-
"Invalid playwright module shape",
|
|
384
|
-
);
|
|
385
|
-
expect(() => resolveChromiumLauncher({ default: {} })).toThrow("missing chromium.launch");
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("keeps a live session when initial launch navigation times out", async () => {
|
|
389
|
-
installMockPlaywright({ failNavigationTo: "https://news.ycombinator.com/" });
|
|
390
|
-
|
|
391
|
-
await expect(launchBrowser({ url: "https://news.ycombinator.com/" })).rejects.toThrow(
|
|
392
|
-
"Session remains active",
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
const session = getSession();
|
|
396
|
-
expect(session).not.toBeNull();
|
|
397
|
-
expect(session?.page.url()).toBe("about:blank");
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it("allows navigate after launch timeout because session remains active", async () => {
|
|
401
|
-
const { goto } = installMockPlaywright({
|
|
402
|
-
failNavigationTo: "https://news.ycombinator.com/",
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
406
|
-
|
|
407
|
-
const launchResult = await browserTool.execute(
|
|
408
|
-
{ action: "launch", url: "https://news.ycombinator.com/" },
|
|
409
|
-
{} as never,
|
|
410
|
-
);
|
|
411
|
-
expect(launchResult.success).toBe(false);
|
|
412
|
-
expect(launchResult.message).toContain("Session remains active");
|
|
413
|
-
|
|
414
|
-
const navigateResult = await browserTool.execute(
|
|
415
|
-
{ action: "navigate", url: "https://example.com/" },
|
|
416
|
-
{} as never,
|
|
417
|
-
);
|
|
418
|
-
expect(navigateResult.success).toBe(true);
|
|
419
|
-
expect(goto).toHaveBeenCalledWith(
|
|
420
|
-
"https://example.com/",
|
|
421
|
-
expect.objectContaining({ waitUntil: "domcontentloaded" }),
|
|
422
|
-
);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
it("auto-launches when navigate is called without an explicit launch", async () => {
|
|
426
|
-
const { goto } = installMockPlaywright();
|
|
427
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
428
|
-
|
|
429
|
-
const result = await browserTool.execute(
|
|
430
|
-
{ action: "navigate", url: "https://example.com/" },
|
|
431
|
-
{} as never,
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
expect(result.success).toBe(true);
|
|
435
|
-
expect(goto).toHaveBeenCalledWith(
|
|
436
|
-
"https://example.com/",
|
|
437
|
-
expect.objectContaining({ waitUntil: "domcontentloaded" }),
|
|
438
|
-
);
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
it("auto-launches when tab_new is called without an explicit launch", async () => {
|
|
442
|
-
installMockPlaywright();
|
|
443
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
444
|
-
|
|
445
|
-
const result = await browserTool.execute(
|
|
446
|
-
{ action: "tab_new", url: "https://example.com/" },
|
|
447
|
-
{} as never,
|
|
448
|
-
);
|
|
449
|
-
|
|
450
|
-
expect(result.success).toBe(true);
|
|
451
|
-
expect(result.message).toContain("[0]");
|
|
452
|
-
expect(result.message).toContain("https://example.com/");
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
// ── browser tool definition ───────────────────────────────────────
|
|
457
|
-
|
|
458
|
-
describe("browser tool definition", () => {
|
|
459
|
-
it("has correct tool metadata", async () => {
|
|
460
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
461
|
-
|
|
462
|
-
expect(browserTool.name).toBe("browser");
|
|
463
|
-
expect(browserTool.category).toBe("web");
|
|
464
|
-
expect(browserTool.riskLevel).toBe("moderate");
|
|
465
|
-
expect(browserTool.isReadOnly).toBe(false);
|
|
466
|
-
expect(browserTool.loadingTier).toBe("always");
|
|
467
|
-
expect(typeof browserTool.execute).toBe("function");
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it("validates action parameter is required in schema", async () => {
|
|
471
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
472
|
-
|
|
473
|
-
const schema = browserTool.parameters as {
|
|
474
|
-
required?: string[];
|
|
475
|
-
properties?: Record<string, unknown>;
|
|
476
|
-
};
|
|
477
|
-
expect(schema.required).toContain("action");
|
|
478
|
-
expect(schema.properties?.action).toBeDefined();
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it("schema lists all supported actions", async () => {
|
|
482
|
-
const { browserTool } = await import("../../src/definitions/browser/browser.js");
|
|
483
|
-
|
|
484
|
-
const actionProp = (browserTool.parameters as any).properties.action;
|
|
485
|
-
expect(actionProp.enum).toContain("launch");
|
|
486
|
-
expect(actionProp.enum).toContain("close");
|
|
487
|
-
expect(actionProp.enum).toContain("navigate");
|
|
488
|
-
expect(actionProp.enum).toContain("snapshot");
|
|
489
|
-
expect(actionProp.enum).toContain("click");
|
|
490
|
-
expect(actionProp.enum).toContain("type");
|
|
491
|
-
expect(actionProp.enum).toContain("screenshot");
|
|
492
|
-
expect(actionProp.enum).toContain("evaluate");
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
});
|