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,288 +0,0 @@
1
- /**
2
- * Parser tests
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- parseCommand,
8
- getFlagString,
9
- getFlagNumber,
10
- getFlagBool,
11
- } from '../parser.js';
12
- import { ParseError } from '../errors.js';
13
-
14
- describe('parseCommand', () => {
15
- describe('basic parsing', () => {
16
- it('parses a simple command', () => {
17
- const result = parseCommand('goto');
18
- expect(result.name).toBe('goto');
19
- expect(result.args).toEqual([]);
20
- expect(result.flags).toEqual({});
21
- });
22
-
23
- it('parses command with one argument', () => {
24
- const result = parseCommand('goto https://example.com');
25
- expect(result.name).toBe('goto');
26
- expect(result.args).toEqual(['https://example.com']);
27
- });
28
-
29
- it('parses command with multiple arguments', () => {
30
- const result = parseCommand('type @ref:1 hello world');
31
- expect(result.name).toBe('type');
32
- expect(result.args).toEqual(['@ref:1', 'hello', 'world']);
33
- });
34
-
35
- it('converts command name to lowercase', () => {
36
- const result = parseCommand('GOTO https://example.com');
37
- expect(result.name).toBe('goto');
38
- });
39
-
40
- it('preserves original input in raw field', () => {
41
- const input = 'goto https://example.com';
42
- const result = parseCommand(input);
43
- expect(result.raw).toBe(input);
44
- });
45
-
46
- it('trims whitespace', () => {
47
- const result = parseCommand(' goto https://example.com ');
48
- expect(result.name).toBe('goto');
49
- expect(result.args).toEqual(['https://example.com']);
50
- });
51
- });
52
-
53
- describe('quoted strings', () => {
54
- it('parses double-quoted strings', () => {
55
- const result = parseCommand('type @ref:1 "hello world"');
56
- expect(result.args).toEqual(['@ref:1', 'hello world']);
57
- });
58
-
59
- it('parses single-quoted strings', () => {
60
- const result = parseCommand("type @ref:1 'hello world'");
61
- expect(result.args).toEqual(['@ref:1', 'hello world']);
62
- });
63
-
64
- it('handles empty quoted strings (parsed but filtered as empty token)', () => {
65
- // Note: empty quoted strings result in empty tokens which are filtered
66
- const result = parseCommand('fill @ref:1 ""');
67
- expect(result.args).toEqual(['@ref:1']);
68
- });
69
-
70
- it('preserves spaces in quoted strings', () => {
71
- const result = parseCommand('type @ref:1 "hello world"');
72
- expect(result.args).toEqual(['@ref:1', 'hello world']);
73
- });
74
-
75
- it('handles quotes within different quotes', () => {
76
- const result = parseCommand('type @ref:1 "it\'s working"');
77
- expect(result.args).toEqual(['@ref:1', "it's working"]);
78
- });
79
-
80
- it('throws on unterminated double quote', () => {
81
- expect(() => parseCommand('type @ref:1 "hello')).toThrow(ParseError);
82
- expect(() => parseCommand('type @ref:1 "hello')).toThrow('Unterminated double quote');
83
- });
84
-
85
- it('throws on unterminated single quote', () => {
86
- expect(() => parseCommand("type @ref:1 'hello")).toThrow(ParseError);
87
- expect(() => parseCommand("type @ref:1 'hello")).toThrow('Unterminated single quote');
88
- });
89
- });
90
-
91
- describe('escape characters', () => {
92
- it('handles escaped quotes', () => {
93
- const result = parseCommand('type @ref:1 "hello \\"world\\""');
94
- expect(result.args).toEqual(['@ref:1', 'hello "world"']);
95
- });
96
-
97
- it('handles escaped backslash', () => {
98
- const result = parseCommand('type @ref:1 "path\\\\file"');
99
- expect(result.args).toEqual(['@ref:1', 'path\\file']);
100
- });
101
- });
102
-
103
- describe('flags', () => {
104
- it('parses long flag with value', () => {
105
- const result = parseCommand('wait @ref:1 --state visible');
106
- expect(result.flags).toEqual({ state: 'visible' });
107
- });
108
-
109
- it('parses long flag with equals syntax', () => {
110
- const result = parseCommand('wait @ref:1 --state=visible');
111
- expect(result.flags).toEqual({ state: 'visible' });
112
- });
113
-
114
- it('parses boolean long flag', () => {
115
- const result = parseCommand('reload --hard');
116
- expect(result.flags).toEqual({ hard: true });
117
- });
118
-
119
- it('parses short flag with value', () => {
120
- const result = parseCommand('screenshot -f png');
121
- expect(result.flags).toEqual({ f: 'png' });
122
- });
123
-
124
- it('parses boolean short flag', () => {
125
- const result = parseCommand('reload -h');
126
- expect(result.flags).toEqual({ h: true });
127
- });
128
-
129
- it('parses multiple flags', () => {
130
- const result = parseCommand('screenshot --format png --quality 80');
131
- expect(result.flags).toEqual({ format: 'png', quality: '80' });
132
- });
133
-
134
- it('separates args and flags correctly', () => {
135
- const result = parseCommand('click @ref:1 --button right');
136
- expect(result.args).toEqual(['@ref:1']);
137
- expect(result.flags).toEqual({ button: 'right' });
138
- });
139
-
140
- it('handles flag value with equals sign', () => {
141
- const result = parseCommand('eval --script=a=b');
142
- expect(result.flags).toEqual({ script: 'a=b' });
143
- });
144
- });
145
-
146
- describe('edge cases', () => {
147
- it('throws on empty input', () => {
148
- expect(() => parseCommand('')).toThrow(ParseError);
149
- expect(() => parseCommand('')).toThrow('Empty command');
150
- });
151
-
152
- it('throws on whitespace-only input', () => {
153
- expect(() => parseCommand(' ')).toThrow(ParseError);
154
- });
155
-
156
- it('handles tabs as whitespace', () => {
157
- const result = parseCommand('goto\thttps://example.com');
158
- expect(result.name).toBe('goto');
159
- expect(result.args).toEqual(['https://example.com']);
160
- });
161
-
162
- it('handles URLs with special characters', () => {
163
- const result = parseCommand('goto https://example.com/path?query=value&foo=bar');
164
- expect(result.args).toEqual(['https://example.com/path?query=value&foo=bar']);
165
- });
166
-
167
- it('handles @ref selectors', () => {
168
- const result = parseCommand('click @ref:123');
169
- expect(result.args).toEqual(['@ref:123']);
170
- });
171
-
172
- it('handles CSS selectors', () => {
173
- const result = parseCommand('click #submit-button');
174
- expect(result.args).toEqual(['#submit-button']);
175
- });
176
-
177
- it('handles class selectors', () => {
178
- const result = parseCommand('click .btn.primary');
179
- expect(result.args).toEqual(['.btn.primary']);
180
- });
181
- });
182
- });
183
-
184
- describe('getFlagString', () => {
185
- it('returns string value for string flag', () => {
186
- const flags = { format: 'png' };
187
- expect(getFlagString(flags, 'format')).toBe('png');
188
- });
189
-
190
- it('returns undefined for missing flag', () => {
191
- const flags = {};
192
- expect(getFlagString(flags, 'format')).toBeUndefined();
193
- });
194
-
195
- it('returns default for missing flag', () => {
196
- const flags = {};
197
- expect(getFlagString(flags, 'format', 'jpeg')).toBe('jpeg');
198
- });
199
-
200
- it('returns default for boolean flag', () => {
201
- const flags = { format: true };
202
- expect(getFlagString(flags, 'format', 'jpeg')).toBe('jpeg');
203
- });
204
- });
205
-
206
- describe('getFlagNumber', () => {
207
- it('returns number for numeric string flag', () => {
208
- const flags = { quality: '80' };
209
- expect(getFlagNumber(flags, 'quality')).toBe(80);
210
- });
211
-
212
- it('returns undefined for missing flag', () => {
213
- const flags = {};
214
- expect(getFlagNumber(flags, 'quality')).toBeUndefined();
215
- });
216
-
217
- it('returns default for missing flag', () => {
218
- const flags = {};
219
- expect(getFlagNumber(flags, 'quality', 100)).toBe(100);
220
- });
221
-
222
- it('returns default for non-numeric string', () => {
223
- const flags = { quality: 'high' };
224
- expect(getFlagNumber(flags, 'quality', 100)).toBe(100);
225
- });
226
-
227
- it('returns default for boolean flag', () => {
228
- const flags = { quality: true };
229
- expect(getFlagNumber(flags, 'quality', 100)).toBe(100);
230
- });
231
-
232
- it('handles zero correctly', () => {
233
- const flags = { offset: '0' };
234
- expect(getFlagNumber(flags, 'offset')).toBe(0);
235
- });
236
-
237
- it('handles negative numbers', () => {
238
- const flags = { offset: '-10' };
239
- expect(getFlagNumber(flags, 'offset')).toBe(-10);
240
- });
241
-
242
- it('handles floating point', () => {
243
- const flags = { scale: '1.5' };
244
- expect(getFlagNumber(flags, 'scale')).toBe(1.5);
245
- });
246
- });
247
-
248
- describe('getFlagBool', () => {
249
- it('returns true for boolean true flag', () => {
250
- const flags = { hard: true };
251
- expect(getFlagBool(flags, 'hard')).toBe(true);
252
- });
253
-
254
- it('returns false for boolean false flag', () => {
255
- const flags = { hard: false };
256
- expect(getFlagBool(flags, 'hard')).toBe(false);
257
- });
258
-
259
- it('returns false for missing flag by default', () => {
260
- const flags = {};
261
- expect(getFlagBool(flags, 'hard')).toBe(false);
262
- });
263
-
264
- it('returns default for missing flag', () => {
265
- const flags = {};
266
- expect(getFlagBool(flags, 'hard', true)).toBe(true);
267
- });
268
-
269
- it('returns true for string "true"', () => {
270
- const flags = { hard: 'true' };
271
- expect(getFlagBool(flags, 'hard')).toBe(true);
272
- });
273
-
274
- it('returns true for string "1"', () => {
275
- const flags = { hard: '1' };
276
- expect(getFlagBool(flags, 'hard')).toBe(true);
277
- });
278
-
279
- it('returns false for other strings', () => {
280
- const flags = { hard: 'yes' };
281
- expect(getFlagBool(flags, 'hard')).toBe(false);
282
- });
283
-
284
- it('is case insensitive for "true"', () => {
285
- const flags = { hard: 'TRUE' };
286
- expect(getFlagBool(flags, 'hard')).toBe(true);
287
- });
288
- });
@@ -1,255 +0,0 @@
1
- /**
2
- * Suggestions tests
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- findSimilarCommands,
8
- getContextualSuggestion,
9
- commandCategories,
10
- getCommandCategory,
11
- getNextStepSuggestions,
12
- } from '../suggestions.js';
13
-
14
- describe('findSimilarCommands', () => {
15
- describe('exact prefix matching', () => {
16
- it('finds commands starting with input', () => {
17
- const results = findSimilarCommands('go');
18
- expect(results).toContain('goto');
19
- });
20
-
21
- it('finds commands starting with input (tabs)', () => {
22
- const results = findSimilarCommands('tab');
23
- expect(results).toContain('tabs');
24
- expect(results).toContain('tab');
25
- });
26
-
27
- it('prioritizes prefix matches', () => {
28
- const results = findSimilarCommands('scr');
29
- expect(results[0]).toBe('scroll');
30
- });
31
- });
32
-
33
- describe('typo correction', () => {
34
- it('suggests click for clck', () => {
35
- const results = findSimilarCommands('clck');
36
- expect(results).toContain('click');
37
- });
38
-
39
- it('suggests goto for gto', () => {
40
- const results = findSimilarCommands('gto');
41
- expect(results).toContain('goto');
42
- });
43
-
44
- it('suggests snapshot for snapshto', () => {
45
- const results = findSimilarCommands('snapshto');
46
- expect(results).toContain('snapshot');
47
- });
48
-
49
- it('suggests type for typ', () => {
50
- const results = findSimilarCommands('typ');
51
- expect(results).toContain('type');
52
- });
53
-
54
- it('suggests fill for fil', () => {
55
- const results = findSimilarCommands('fil');
56
- expect(results).toContain('fill');
57
- });
58
-
59
- it('suggests reload for relod', () => {
60
- const results = findSimilarCommands('relod');
61
- expect(results).toContain('reload');
62
- });
63
- });
64
-
65
- describe('substring matching', () => {
66
- it('finds commands containing input', () => {
67
- const results = findSimilarCommands('shot');
68
- expect(results).toContain('screenshot');
69
- });
70
-
71
- it('finds check in commands', () => {
72
- const results = findSimilarCommands('check');
73
- expect(results).toContain('check');
74
- expect(results).toContain('uncheck');
75
- });
76
- });
77
-
78
- describe('limits', () => {
79
- it('returns at most maxSuggestions results', () => {
80
- const results = findSimilarCommands('t', 2);
81
- expect(results.length).toBeLessThanOrEqual(2);
82
- });
83
-
84
- it('returns empty for completely unrelated input', () => {
85
- const results = findSimilarCommands('xyzabc123');
86
- expect(results).toHaveLength(0);
87
- });
88
- });
89
-
90
- describe('case insensitivity', () => {
91
- it('matches regardless of case', () => {
92
- const results = findSimilarCommands('GOTO');
93
- expect(results).toContain('goto');
94
- });
95
-
96
- it('matches mixed case', () => {
97
- const results = findSimilarCommands('GoTo');
98
- expect(results).toContain('goto');
99
- });
100
- });
101
- });
102
-
103
- describe('getContextualSuggestion', () => {
104
- describe('element not found', () => {
105
- it('provides suggestion for "not found" error', () => {
106
- const suggestion = getContextualSuggestion('click', 'Element not found', ['@ref:5']);
107
- expect(suggestion).toContain('snapshot');
108
- expect(suggestion).toContain('@ref:5');
109
- });
110
-
111
- it('provides suggestion for "no element" error', () => {
112
- const suggestion = getContextualSuggestion('click', 'No element matches selector', ['#btn']);
113
- expect(suggestion).toContain('snapshot');
114
- });
115
- });
116
-
117
- describe('invalid selector', () => {
118
- it('provides suggestion for selector errors', () => {
119
- const suggestion = getContextualSuggestion('click', 'Invalid selector', ['>>>']);
120
- expect(suggestion).toContain('@ref:5');
121
- expect(suggestion).toContain('#id');
122
- });
123
- });
124
-
125
- describe('navigation errors', () => {
126
- it('provides suggestion for navigation failures', () => {
127
- const suggestion = getContextualSuggestion('goto', 'Failed to navigate', ['example.com']);
128
- expect(suggestion).toContain('https://');
129
- });
130
-
131
- it('provides suggestion for URL errors', () => {
132
- // Note: 'invalid' pattern matches first, giving selector advice
133
- // Use 'navigate' or 'url' pattern for navigation-specific advice
134
- const suggestion = getContextualSuggestion('goto', 'Failed to navigate to URL', ['not-a-url']);
135
- expect(suggestion).toContain('https://');
136
- });
137
- });
138
-
139
- describe('timeout errors', () => {
140
- it('provides suggestion for timeout', () => {
141
- const suggestion = getContextualSuggestion('wait', 'Timeout waiting for element', ['@ref:5']);
142
- expect(suggestion).toContain('wait');
143
- expect(suggestion).toContain('@ref:5');
144
- });
145
- });
146
-
147
- describe('permission errors', () => {
148
- it('provides suggestion for permission denied', () => {
149
- const suggestion = getContextualSuggestion('click', 'Permission denied', ['#btn']);
150
- expect(suggestion).toContain('Cross-origin');
151
- });
152
-
153
- it('provides suggestion for blocked actions', () => {
154
- const suggestion = getContextualSuggestion('type', 'Action blocked by security policy', ['#input']);
155
- expect(suggestion).toContain('iframe');
156
- });
157
- });
158
-
159
- describe('no suggestion', () => {
160
- it('returns null for unknown errors', () => {
161
- const suggestion = getContextualSuggestion('click', 'Some random error', ['@ref:1']);
162
- expect(suggestion).toBeNull();
163
- });
164
- });
165
- });
166
-
167
- describe('commandCategories', () => {
168
- it('has navigation category', () => {
169
- expect(commandCategories.navigation).toBeDefined();
170
- expect(commandCategories.navigation.commands).toContain('goto');
171
- expect(commandCategories.navigation.commands).toContain('back');
172
- });
173
-
174
- it('has inspection category', () => {
175
- expect(commandCategories.inspection).toBeDefined();
176
- expect(commandCategories.inspection.commands).toContain('snapshot');
177
- expect(commandCategories.inspection.commands).toContain('screenshot');
178
- });
179
-
180
- it('has interaction category', () => {
181
- expect(commandCategories.interaction).toBeDefined();
182
- expect(commandCategories.interaction.commands).toContain('click');
183
- expect(commandCategories.interaction.commands).toContain('type');
184
- });
185
-
186
- it('has forms category', () => {
187
- expect(commandCategories.forms).toBeDefined();
188
- expect(commandCategories.forms.commands).toContain('check');
189
- expect(commandCategories.forms.commands).toContain('select');
190
- });
191
-
192
- it('has tabs category', () => {
193
- expect(commandCategories.tabs).toBeDefined();
194
- expect(commandCategories.tabs.commands).toContain('tabs');
195
- expect(commandCategories.tabs.commands).toContain('newtab');
196
- });
197
-
198
- it('has utility category', () => {
199
- expect(commandCategories.utility).toBeDefined();
200
- expect(commandCategories.utility.commands).toContain('wait');
201
- expect(commandCategories.utility.commands).toContain('help');
202
- });
203
-
204
- it('each category has name and description', () => {
205
- for (const category of Object.values(commandCategories)) {
206
- expect(category.name).toBeDefined();
207
- expect(category.description).toBeDefined();
208
- expect(category.commands.length).toBeGreaterThan(0);
209
- }
210
- });
211
- });
212
-
213
- describe('getCommandCategory', () => {
214
- it('returns correct category for navigation commands', () => {
215
- expect(getCommandCategory('goto')).toBe('navigation');
216
- expect(getCommandCategory('back')).toBe('navigation');
217
- expect(getCommandCategory('reload')).toBe('navigation');
218
- });
219
-
220
- it('returns correct category for interaction commands', () => {
221
- expect(getCommandCategory('click')).toBe('interaction');
222
- expect(getCommandCategory('type')).toBe('interaction');
223
- });
224
-
225
- it('returns correct category for inspection commands', () => {
226
- expect(getCommandCategory('snapshot')).toBe('inspection');
227
- expect(getCommandCategory('screenshot')).toBe('inspection');
228
- });
229
-
230
- it('returns null for unknown commands', () => {
231
- expect(getCommandCategory('unknown')).toBeNull();
232
- });
233
- });
234
-
235
- describe('getNextStepSuggestions', () => {
236
- it('suggests snapshot after navigation', () => {
237
- const suggestions = getNextStepSuggestions('goto');
238
- expect(suggestions).toContain('snapshot # See page structure and get element refs');
239
- });
240
-
241
- it('suggests actions after snapshot', () => {
242
- const suggestions = getNextStepSuggestions('snapshot');
243
- expect(suggestions.some((s) => s.includes('click'))).toBe(true);
244
- });
245
-
246
- it('suggests snapshot after click', () => {
247
- const suggestions = getNextStepSuggestions('click');
248
- expect(suggestions.some((s) => s.includes('snapshot'))).toBe(true);
249
- });
250
-
251
- it('returns empty for unknown commands', () => {
252
- const suggestions = getNextStepSuggestions('unknown');
253
- expect(suggestions).toHaveLength(0);
254
- });
255
- });
@@ -1,22 +0,0 @@
1
- /**
2
- * back command - Go back in browser history
3
- */
4
-
5
- import type { CommandHandler } from '../types.js';
6
-
7
- export const backCommand: CommandHandler = {
8
- name: 'back',
9
- description: 'Go back in browser history',
10
- usage: 'back',
11
- examples: ['back'],
12
-
13
- async execute(client) {
14
- const response = await client.back();
15
-
16
- if (response.success) {
17
- return { success: true, message: 'Navigated back' };
18
- }
19
-
20
- return { success: false, error: response.error };
21
- },
22
- };
@@ -1,33 +0,0 @@
1
- /**
2
- * check command - Check a checkbox
3
- */
4
-
5
- import type { CommandHandler } from '../types.js';
6
- import { InvalidArgumentsError } from '../errors.js';
7
-
8
- export const checkCommand: CommandHandler = {
9
- name: 'check',
10
- description: 'Check a checkbox',
11
- usage: 'check <selector>',
12
- examples: ['check @ref:5', 'check #agree'],
13
-
14
- async execute(client, args) {
15
- if (args.length === 0) {
16
- throw new InvalidArgumentsError('Selector required', 'check <selector>');
17
- }
18
-
19
- const selector = args[0];
20
-
21
- const response = await client.execute({
22
- id: `cmd_${Date.now()}`,
23
- action: 'check',
24
- selector,
25
- } as any);
26
-
27
- if (response.success) {
28
- return { success: true, message: `Checked: ${selector}` };
29
- }
30
-
31
- return { success: false, error: response.error };
32
- },
33
- };
@@ -1,33 +0,0 @@
1
- /**
2
- * clear command - Clear an input field
3
- */
4
-
5
- import type { CommandHandler } from '../types.js';
6
- import { InvalidArgumentsError } from '../errors.js';
7
-
8
- export const clearCommand: CommandHandler = {
9
- name: 'clear',
10
- description: 'Clear an input field',
11
- usage: 'clear <selector>',
12
- examples: ['clear @ref:1', 'clear #search'],
13
-
14
- async execute(client, args) {
15
- if (args.length === 0) {
16
- throw new InvalidArgumentsError('Selector required', 'clear <selector>');
17
- }
18
-
19
- const selector = args[0];
20
-
21
- const response = await client.execute({
22
- id: `cmd_${Date.now()}`,
23
- action: 'clear',
24
- selector,
25
- } as any);
26
-
27
- if (response.success) {
28
- return { success: true, message: `Cleared: ${selector}` };
29
- }
30
-
31
- return { success: false, error: response.error };
32
- },
33
- };
@@ -1,32 +0,0 @@
1
- /**
2
- * click command - Click an element
3
- */
4
-
5
- import type { CommandHandler } from '../types.js';
6
- import { InvalidArgumentsError } from '../errors.js';
7
- import { getFlagString } from '../parser.js';
8
-
9
- export const clickCommand: CommandHandler = {
10
- name: 'click',
11
- description: 'Click an element',
12
- usage: 'click <selector> [--button left|right|middle]',
13
- examples: ['click @ref:5', 'click #submit', 'click @ref:3 --button right'],
14
-
15
- async execute(client, args, flags) {
16
- if (args.length === 0) {
17
- throw new InvalidArgumentsError('Selector required', 'click <selector>');
18
- }
19
-
20
- const selector = args[0];
21
- const buttonFlag = getFlagString(flags, 'button');
22
- const button = buttonFlag as 'left' | 'right' | 'middle' | undefined;
23
-
24
- const response = await client.click(selector, { button });
25
-
26
- if (response.success) {
27
- return { success: true, message: `Clicked: ${selector}` };
28
- }
29
-
30
- return { success: false, error: response.error };
31
- },
32
- };
@@ -1,31 +0,0 @@
1
- /**
2
- * closetab command - Close a tab
3
- */
4
-
5
- import type { CommandHandler } from '../types.js';
6
-
7
- export const closetabCommand: CommandHandler = {
8
- name: 'closetab',
9
- description: 'Close a tab (current tab if no ID specified)',
10
- usage: 'closetab [id]',
11
- examples: ['closetab', 'closetab 123'],
12
-
13
- async execute(client, args) {
14
- const tabId = args[0] ? parseInt(args[0], 10) : undefined;
15
-
16
- if (args[0] && isNaN(tabId!)) {
17
- return { success: false, error: 'Invalid tab ID' };
18
- }
19
-
20
- const response = await client.tabClose(tabId);
21
-
22
- if (response.success) {
23
- return {
24
- success: true,
25
- message: tabId ? `Closed tab ${tabId}` : 'Closed current tab',
26
- };
27
- }
28
-
29
- return { success: false, error: response.error };
30
- },
31
- };