btcp-browser-agent 0.1.0 → 0.1.1

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 (136) hide show
  1. package/package.json +8 -9
  2. package/packages/core/dist/actions.d.ts +97 -0
  3. package/packages/core/dist/actions.js +940 -0
  4. package/packages/core/dist/errors.d.ts +138 -0
  5. package/packages/core/dist/errors.js +157 -0
  6. package/packages/core/dist/index.d.ts +120 -0
  7. package/packages/core/dist/index.js +134 -0
  8. package/packages/core/dist/ref-map.d.ts +16 -0
  9. package/packages/core/dist/ref-map.js +91 -0
  10. package/packages/core/dist/snapshot.d.ts +37 -0
  11. package/packages/core/dist/snapshot.js +751 -0
  12. package/packages/core/dist/types.d.ts +396 -0
  13. package/packages/core/dist/types.js +7 -0
  14. package/packages/extension/dist/background.d.ts +227 -0
  15. package/packages/extension/dist/background.js +737 -0
  16. package/packages/extension/dist/content.d.ts +18 -0
  17. package/packages/extension/dist/content.js +149 -0
  18. package/packages/extension/dist/index.d.ts +228 -0
  19. package/packages/extension/dist/index.js +350 -0
  20. package/packages/extension/dist/session-manager.d.ts +87 -0
  21. package/packages/extension/dist/session-manager.js +322 -0
  22. package/packages/extension/{src/session-types.ts → dist/session-types.d.ts} +113 -144
  23. package/packages/extension/dist/session-types.js +5 -0
  24. package/packages/extension/dist/types.d.ts +88 -0
  25. package/packages/extension/dist/types.js +7 -0
  26. package/CLAUDE.md +0 -230
  27. package/SKILL.md +0 -143
  28. package/SNAPSHOT_IMPROVEMENTS.md +0 -302
  29. package/USAGE.md +0 -146
  30. package/dist/index.d.ts.map +0 -1
  31. package/dist/index.js.map +0 -1
  32. package/docs/browser-cli-design.md +0 -500
  33. package/examples/chrome-extension/CHANGELOG.md +0 -210
  34. package/examples/chrome-extension/DEBUG.md +0 -231
  35. package/examples/chrome-extension/ERROR_FIXED.md +0 -147
  36. package/examples/chrome-extension/QUICK_TEST.md +0 -189
  37. package/examples/chrome-extension/README.md +0 -149
  38. package/examples/chrome-extension/SESSION_ONLY_MODE.md +0 -305
  39. package/examples/chrome-extension/TEST_WITH_YOUR_TABS.md +0 -97
  40. package/examples/chrome-extension/build.js +0 -43
  41. package/examples/chrome-extension/manifest.json +0 -37
  42. package/examples/chrome-extension/package-lock.json +0 -1063
  43. package/examples/chrome-extension/package.json +0 -21
  44. package/examples/chrome-extension/popup.html +0 -195
  45. package/examples/chrome-extension/src/background.ts +0 -12
  46. package/examples/chrome-extension/src/content.ts +0 -7
  47. package/examples/chrome-extension/src/popup.ts +0 -303
  48. package/examples/chrome-extension/src/scenario-google-github.ts +0 -389
  49. package/examples/chrome-extension/test-page.html +0 -127
  50. package/examples/chrome-extension/tests/README.md +0 -206
  51. package/examples/chrome-extension/tests/scenario-google-to-github-star.ts +0 -380
  52. package/examples/chrome-extension/tsconfig.json +0 -14
  53. package/examples/snapshots/README.md +0 -207
  54. package/examples/snapshots/amazon-com-detail.html +0 -9528
  55. package/examples/snapshots/amazon-com-detail.snapshot.txt +0 -997
  56. package/examples/snapshots/convert-snapshots.ts +0 -97
  57. package/examples/snapshots/edition-cnn-com.html +0 -13292
  58. package/examples/snapshots/edition-cnn-com.snapshot.txt +0 -562
  59. package/examples/snapshots/github-com-microsoft-vscode.html +0 -2916
  60. package/examples/snapshots/github-com-microsoft-vscode.snapshot.txt +0 -455
  61. package/examples/snapshots/google-search.html +0 -20012
  62. package/examples/snapshots/google-search.snapshot.txt +0 -195
  63. package/examples/snapshots/metadata.json +0 -86
  64. package/examples/snapshots/npr-org-templates.html +0 -2031
  65. package/examples/snapshots/npr-org-templates.snapshot.txt +0 -224
  66. package/examples/snapshots/stackoverflow-com.html +0 -5216
  67. package/examples/snapshots/stackoverflow-com.snapshot.txt +0 -2404
  68. package/examples/snapshots/test-all-mode.html +0 -46
  69. package/examples/snapshots/test-all-mode.snapshot.txt +0 -5
  70. package/examples/snapshots/validate.test.ts +0 -296
  71. package/packages/cli/package.json +0 -42
  72. package/packages/cli/src/__tests__/cli.test.ts +0 -434
  73. package/packages/cli/src/__tests__/errors.test.ts +0 -226
  74. package/packages/cli/src/__tests__/executor.test.ts +0 -275
  75. package/packages/cli/src/__tests__/formatter.test.ts +0 -260
  76. package/packages/cli/src/__tests__/parser.test.ts +0 -288
  77. package/packages/cli/src/__tests__/suggestions.test.ts +0 -255
  78. package/packages/cli/src/commands/back.ts +0 -22
  79. package/packages/cli/src/commands/check.ts +0 -33
  80. package/packages/cli/src/commands/clear.ts +0 -33
  81. package/packages/cli/src/commands/click.ts +0 -32
  82. package/packages/cli/src/commands/closetab.ts +0 -31
  83. package/packages/cli/src/commands/eval.ts +0 -41
  84. package/packages/cli/src/commands/fill.ts +0 -30
  85. package/packages/cli/src/commands/focus.ts +0 -33
  86. package/packages/cli/src/commands/forward.ts +0 -22
  87. package/packages/cli/src/commands/goto.ts +0 -34
  88. package/packages/cli/src/commands/help.ts +0 -162
  89. package/packages/cli/src/commands/hover.ts +0 -34
  90. package/packages/cli/src/commands/index.ts +0 -129
  91. package/packages/cli/src/commands/newtab.ts +0 -35
  92. package/packages/cli/src/commands/press.ts +0 -40
  93. package/packages/cli/src/commands/reload.ts +0 -25
  94. package/packages/cli/src/commands/screenshot.ts +0 -27
  95. package/packages/cli/src/commands/scroll.ts +0 -64
  96. package/packages/cli/src/commands/select.ts +0 -35
  97. package/packages/cli/src/commands/snapshot.ts +0 -21
  98. package/packages/cli/src/commands/tab.ts +0 -32
  99. package/packages/cli/src/commands/tabs.ts +0 -26
  100. package/packages/cli/src/commands/text.ts +0 -27
  101. package/packages/cli/src/commands/title.ts +0 -17
  102. package/packages/cli/src/commands/type.ts +0 -38
  103. package/packages/cli/src/commands/uncheck.ts +0 -33
  104. package/packages/cli/src/commands/url.ts +0 -17
  105. package/packages/cli/src/commands/wait.ts +0 -54
  106. package/packages/cli/src/errors.ts +0 -164
  107. package/packages/cli/src/executor.ts +0 -68
  108. package/packages/cli/src/formatter.ts +0 -215
  109. package/packages/cli/src/index.ts +0 -257
  110. package/packages/cli/src/parser.ts +0 -195
  111. package/packages/cli/src/suggestions.ts +0 -207
  112. package/packages/cli/src/terminal/Terminal.ts +0 -365
  113. package/packages/cli/src/terminal/index.ts +0 -5
  114. package/packages/cli/src/types.ts +0 -155
  115. package/packages/cli/tsconfig.json +0 -20
  116. package/packages/core/package.json +0 -35
  117. package/packages/core/src/actions.ts +0 -1210
  118. package/packages/core/src/errors.ts +0 -296
  119. package/packages/core/src/index.test.ts +0 -638
  120. package/packages/core/src/index.ts +0 -220
  121. package/packages/core/src/ref-map.ts +0 -107
  122. package/packages/core/src/snapshot.ts +0 -873
  123. package/packages/core/src/types.ts +0 -536
  124. package/packages/core/tsconfig.json +0 -23
  125. package/packages/extension/README.md +0 -129
  126. package/packages/extension/package.json +0 -43
  127. package/packages/extension/src/background.ts +0 -888
  128. package/packages/extension/src/content.ts +0 -172
  129. package/packages/extension/src/index.ts +0 -579
  130. package/packages/extension/src/session-manager.ts +0 -385
  131. package/packages/extension/src/types.ts +0 -162
  132. package/packages/extension/tsconfig.json +0 -28
  133. package/src/index.ts +0 -64
  134. package/tsconfig.build.json +0 -12
  135. package/tsconfig.json +0 -26
  136. package/vitest.config.ts +0 -13
@@ -1,434 +0,0 @@
1
- /**
2
- * CLI integration tests
3
- */
4
-
5
- import { describe, it, expect, vi, beforeEach } from 'vitest';
6
- import { createCLI } from '../index.js';
7
- import type { CommandClient, CommandResult } from '../types.js';
8
-
9
- /**
10
- * Create a mock client for testing
11
- */
12
- function createMockClient(overrides: Partial<CommandClient> = {}): CommandClient {
13
- return {
14
- execute: vi.fn().mockResolvedValue({ id: '1', success: true }),
15
- navigate: vi.fn().mockResolvedValue({ id: '1', success: true }),
16
- back: vi.fn().mockResolvedValue({ id: '1', success: true }),
17
- forward: vi.fn().mockResolvedValue({ id: '1', success: true }),
18
- reload: vi.fn().mockResolvedValue({ id: '1', success: true }),
19
- getUrl: vi.fn().mockResolvedValue('https://example.com'),
20
- getTitle: vi.fn().mockResolvedValue('Example Page'),
21
- snapshot: vi.fn().mockResolvedValue('- button "Submit" [@ref:1]'),
22
- click: vi.fn().mockResolvedValue({ id: '1', success: true }),
23
- type: vi.fn().mockResolvedValue({ id: '1', success: true }),
24
- fill: vi.fn().mockResolvedValue({ id: '1', success: true }),
25
- getText: vi.fn().mockResolvedValue('Hello'),
26
- isVisible: vi.fn().mockResolvedValue(true),
27
- screenshot: vi.fn().mockResolvedValue('data:image/png;base64,ABC123'),
28
- tabNew: vi.fn().mockResolvedValue({ tabId: 123, url: 'about:blank' }),
29
- tabClose: vi.fn().mockResolvedValue({ id: '1', success: true }),
30
- tabSwitch: vi.fn().mockResolvedValue({ id: '1', success: true }),
31
- tabList: vi.fn().mockResolvedValue([
32
- { id: 1, url: 'https://example.com', title: 'Example', active: true, index: 0 },
33
- ]),
34
- ...overrides,
35
- };
36
- }
37
-
38
- describe('createCLI', () => {
39
- let client: CommandClient;
40
- let cli: ReturnType<typeof createCLI>;
41
-
42
- beforeEach(() => {
43
- client = createMockClient();
44
- cli = createCLI(client);
45
- });
46
-
47
- describe('execute - single commands', () => {
48
- it('executes goto command', async () => {
49
- const result = await cli.execute('goto https://example.com');
50
- expect(result.success).toBe(true);
51
- expect(client.navigate).toHaveBeenCalledWith('https://example.com');
52
- });
53
-
54
- it('executes snapshot command', async () => {
55
- const result = await cli.execute('snapshot');
56
- expect(result.success).toBe(true);
57
- expect(client.snapshot).toHaveBeenCalled();
58
- });
59
-
60
- it('executes click command', async () => {
61
- const result = await cli.execute('click @ref:1');
62
- expect(result.success).toBe(true);
63
- expect(client.click).toHaveBeenCalledWith('@ref:1', { button: undefined });
64
- });
65
-
66
- it('executes type command', async () => {
67
- const result = await cli.execute('type @ref:1 "hello world"');
68
- expect(result.success).toBe(true);
69
- expect(client.type).toHaveBeenCalledWith('@ref:1', 'hello world', expect.any(Object));
70
- });
71
-
72
- it('executes help command', async () => {
73
- const result = await cli.execute('help');
74
- expect(result.success).toBe(true);
75
- expect(result.data).toContain('Commands');
76
- });
77
-
78
- it('returns empty result for empty input', async () => {
79
- const result = await cli.execute('');
80
- expect(result.success).toBe(true);
81
- expect(result.message).toBe('');
82
- });
83
-
84
- it('returns empty result for whitespace input', async () => {
85
- const result = await cli.execute(' ');
86
- expect(result.success).toBe(true);
87
- });
88
-
89
- it('handles unknown commands', async () => {
90
- const result = await cli.execute('unknowncommand');
91
- expect(result.success).toBe(false);
92
- expect(result.error).toContain('Unknown command');
93
- });
94
-
95
- it('includes suggestions for typos', async () => {
96
- const result = await cli.execute('clck @ref:1');
97
- expect(result.success).toBe(false);
98
- expect(result.error).toContain('click');
99
- });
100
- });
101
-
102
- describe('execute - multi-line commands', () => {
103
- it('executes multiple commands', async () => {
104
- const result = await cli.execute(`
105
- goto https://example.com
106
- snapshot
107
- `);
108
- expect(result.success).toBe(true);
109
- expect(client.navigate).toHaveBeenCalled();
110
- expect(client.snapshot).toHaveBeenCalled();
111
- });
112
-
113
- it('stops on first error', async () => {
114
- client = createMockClient({
115
- navigate: vi.fn().mockResolvedValue({ id: '1', success: false, error: 'Failed' }),
116
- });
117
- cli = createCLI(client);
118
-
119
- const result = await cli.execute(`
120
- goto https://example.com
121
- snapshot
122
- `);
123
- expect(result.success).toBe(false);
124
- expect(client.snapshot).not.toHaveBeenCalled();
125
- });
126
-
127
- it('skips comment lines', async () => {
128
- const result = await cli.execute(`
129
- # Navigate first
130
- goto https://example.com
131
- # Then take snapshot
132
- snapshot
133
- `);
134
- expect(result.success).toBe(true);
135
- expect(client.navigate).toHaveBeenCalled();
136
- expect(client.snapshot).toHaveBeenCalled();
137
- });
138
-
139
- it('skips empty lines', async () => {
140
- const result = await cli.execute(`
141
- goto https://example.com
142
-
143
- snapshot
144
- `);
145
- expect(result.success).toBe(true);
146
- });
147
-
148
- it('returns last result data on success', async () => {
149
- const result = await cli.execute(`
150
- goto https://example.com
151
- snapshot
152
- `);
153
- expect(result.data).toContain('button');
154
- });
155
-
156
- it('includes failure index in error data', async () => {
157
- // Use click which properly handles error results, not snapshot
158
- client = createMockClient({
159
- click: vi.fn().mockResolvedValue({ id: '1', success: false, error: 'Element not found' }),
160
- });
161
- cli = createCLI(client);
162
-
163
- const result = await cli.execute(`
164
- goto https://example.com
165
- click @ref:1
166
- `);
167
- expect(result.success).toBe(false);
168
- expect(result.data).toHaveProperty('failedAt', 1);
169
- });
170
- });
171
-
172
- describe('executeAll', () => {
173
- it('returns batch result', async () => {
174
- const result = await cli.executeAll(`
175
- goto https://example.com
176
- snapshot
177
- `);
178
- expect(result.allSucceeded).toBe(true);
179
- expect(result.results.length).toBe(2);
180
- expect(result.executed).toBe(2);
181
- expect(result.firstFailedIndex).toBe(-1);
182
- });
183
-
184
- it('stops on error by default', async () => {
185
- client = createMockClient({
186
- navigate: vi.fn().mockResolvedValue({ id: '1', success: false, error: 'Failed' }),
187
- });
188
- cli = createCLI(client);
189
-
190
- const result = await cli.executeAll(`
191
- goto https://example.com
192
- snapshot
193
- click @ref:1
194
- `);
195
- expect(result.allSucceeded).toBe(false);
196
- expect(result.executed).toBe(1);
197
- expect(result.firstFailedIndex).toBe(0);
198
- });
199
-
200
- it('continues on error with flag', async () => {
201
- client = createMockClient({
202
- navigate: vi.fn().mockResolvedValue({ id: '1', success: false, error: 'Failed' }),
203
- });
204
- cli = createCLI(client);
205
-
206
- const result = await cli.executeAll(
207
- `
208
- goto https://example.com
209
- snapshot
210
- click @ref:1
211
- `,
212
- { continueOnError: true }
213
- );
214
- expect(result.allSucceeded).toBe(false);
215
- expect(result.executed).toBe(3);
216
- expect(result.firstFailedIndex).toBe(0);
217
- });
218
-
219
- it('returns empty result for empty input', async () => {
220
- const result = await cli.executeAll('');
221
- expect(result.allSucceeded).toBe(true);
222
- expect(result.results).toHaveLength(0);
223
- expect(result.executed).toBe(0);
224
- });
225
- });
226
-
227
- describe('getCommands', () => {
228
- it('returns all commands', () => {
229
- const commands = cli.getCommands();
230
- expect(commands.length).toBeGreaterThan(0);
231
- expect(commands.find((c) => c.name === 'goto')).toBeDefined();
232
- expect(commands.find((c) => c.name === 'click')).toBeDefined();
233
- expect(commands.find((c) => c.name === 'snapshot')).toBeDefined();
234
- });
235
-
236
- it('each command has required fields', () => {
237
- const commands = cli.getCommands();
238
- for (const cmd of commands) {
239
- expect(cmd.name).toBeDefined();
240
- expect(cmd.description).toBeDefined();
241
- expect(cmd.usage).toBeDefined();
242
- expect(cmd.execute).toBeInstanceOf(Function);
243
- }
244
- });
245
- });
246
-
247
- describe('getHelp', () => {
248
- it('returns general help', () => {
249
- const help = cli.getHelp();
250
- expect(help).toContain('Commands');
251
- expect(help).toContain('goto');
252
- expect(help).toContain('click');
253
- });
254
-
255
- it('returns command-specific help', () => {
256
- const help = cli.getHelp('goto');
257
- expect(help).toContain('goto');
258
- expect(help).toContain('Navigate');
259
- });
260
-
261
- it('returns error for unknown command', () => {
262
- const help = cli.getHelp('unknowncommand');
263
- expect(help).toContain('Unknown');
264
- });
265
- });
266
- });
267
-
268
- describe('command execution', () => {
269
- let client: CommandClient;
270
- let cli: ReturnType<typeof createCLI>;
271
-
272
- beforeEach(() => {
273
- client = createMockClient();
274
- cli = createCLI(client);
275
- });
276
-
277
- describe('navigation commands', () => {
278
- it('goto adds https if missing', async () => {
279
- await cli.execute('goto example.com');
280
- expect(client.navigate).toHaveBeenCalledWith('https://example.com');
281
- });
282
-
283
- it('goto preserves existing protocol', async () => {
284
- await cli.execute('goto http://example.com');
285
- expect(client.navigate).toHaveBeenCalledWith('http://example.com');
286
- });
287
-
288
- it('back calls client.back', async () => {
289
- await cli.execute('back');
290
- expect(client.back).toHaveBeenCalled();
291
- });
292
-
293
- it('forward calls client.forward', async () => {
294
- await cli.execute('forward');
295
- expect(client.forward).toHaveBeenCalled();
296
- });
297
-
298
- it('reload calls client.reload', async () => {
299
- await cli.execute('reload');
300
- expect(client.reload).toHaveBeenCalled();
301
- });
302
-
303
- it('reload --hard passes bypassCache', async () => {
304
- await cli.execute('reload --hard');
305
- expect(client.reload).toHaveBeenCalledWith({ bypassCache: true });
306
- });
307
- });
308
-
309
- describe('interaction commands', () => {
310
- it('click with right button', async () => {
311
- await cli.execute('click @ref:1 --button right');
312
- expect(client.click).toHaveBeenCalledWith('@ref:1', { button: 'right' });
313
- });
314
-
315
- it('type with delay', async () => {
316
- await cli.execute('type @ref:1 hello --delay 50');
317
- expect(client.type).toHaveBeenCalledWith('@ref:1', 'hello', expect.objectContaining({ delay: 50 }));
318
- });
319
-
320
- it('type with clear flag', async () => {
321
- await cli.execute('type @ref:1 hello --clear');
322
- expect(client.type).toHaveBeenCalledWith('@ref:1', 'hello', expect.objectContaining({ clear: true }));
323
- });
324
-
325
- it('fill sets value', async () => {
326
- await cli.execute('fill @ref:1 "test value"');
327
- expect(client.fill).toHaveBeenCalledWith('@ref:1', 'test value');
328
- });
329
- });
330
-
331
- describe('tab commands', () => {
332
- it('tabs lists tabs', async () => {
333
- const result = await cli.execute('tabs');
334
- expect(result.success).toBe(true);
335
- expect(client.tabList).toHaveBeenCalled();
336
- });
337
-
338
- it('newtab opens new tab', async () => {
339
- await cli.execute('newtab');
340
- expect(client.tabNew).toHaveBeenCalled();
341
- });
342
-
343
- it('newtab with URL', async () => {
344
- await cli.execute('newtab https://example.com');
345
- expect(client.tabNew).toHaveBeenCalledWith(expect.objectContaining({
346
- url: 'https://example.com',
347
- }));
348
- });
349
-
350
- it('newtab --background', async () => {
351
- await cli.execute('newtab --background');
352
- expect(client.tabNew).toHaveBeenCalledWith(expect.objectContaining({
353
- active: false,
354
- }));
355
- });
356
-
357
- it('tab switches to tab', async () => {
358
- await cli.execute('tab 123');
359
- expect(client.tabSwitch).toHaveBeenCalledWith(123);
360
- });
361
-
362
- it('closetab closes current tab', async () => {
363
- await cli.execute('closetab');
364
- expect(client.tabClose).toHaveBeenCalledWith(undefined);
365
- });
366
-
367
- it('closetab closes specific tab', async () => {
368
- await cli.execute('closetab 123');
369
- expect(client.tabClose).toHaveBeenCalledWith(123);
370
- });
371
- });
372
-
373
- describe('inspection commands', () => {
374
- it('snapshot returns tree', async () => {
375
- const result = await cli.execute('snapshot');
376
- expect(result.success).toBe(true);
377
- expect(result.data).toContain('button');
378
- });
379
-
380
- it('screenshot returns data URL', async () => {
381
- const result = await cli.execute('screenshot');
382
- expect(result.success).toBe(true);
383
- expect(result.data).toContain('data:image');
384
- });
385
-
386
- it('url returns current URL', async () => {
387
- const result = await cli.execute('url');
388
- expect(result.success).toBe(true);
389
- expect(result.data).toBe('https://example.com');
390
- });
391
-
392
- it('title returns page title', async () => {
393
- const result = await cli.execute('title');
394
- expect(result.success).toBe(true);
395
- expect(result.data).toBe('Example Page');
396
- });
397
-
398
- it('text returns element text', async () => {
399
- const result = await cli.execute('text @ref:1');
400
- expect(result.success).toBe(true);
401
- expect(result.data).toBe('Hello');
402
- });
403
- });
404
- });
405
-
406
- describe('error handling', () => {
407
- it('handles client errors gracefully', async () => {
408
- const client = createMockClient({
409
- navigate: vi.fn().mockRejectedValue(new Error('Network error')),
410
- });
411
- const cli = createCLI(client);
412
-
413
- const result = await cli.execute('goto https://example.com');
414
- expect(result.success).toBe(false);
415
- expect(result.error).toContain('Network error');
416
- });
417
-
418
- it('handles missing arguments', async () => {
419
- const client = createMockClient();
420
- const cli = createCLI(client);
421
-
422
- const result = await cli.execute('goto');
423
- expect(result.success).toBe(false);
424
- expect(result.error).toContain('URL required');
425
- });
426
-
427
- it('handles invalid tab ID', async () => {
428
- const client = createMockClient();
429
- const cli = createCLI(client);
430
-
431
- const result = await cli.execute('tab notanumber');
432
- expect(result.success).toBe(false);
433
- });
434
- });
@@ -1,226 +0,0 @@
1
- /**
2
- * Error classes tests
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- CLIError,
8
- CommandNotFoundError,
9
- InvalidArgumentsError,
10
- ParseError,
11
- ExecutionError,
12
- ElementNotFoundError,
13
- NavigationError,
14
- TimeoutError,
15
- } from '../errors.js';
16
-
17
- describe('CLIError', () => {
18
- it('creates basic error', () => {
19
- const error = new CLIError('Something went wrong');
20
- expect(error.message).toBe('Something went wrong');
21
- expect(error.name).toBe('CLIError');
22
- expect(error).toBeInstanceOf(Error);
23
- });
24
-
25
- it('creates error with suggestions', () => {
26
- const error = new CLIError('Error', {
27
- suggestions: ['Try this', 'Or that'],
28
- });
29
- expect(error.suggestions).toEqual(['Try this', 'Or that']);
30
- });
31
-
32
- it('creates error with usage', () => {
33
- const error = new CLIError('Error', { usage: 'command <arg>' });
34
- expect(error.usage).toBe('command <arg>');
35
- });
36
-
37
- it('creates error with command', () => {
38
- const error = new CLIError('Error', { command: 'click' });
39
- expect(error.command).toBe('click');
40
- });
41
-
42
- describe('toFormattedString', () => {
43
- it('formats basic error', () => {
44
- const error = new CLIError('Something failed');
45
- expect(error.toFormattedString()).toBe('Something failed');
46
- });
47
-
48
- it('includes usage in formatted output', () => {
49
- const error = new CLIError('Missing argument', {
50
- usage: 'click <selector>',
51
- });
52
- const formatted = error.toFormattedString();
53
- expect(formatted).toContain('Missing argument');
54
- expect(formatted).toContain('Usage: click <selector>');
55
- });
56
-
57
- it('includes suggestions in formatted output', () => {
58
- const error = new CLIError('Error', {
59
- suggestions: ['Try running snapshot first', 'Check the selector'],
60
- });
61
- const formatted = error.toFormattedString();
62
- expect(formatted).toContain('Suggestions:');
63
- expect(formatted).toContain('Try running snapshot first');
64
- expect(formatted).toContain('Check the selector');
65
- });
66
-
67
- it('includes both usage and suggestions', () => {
68
- const error = new CLIError('Error', {
69
- usage: 'click <selector>',
70
- suggestions: ['Use @ref:N format'],
71
- });
72
- const formatted = error.toFormattedString();
73
- expect(formatted).toContain('Usage:');
74
- expect(formatted).toContain('Suggestions:');
75
- });
76
- });
77
- });
78
-
79
- describe('CommandNotFoundError', () => {
80
- it('creates error with command name', () => {
81
- const error = new CommandNotFoundError('clck');
82
- expect(error.message).toBe('Unknown command: clck');
83
- expect(error.commandName).toBe('clck');
84
- expect(error.name).toBe('CommandNotFoundError');
85
- });
86
-
87
- it('includes similar commands in suggestions', () => {
88
- const error = new CommandNotFoundError('clck', ['click']);
89
- expect(error.suggestions).toContain("Did you mean 'click'?");
90
- });
91
-
92
- it('includes multiple similar commands', () => {
93
- const error = new CommandNotFoundError('chck', ['check', 'click']);
94
- expect(error.suggestions?.length).toBe(2);
95
- });
96
-
97
- it('provides default suggestion when no similar commands', () => {
98
- const error = new CommandNotFoundError('xyz');
99
- expect(error.suggestions).toContain('Type "help" to see available commands');
100
- });
101
-
102
- it('provides default suggestion for empty similar commands', () => {
103
- const error = new CommandNotFoundError('xyz', []);
104
- expect(error.suggestions).toContain('Type "help" to see available commands');
105
- });
106
- });
107
-
108
- describe('InvalidArgumentsError', () => {
109
- it('creates error with message', () => {
110
- const error = new InvalidArgumentsError('Missing selector');
111
- expect(error.message).toBe('Missing selector');
112
- expect(error.name).toBe('InvalidArgumentsError');
113
- });
114
-
115
- it('includes usage', () => {
116
- const error = new InvalidArgumentsError('Missing selector', 'click <selector>');
117
- expect(error.usage).toBe('click <selector>');
118
- });
119
-
120
- it('includes examples as suggestions', () => {
121
- const error = new InvalidArgumentsError(
122
- 'Missing selector',
123
- 'click <selector>',
124
- ['click @ref:5', 'click #submit']
125
- );
126
- expect(error.suggestions).toContain('Example: click @ref:5');
127
- expect(error.suggestions).toContain('Example: click #submit');
128
- });
129
- });
130
-
131
- describe('ParseError', () => {
132
- it('creates error with message', () => {
133
- const error = new ParseError('Unterminated quote');
134
- expect(error.message).toBe('Unterminated quote');
135
- expect(error.name).toBe('ParseError');
136
- });
137
-
138
- it('includes default suggestions', () => {
139
- const error = new ParseError('Syntax error');
140
- expect(error.suggestions?.length).toBeGreaterThan(0);
141
- expect(error.suggestions?.some((s) => s.includes('quotes'))).toBe(true);
142
- });
143
-
144
- it('uses custom hint when provided', () => {
145
- const error = new ParseError('Error', 'Custom hint');
146
- expect(error.suggestions).toEqual(['Custom hint']);
147
- });
148
- });
149
-
150
- describe('ExecutionError', () => {
151
- it('creates error with message', () => {
152
- const error = new ExecutionError('Execution failed');
153
- expect(error.message).toBe('Execution failed');
154
- expect(error.name).toBe('ExecutionError');
155
- });
156
-
157
- it('includes suggestions', () => {
158
- const error = new ExecutionError('Failed', {
159
- suggestions: ['Try again'],
160
- });
161
- expect(error.suggestions).toEqual(['Try again']);
162
- });
163
-
164
- it('includes command', () => {
165
- const error = new ExecutionError('Failed', {
166
- command: 'click',
167
- });
168
- expect(error.command).toBe('click');
169
- });
170
- });
171
-
172
- describe('ElementNotFoundError', () => {
173
- it('creates error with selector', () => {
174
- const error = new ElementNotFoundError('@ref:5');
175
- expect(error.message).toBe('Element not found: @ref:5');
176
- expect(error.name).toBe('ElementNotFoundError');
177
- });
178
-
179
- it('includes helpful suggestions', () => {
180
- const error = new ElementNotFoundError('@ref:5');
181
- expect(error.suggestions?.some((s) => s.includes('snapshot'))).toBe(true);
182
- expect(error.suggestions?.some((s) => s.includes('wait'))).toBe(true);
183
- });
184
-
185
- it('includes selector in wait suggestion', () => {
186
- const error = new ElementNotFoundError('#my-button');
187
- expect(error.suggestions?.some((s) => s.includes('#my-button'))).toBe(true);
188
- });
189
- });
190
-
191
- describe('NavigationError', () => {
192
- it('creates error with URL', () => {
193
- const error = new NavigationError('https://example.com');
194
- expect(error.message).toContain('example.com');
195
- expect(error.name).toBe('NavigationError');
196
- });
197
-
198
- it('creates error with reason', () => {
199
- const error = new NavigationError('https://example.com', 'Network error');
200
- expect(error.message).toContain('Network error');
201
- });
202
-
203
- it('includes protocol suggestion', () => {
204
- const error = new NavigationError('example.com');
205
- expect(error.suggestions?.some((s) => s.includes('https://'))).toBe(true);
206
- });
207
- });
208
-
209
- describe('TimeoutError', () => {
210
- it('creates error with operation', () => {
211
- const error = new TimeoutError('waiting for element');
212
- expect(error.message).toBe('Timeout: waiting for element');
213
- expect(error.name).toBe('TimeoutError');
214
- });
215
-
216
- it('includes general suggestions', () => {
217
- const error = new TimeoutError('operation');
218
- expect(error.suggestions?.some((s) => s.includes('wait'))).toBe(true);
219
- expect(error.suggestions?.some((s) => s.includes('snapshot'))).toBe(true);
220
- });
221
-
222
- it('includes selector-specific suggestion when provided', () => {
223
- const error = new TimeoutError('waiting', '@ref:5');
224
- expect(error.suggestions?.some((s) => s.includes('@ref:5'))).toBe(true);
225
- });
226
- });