illuma-agents 1.0.18 → 1.0.19
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/dist/cjs/main.cjs +15 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +473 -0
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -0
- package/dist/esm/main.mjs +1 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +458 -0
- package/dist/esm/tools/BrowserTools.mjs.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/tools/BrowserTools.d.ts +244 -0
- package/package.json +5 -1
- package/src/index.ts +1 -0
- package/src/tools/BrowserTools.test.ts +528 -0
- package/src/tools/BrowserTools.ts +582 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBrowserClickTool,
|
|
3
|
+
createBrowserTypeTool,
|
|
4
|
+
createBrowserNavigateTool,
|
|
5
|
+
createBrowserScrollTool,
|
|
6
|
+
createBrowserExtractTool,
|
|
7
|
+
createBrowserHoverTool,
|
|
8
|
+
createBrowserWaitTool,
|
|
9
|
+
createBrowserGoBackTool,
|
|
10
|
+
createBrowserScreenshotTool,
|
|
11
|
+
createBrowserTools,
|
|
12
|
+
EBrowserTools,
|
|
13
|
+
BROWSER_TOOL_NAMES,
|
|
14
|
+
isBrowserToolCall,
|
|
15
|
+
hasBrowserCapability,
|
|
16
|
+
type BrowserToolsConfig,
|
|
17
|
+
} from './BrowserTools';
|
|
18
|
+
|
|
19
|
+
describe('BrowserTools', () => {
|
|
20
|
+
// ============================================
|
|
21
|
+
// EBrowserTools Enum Tests
|
|
22
|
+
// ============================================
|
|
23
|
+
describe('EBrowserTools enum', () => {
|
|
24
|
+
it('should have all expected tool names', () => {
|
|
25
|
+
expect(EBrowserTools.CLICK).toBe('browser_click');
|
|
26
|
+
expect(EBrowserTools.TYPE).toBe('browser_type');
|
|
27
|
+
expect(EBrowserTools.NAVIGATE).toBe('browser_navigate');
|
|
28
|
+
expect(EBrowserTools.SCROLL).toBe('browser_scroll');
|
|
29
|
+
expect(EBrowserTools.EXTRACT).toBe('browser_extract');
|
|
30
|
+
expect(EBrowserTools.HOVER).toBe('browser_hover');
|
|
31
|
+
expect(EBrowserTools.WAIT).toBe('browser_wait');
|
|
32
|
+
expect(EBrowserTools.BACK).toBe('browser_back');
|
|
33
|
+
expect(EBrowserTools.SCREENSHOT).toBe('browser_screenshot');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should have exactly 9 tool names', () => {
|
|
37
|
+
expect(Object.keys(EBrowserTools)).toHaveLength(9);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// BROWSER_TOOL_NAMES Tests
|
|
43
|
+
// ============================================
|
|
44
|
+
describe('BROWSER_TOOL_NAMES', () => {
|
|
45
|
+
it('should contain all tool names from enum', () => {
|
|
46
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.CLICK);
|
|
47
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.TYPE);
|
|
48
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.NAVIGATE);
|
|
49
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.SCROLL);
|
|
50
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.EXTRACT);
|
|
51
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.HOVER);
|
|
52
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.WAIT);
|
|
53
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.BACK);
|
|
54
|
+
expect(BROWSER_TOOL_NAMES).toContain(EBrowserTools.SCREENSHOT);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should have exactly 9 entries', () => {
|
|
58
|
+
expect(BROWSER_TOOL_NAMES).toHaveLength(9);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ============================================
|
|
63
|
+
// isBrowserToolCall Tests
|
|
64
|
+
// ============================================
|
|
65
|
+
describe('isBrowserToolCall', () => {
|
|
66
|
+
it('should return true for valid browser tool names', () => {
|
|
67
|
+
expect(isBrowserToolCall('browser_click')).toBe(true);
|
|
68
|
+
expect(isBrowserToolCall('browser_type')).toBe(true);
|
|
69
|
+
expect(isBrowserToolCall('browser_navigate')).toBe(true);
|
|
70
|
+
expect(isBrowserToolCall('browser_scroll')).toBe(true);
|
|
71
|
+
expect(isBrowserToolCall('browser_extract')).toBe(true);
|
|
72
|
+
expect(isBrowserToolCall('browser_hover')).toBe(true);
|
|
73
|
+
expect(isBrowserToolCall('browser_wait')).toBe(true);
|
|
74
|
+
expect(isBrowserToolCall('browser_back')).toBe(true);
|
|
75
|
+
expect(isBrowserToolCall('browser_screenshot')).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return false for non-browser tool names', () => {
|
|
79
|
+
expect(isBrowserToolCall('calculator')).toBe(false);
|
|
80
|
+
expect(isBrowserToolCall('web_search')).toBe(false);
|
|
81
|
+
expect(isBrowserToolCall('code_execution')).toBe(false);
|
|
82
|
+
expect(isBrowserToolCall('random_tool')).toBe(false);
|
|
83
|
+
expect(isBrowserToolCall('')).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should return false for similar but incorrect names', () => {
|
|
87
|
+
expect(isBrowserToolCall('browser_Click')).toBe(false); // case sensitive
|
|
88
|
+
expect(isBrowserToolCall('click')).toBe(false);
|
|
89
|
+
expect(isBrowserToolCall('browser-click')).toBe(false); // wrong separator
|
|
90
|
+
expect(isBrowserToolCall('browserclick')).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ============================================
|
|
95
|
+
// hasBrowserCapability Tests
|
|
96
|
+
// ============================================
|
|
97
|
+
describe('hasBrowserCapability', () => {
|
|
98
|
+
it('should return true when x-ranger-browser-extension header is "true"', () => {
|
|
99
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-extension': 'true' })).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return true when x-ranger-browser-capable header is "true"', () => {
|
|
103
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-capable': 'true' })).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should return true when header is array containing "true"', () => {
|
|
107
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-extension': ['true', 'other'] })).toBe(true);
|
|
108
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-capable': ['true'] })).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should return false when headers are missing', () => {
|
|
112
|
+
expect(hasBrowserCapability({})).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should return false when header value is "false"', () => {
|
|
116
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-extension': 'false' })).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should return false when header value is undefined', () => {
|
|
120
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-extension': undefined })).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return false when array does not contain "true"', () => {
|
|
124
|
+
expect(hasBrowserCapability({ 'x-ranger-browser-extension': ['false', 'other'] })).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should handle mixed case and other headers', () => {
|
|
128
|
+
// Only exact match should work
|
|
129
|
+
expect(hasBrowserCapability({
|
|
130
|
+
'x-ranger-browser-extension': 'false',
|
|
131
|
+
'x-ranger-browser-capable': 'true'
|
|
132
|
+
})).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ============================================
|
|
137
|
+
// Individual Tool Creation Tests
|
|
138
|
+
// ============================================
|
|
139
|
+
describe('createBrowserClickTool', () => {
|
|
140
|
+
it('should create a tool with correct name', () => {
|
|
141
|
+
const tool = createBrowserClickTool();
|
|
142
|
+
expect(tool.name).toBe(EBrowserTools.CLICK);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should have a description', () => {
|
|
146
|
+
const tool = createBrowserClickTool();
|
|
147
|
+
expect(tool.description).toBeTruthy();
|
|
148
|
+
expect(tool.description.length).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should return browser_action with index', async () => {
|
|
152
|
+
const tool = createBrowserClickTool();
|
|
153
|
+
const result = await tool.invoke({ index: 5, reason: 'clicking button' });
|
|
154
|
+
const parsed = JSON.parse(result);
|
|
155
|
+
|
|
156
|
+
expect(parsed.type).toBe('browser_action');
|
|
157
|
+
expect(parsed.action.type).toBe('click');
|
|
158
|
+
expect(parsed.action.index).toBe(5);
|
|
159
|
+
expect(parsed.action.reason).toBe('clicking button');
|
|
160
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should return browser_action with coordinates', async () => {
|
|
164
|
+
const tool = createBrowserClickTool();
|
|
165
|
+
const result = await tool.invoke({
|
|
166
|
+
coordinates: { x: 100, y: 200 },
|
|
167
|
+
visualDescription: 'blue button'
|
|
168
|
+
});
|
|
169
|
+
const parsed = JSON.parse(result);
|
|
170
|
+
|
|
171
|
+
expect(parsed.type).toBe('browser_action');
|
|
172
|
+
expect(parsed.action.type).toBe('click');
|
|
173
|
+
expect(parsed.action.coordinates).toEqual({ x: 100, y: 200 });
|
|
174
|
+
expect(parsed.action.visualDescription).toBe('blue button');
|
|
175
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should return error when neither index nor coordinates provided', async () => {
|
|
179
|
+
const tool = createBrowserClickTool();
|
|
180
|
+
const result = await tool.invoke({});
|
|
181
|
+
const parsed = JSON.parse(result);
|
|
182
|
+
|
|
183
|
+
expect(parsed.type).toBe('error');
|
|
184
|
+
expect(parsed.error).toContain('Either index or coordinates must be provided');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('createBrowserTypeTool', () => {
|
|
189
|
+
it('should create a tool with correct name', () => {
|
|
190
|
+
const tool = createBrowserTypeTool();
|
|
191
|
+
expect(tool.name).toBe(EBrowserTools.TYPE);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should return browser_action for typing', async () => {
|
|
195
|
+
const tool = createBrowserTypeTool();
|
|
196
|
+
const result = await tool.invoke({
|
|
197
|
+
index: 2,
|
|
198
|
+
text: 'Hello World',
|
|
199
|
+
clear: true,
|
|
200
|
+
pressEnter: true
|
|
201
|
+
});
|
|
202
|
+
const parsed = JSON.parse(result);
|
|
203
|
+
|
|
204
|
+
expect(parsed.type).toBe('browser_action');
|
|
205
|
+
expect(parsed.action.type).toBe('type');
|
|
206
|
+
expect(parsed.action.index).toBe(2);
|
|
207
|
+
expect(parsed.action.text).toBe('Hello World');
|
|
208
|
+
expect(parsed.action.clear).toBe(true);
|
|
209
|
+
expect(parsed.action.pressEnter).toBe(true);
|
|
210
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should default clear and pressEnter to false', async () => {
|
|
214
|
+
const tool = createBrowserTypeTool();
|
|
215
|
+
const result = await tool.invoke({ index: 1, text: 'test' });
|
|
216
|
+
const parsed = JSON.parse(result);
|
|
217
|
+
|
|
218
|
+
expect(parsed.action.clear).toBe(false);
|
|
219
|
+
expect(parsed.action.pressEnter).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('createBrowserNavigateTool', () => {
|
|
224
|
+
it('should create a tool with correct name', () => {
|
|
225
|
+
const tool = createBrowserNavigateTool();
|
|
226
|
+
expect(tool.name).toBe(EBrowserTools.NAVIGATE);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should return browser_action for navigation', async () => {
|
|
230
|
+
const tool = createBrowserNavigateTool();
|
|
231
|
+
const result = await tool.invoke({
|
|
232
|
+
url: 'https://example.com',
|
|
233
|
+
reason: 'going to example site'
|
|
234
|
+
});
|
|
235
|
+
const parsed = JSON.parse(result);
|
|
236
|
+
|
|
237
|
+
expect(parsed.type).toBe('browser_action');
|
|
238
|
+
expect(parsed.action.type).toBe('navigate');
|
|
239
|
+
expect(parsed.action.url).toBe('https://example.com');
|
|
240
|
+
expect(parsed.action.reason).toBe('going to example site');
|
|
241
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('createBrowserScrollTool', () => {
|
|
246
|
+
it('should create a tool with correct name', () => {
|
|
247
|
+
const tool = createBrowserScrollTool();
|
|
248
|
+
expect(tool.name).toBe(EBrowserTools.SCROLL);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should return browser_action for scrolling', async () => {
|
|
252
|
+
const tool = createBrowserScrollTool();
|
|
253
|
+
const result = await tool.invoke({ direction: 'down', amount: 800 });
|
|
254
|
+
const parsed = JSON.parse(result);
|
|
255
|
+
|
|
256
|
+
expect(parsed.type).toBe('browser_action');
|
|
257
|
+
expect(parsed.action.type).toBe('scroll');
|
|
258
|
+
expect(parsed.action.scroll.direction).toBe('down');
|
|
259
|
+
expect(parsed.action.scroll.amount).toBe(800);
|
|
260
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should default amount to 500', async () => {
|
|
264
|
+
const tool = createBrowserScrollTool();
|
|
265
|
+
const result = await tool.invoke({ direction: 'up' });
|
|
266
|
+
const parsed = JSON.parse(result);
|
|
267
|
+
|
|
268
|
+
expect(parsed.action.scroll.amount).toBe(500);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should support all directions', async () => {
|
|
272
|
+
const tool = createBrowserScrollTool();
|
|
273
|
+
|
|
274
|
+
for (const direction of ['up', 'down', 'left', 'right'] as const) {
|
|
275
|
+
const result = await tool.invoke({ direction });
|
|
276
|
+
const parsed = JSON.parse(result);
|
|
277
|
+
expect(parsed.action.scroll.direction).toBe(direction);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('createBrowserExtractTool', () => {
|
|
283
|
+
it('should create a tool with correct name', () => {
|
|
284
|
+
const tool = createBrowserExtractTool();
|
|
285
|
+
expect(tool.name).toBe(EBrowserTools.EXTRACT);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should return browser_action for extraction with query', async () => {
|
|
289
|
+
const tool = createBrowserExtractTool();
|
|
290
|
+
const result = await tool.invoke({ query: 'price information' });
|
|
291
|
+
const parsed = JSON.parse(result);
|
|
292
|
+
|
|
293
|
+
expect(parsed.type).toBe('browser_action');
|
|
294
|
+
expect(parsed.action.type).toBe('extract');
|
|
295
|
+
expect(parsed.action.query).toBe('price information');
|
|
296
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should return browser_action for extraction with selector', async () => {
|
|
300
|
+
const tool = createBrowserExtractTool();
|
|
301
|
+
const result = await tool.invoke({ selector: '#main-content' });
|
|
302
|
+
const parsed = JSON.parse(result);
|
|
303
|
+
|
|
304
|
+
expect(parsed.action.selector).toBe('#main-content');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should work with empty input', async () => {
|
|
308
|
+
const tool = createBrowserExtractTool();
|
|
309
|
+
const result = await tool.invoke({});
|
|
310
|
+
const parsed = JSON.parse(result);
|
|
311
|
+
|
|
312
|
+
expect(parsed.type).toBe('browser_action');
|
|
313
|
+
expect(parsed.action.type).toBe('extract');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('createBrowserHoverTool', () => {
|
|
318
|
+
it('should create a tool with correct name', () => {
|
|
319
|
+
const tool = createBrowserHoverTool();
|
|
320
|
+
expect(tool.name).toBe(EBrowserTools.HOVER);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should return browser_action for hovering', async () => {
|
|
324
|
+
const tool = createBrowserHoverTool();
|
|
325
|
+
const result = await tool.invoke({ index: 3 });
|
|
326
|
+
const parsed = JSON.parse(result);
|
|
327
|
+
|
|
328
|
+
expect(parsed.type).toBe('browser_action');
|
|
329
|
+
expect(parsed.action.type).toBe('hover');
|
|
330
|
+
expect(parsed.action.index).toBe(3);
|
|
331
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('createBrowserWaitTool', () => {
|
|
336
|
+
it('should create a tool with correct name', () => {
|
|
337
|
+
const tool = createBrowserWaitTool();
|
|
338
|
+
expect(tool.name).toBe(EBrowserTools.WAIT);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should return browser_action for waiting', async () => {
|
|
342
|
+
const tool = createBrowserWaitTool();
|
|
343
|
+
const result = await tool.invoke({
|
|
344
|
+
duration: 2000,
|
|
345
|
+
reason: 'waiting for page load'
|
|
346
|
+
});
|
|
347
|
+
const parsed = JSON.parse(result);
|
|
348
|
+
|
|
349
|
+
expect(parsed.type).toBe('browser_action');
|
|
350
|
+
expect(parsed.action.type).toBe('wait');
|
|
351
|
+
expect(parsed.action.duration).toBe(2000);
|
|
352
|
+
expect(parsed.action.reason).toBe('waiting for page load');
|
|
353
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should default duration to 1000', async () => {
|
|
357
|
+
const tool = createBrowserWaitTool();
|
|
358
|
+
const result = await tool.invoke({});
|
|
359
|
+
const parsed = JSON.parse(result);
|
|
360
|
+
|
|
361
|
+
expect(parsed.action.duration).toBe(1000);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('createBrowserGoBackTool', () => {
|
|
366
|
+
it('should create a tool with correct name', () => {
|
|
367
|
+
const tool = createBrowserGoBackTool();
|
|
368
|
+
expect(tool.name).toBe(EBrowserTools.BACK);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it('should return browser_action for going back', async () => {
|
|
372
|
+
const tool = createBrowserGoBackTool();
|
|
373
|
+
const result = await tool.invoke({ reason: 'returning to previous page' });
|
|
374
|
+
const parsed = JSON.parse(result);
|
|
375
|
+
|
|
376
|
+
expect(parsed.type).toBe('browser_action');
|
|
377
|
+
expect(parsed.action.type).toBe('back');
|
|
378
|
+
expect(parsed.action.reason).toBe('returning to previous page');
|
|
379
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('createBrowserScreenshotTool', () => {
|
|
384
|
+
it('should create a tool with correct name', () => {
|
|
385
|
+
const tool = createBrowserScreenshotTool();
|
|
386
|
+
expect(tool.name).toBe(EBrowserTools.SCREENSHOT);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should return browser_action for screenshot', async () => {
|
|
390
|
+
const tool = createBrowserScreenshotTool();
|
|
391
|
+
const result = await tool.invoke({ fullPage: true });
|
|
392
|
+
const parsed = JSON.parse(result);
|
|
393
|
+
|
|
394
|
+
expect(parsed.type).toBe('browser_action');
|
|
395
|
+
expect(parsed.action.type).toBe('screenshot');
|
|
396
|
+
expect(parsed.action.fullPage).toBe(true);
|
|
397
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should default fullPage to false', async () => {
|
|
401
|
+
const tool = createBrowserScreenshotTool();
|
|
402
|
+
const result = await tool.invoke({});
|
|
403
|
+
const parsed = JSON.parse(result);
|
|
404
|
+
|
|
405
|
+
expect(parsed.action.fullPage).toBe(false);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// ============================================
|
|
410
|
+
// createBrowserTools Tests
|
|
411
|
+
// ============================================
|
|
412
|
+
describe('createBrowserTools', () => {
|
|
413
|
+
it('should create all 9 tools by default', () => {
|
|
414
|
+
const tools = createBrowserTools();
|
|
415
|
+
expect(tools).toHaveLength(9);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should create tools with correct names', () => {
|
|
419
|
+
const tools = createBrowserTools();
|
|
420
|
+
const toolNames = tools.map(t => t.name);
|
|
421
|
+
|
|
422
|
+
expect(toolNames).toContain(EBrowserTools.CLICK);
|
|
423
|
+
expect(toolNames).toContain(EBrowserTools.TYPE);
|
|
424
|
+
expect(toolNames).toContain(EBrowserTools.NAVIGATE);
|
|
425
|
+
expect(toolNames).toContain(EBrowserTools.SCROLL);
|
|
426
|
+
expect(toolNames).toContain(EBrowserTools.EXTRACT);
|
|
427
|
+
expect(toolNames).toContain(EBrowserTools.HOVER);
|
|
428
|
+
expect(toolNames).toContain(EBrowserTools.WAIT);
|
|
429
|
+
expect(toolNames).toContain(EBrowserTools.BACK);
|
|
430
|
+
expect(toolNames).toContain(EBrowserTools.SCREENSHOT);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('should allow disabling specific tools', () => {
|
|
434
|
+
const config: BrowserToolsConfig = {
|
|
435
|
+
enableClick: false,
|
|
436
|
+
enableType: false,
|
|
437
|
+
enableNavigate: true,
|
|
438
|
+
};
|
|
439
|
+
const tools = createBrowserTools(config);
|
|
440
|
+
const toolNames = tools.map(t => t.name);
|
|
441
|
+
|
|
442
|
+
expect(toolNames).not.toContain(EBrowserTools.CLICK);
|
|
443
|
+
expect(toolNames).not.toContain(EBrowserTools.TYPE);
|
|
444
|
+
expect(toolNames).toContain(EBrowserTools.NAVIGATE);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('should create no tools when all disabled', () => {
|
|
448
|
+
const config: BrowserToolsConfig = {
|
|
449
|
+
enableClick: false,
|
|
450
|
+
enableType: false,
|
|
451
|
+
enableNavigate: false,
|
|
452
|
+
enableScroll: false,
|
|
453
|
+
enableExtract: false,
|
|
454
|
+
enableHover: false,
|
|
455
|
+
enableWait: false,
|
|
456
|
+
enableBack: false,
|
|
457
|
+
enableScreenshot: false,
|
|
458
|
+
};
|
|
459
|
+
const tools = createBrowserTools(config);
|
|
460
|
+
expect(tools).toHaveLength(0);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should create only specified tools', () => {
|
|
464
|
+
const config: BrowserToolsConfig = {
|
|
465
|
+
enableClick: true,
|
|
466
|
+
enableType: true,
|
|
467
|
+
enableNavigate: false,
|
|
468
|
+
enableScroll: false,
|
|
469
|
+
enableExtract: false,
|
|
470
|
+
enableHover: false,
|
|
471
|
+
enableWait: false,
|
|
472
|
+
enableBack: false,
|
|
473
|
+
enableScreenshot: false,
|
|
474
|
+
};
|
|
475
|
+
const tools = createBrowserTools(config);
|
|
476
|
+
expect(tools).toHaveLength(2);
|
|
477
|
+
|
|
478
|
+
const toolNames = tools.map(t => t.name);
|
|
479
|
+
expect(toolNames).toContain(EBrowserTools.CLICK);
|
|
480
|
+
expect(toolNames).toContain(EBrowserTools.TYPE);
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// ============================================
|
|
485
|
+
// Integration-like Tests
|
|
486
|
+
// ============================================
|
|
487
|
+
describe('tool response format consistency', () => {
|
|
488
|
+
it('all tools should return JSON with type and requiresBrowserExecution', async () => {
|
|
489
|
+
const tools = createBrowserTools();
|
|
490
|
+
|
|
491
|
+
// Test each tool with minimal valid input
|
|
492
|
+
const testInputs = [
|
|
493
|
+
{ tool: EBrowserTools.CLICK, input: { index: 0 } },
|
|
494
|
+
{ tool: EBrowserTools.TYPE, input: { index: 0, text: 'test' } },
|
|
495
|
+
{ tool: EBrowserTools.NAVIGATE, input: { url: 'https://test.com' } },
|
|
496
|
+
{ tool: EBrowserTools.SCROLL, input: { direction: 'down' } },
|
|
497
|
+
{ tool: EBrowserTools.EXTRACT, input: {} },
|
|
498
|
+
{ tool: EBrowserTools.HOVER, input: { index: 0 } },
|
|
499
|
+
{ tool: EBrowserTools.WAIT, input: {} },
|
|
500
|
+
{ tool: EBrowserTools.BACK, input: {} },
|
|
501
|
+
{ tool: EBrowserTools.SCREENSHOT, input: {} },
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
for (const { tool: toolName, input } of testInputs) {
|
|
505
|
+
const tool = tools.find(t => t.name === toolName);
|
|
506
|
+
expect(tool).toBeDefined();
|
|
507
|
+
|
|
508
|
+
const result = await tool!.invoke(input);
|
|
509
|
+
const parsed = JSON.parse(result);
|
|
510
|
+
|
|
511
|
+
expect(parsed.type).toBe('browser_action');
|
|
512
|
+
expect(parsed.requiresBrowserExecution).toBe(true);
|
|
513
|
+
expect(parsed.action).toBeDefined();
|
|
514
|
+
expect(parsed.action.type).toBeDefined();
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
describe('error handling', () => {
|
|
520
|
+
it('click tool should handle missing required params', async () => {
|
|
521
|
+
const tool = createBrowserClickTool();
|
|
522
|
+
const result = await tool.invoke({});
|
|
523
|
+
const parsed = JSON.parse(result);
|
|
524
|
+
|
|
525
|
+
expect(parsed.type).toBe('error');
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
});
|