btcp-browser-agent 0.1.0 → 0.1.2

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,275 +0,0 @@
1
- /**
2
- * Executor tests
3
- */
4
-
5
- import { describe, it, expect, vi, beforeEach } from 'vitest';
6
- import { executeCommand, executeCommandString } from '../executor.js';
7
- import { parseCommand } from '../parser.js';
8
- import { CommandNotFoundError, ExecutionError, CLIError } from '../errors.js';
9
- import type { CommandClient, ParsedCommand } from '../types.js';
10
-
11
- /**
12
- * Create a mock client for testing
13
- */
14
- function createMockClient(overrides: Partial<CommandClient> = {}): CommandClient {
15
- return {
16
- execute: vi.fn().mockResolvedValue({ id: '1', success: true }),
17
- navigate: vi.fn().mockResolvedValue({ id: '1', success: true }),
18
- back: vi.fn().mockResolvedValue({ id: '1', success: true }),
19
- forward: vi.fn().mockResolvedValue({ id: '1', success: true }),
20
- reload: vi.fn().mockResolvedValue({ id: '1', success: true }),
21
- getUrl: vi.fn().mockResolvedValue('https://example.com'),
22
- getTitle: vi.fn().mockResolvedValue('Example Page'),
23
- snapshot: vi.fn().mockResolvedValue('- button "Submit" [@ref:1]'),
24
- click: vi.fn().mockResolvedValue({ id: '1', success: true }),
25
- type: vi.fn().mockResolvedValue({ id: '1', success: true }),
26
- fill: vi.fn().mockResolvedValue({ id: '1', success: true }),
27
- getText: vi.fn().mockResolvedValue('Hello'),
28
- isVisible: vi.fn().mockResolvedValue(true),
29
- screenshot: vi.fn().mockResolvedValue('data:image/png;base64,ABC123'),
30
- tabNew: vi.fn().mockResolvedValue({ tabId: 123, url: 'about:blank' }),
31
- tabClose: vi.fn().mockResolvedValue({ id: '1', success: true }),
32
- tabSwitch: vi.fn().mockResolvedValue({ id: '1', success: true }),
33
- tabList: vi.fn().mockResolvedValue([
34
- { id: 1, url: 'https://example.com', title: 'Example', active: true, index: 0 },
35
- ]),
36
- ...overrides,
37
- };
38
- }
39
-
40
- describe('executeCommand', () => {
41
- let client: CommandClient;
42
-
43
- beforeEach(() => {
44
- client = createMockClient();
45
- });
46
-
47
- describe('command lookup', () => {
48
- it('executes valid command', async () => {
49
- const command: ParsedCommand = {
50
- name: 'snapshot',
51
- args: [],
52
- flags: {},
53
- raw: 'snapshot',
54
- };
55
- const result = await executeCommand(client, command);
56
- expect(result.success).toBe(true);
57
- expect(client.snapshot).toHaveBeenCalled();
58
- });
59
-
60
- it('throws CommandNotFoundError for unknown command', async () => {
61
- const command: ParsedCommand = {
62
- name: 'unknowncommand',
63
- args: [],
64
- flags: {},
65
- raw: 'unknowncommand',
66
- };
67
- await expect(executeCommand(client, command)).rejects.toThrow(CommandNotFoundError);
68
- });
69
-
70
- it('includes similar commands in CommandNotFoundError', async () => {
71
- const command: ParsedCommand = {
72
- name: 'clck',
73
- args: [],
74
- flags: {},
75
- raw: 'clck',
76
- };
77
- try {
78
- await executeCommand(client, command);
79
- expect.fail('Should have thrown');
80
- } catch (error) {
81
- expect(error).toBeInstanceOf(CommandNotFoundError);
82
- const cmdError = error as CommandNotFoundError;
83
- expect(cmdError.suggestions).toBeDefined();
84
- expect(cmdError.suggestions?.some((s) => s.includes('click'))).toBe(true);
85
- }
86
- });
87
- });
88
-
89
- describe('command execution', () => {
90
- it('passes args to command handler', async () => {
91
- const command: ParsedCommand = {
92
- name: 'goto',
93
- args: ['https://example.com'],
94
- flags: {},
95
- raw: 'goto https://example.com',
96
- };
97
- await executeCommand(client, command);
98
- expect(client.navigate).toHaveBeenCalledWith('https://example.com');
99
- });
100
-
101
- it('passes flags to command handler', async () => {
102
- const command: ParsedCommand = {
103
- name: 'reload',
104
- args: [],
105
- flags: { hard: true },
106
- raw: 'reload --hard',
107
- };
108
- await executeCommand(client, command);
109
- expect(client.reload).toHaveBeenCalledWith({ bypassCache: true });
110
- });
111
-
112
- it('returns command result', async () => {
113
- const command: ParsedCommand = {
114
- name: 'url',
115
- args: [],
116
- flags: {},
117
- raw: 'url',
118
- };
119
- const result = await executeCommand(client, command);
120
- expect(result.success).toBe(true);
121
- expect(result.data).toBe('https://example.com');
122
- });
123
- });
124
-
125
- describe('error handling', () => {
126
- it('adds contextual suggestion on error result', async () => {
127
- client = createMockClient({
128
- click: vi.fn().mockResolvedValue({
129
- id: '1',
130
- success: false,
131
- error: 'Element not found',
132
- }),
133
- });
134
- const command: ParsedCommand = {
135
- name: 'click',
136
- args: ['@ref:5'],
137
- flags: {},
138
- raw: 'click @ref:5',
139
- };
140
- const result = await executeCommand(client, command);
141
- expect(result.success).toBe(false);
142
- expect(result.error).toContain('Element not found');
143
- // Should include contextual suggestion
144
- expect(result.error).toContain('snapshot');
145
- });
146
-
147
- it('re-throws CLIError as-is', async () => {
148
- const cliError = new CLIError('Custom CLI error');
149
- client = createMockClient({
150
- click: vi.fn().mockRejectedValue(cliError),
151
- });
152
- const command: ParsedCommand = {
153
- name: 'click',
154
- args: ['@ref:1'],
155
- flags: {},
156
- raw: 'click @ref:1',
157
- };
158
- await expect(executeCommand(client, command)).rejects.toBe(cliError);
159
- });
160
-
161
- it('wraps unexpected errors in ExecutionError', async () => {
162
- const genericError = new Error('Network failure');
163
- client = createMockClient({
164
- navigate: vi.fn().mockRejectedValue(genericError),
165
- });
166
- const command: ParsedCommand = {
167
- name: 'goto',
168
- args: ['https://example.com'],
169
- flags: {},
170
- raw: 'goto https://example.com',
171
- };
172
- try {
173
- await executeCommand(client, command);
174
- expect.fail('Should have thrown');
175
- } catch (error) {
176
- expect(error).toBeInstanceOf(ExecutionError);
177
- const execError = error as ExecutionError;
178
- expect(execError.message).toContain('Network failure');
179
- expect(execError.command).toBe('goto');
180
- }
181
- });
182
-
183
- it('includes contextual suggestion in wrapped error', async () => {
184
- const genericError = new Error('Failed to navigate');
185
- client = createMockClient({
186
- navigate: vi.fn().mockRejectedValue(genericError),
187
- });
188
- const command: ParsedCommand = {
189
- name: 'goto',
190
- args: ['example.com'],
191
- flags: {},
192
- raw: 'goto example.com',
193
- };
194
- try {
195
- await executeCommand(client, command);
196
- expect.fail('Should have thrown');
197
- } catch (error) {
198
- expect(error).toBeInstanceOf(ExecutionError);
199
- const execError = error as ExecutionError;
200
- // Should include suggestion about URL format
201
- expect(execError.suggestions).toBeDefined();
202
- }
203
- });
204
- });
205
-
206
- describe('result passthrough', () => {
207
- it('returns success result unchanged when no error', async () => {
208
- const command: ParsedCommand = {
209
- name: 'snapshot',
210
- args: [],
211
- flags: {},
212
- raw: 'snapshot',
213
- };
214
- const result = await executeCommand(client, command);
215
- expect(result.success).toBe(true);
216
- expect(result.data).toContain('button');
217
- });
218
-
219
- it('returns error result from command handler', async () => {
220
- client = createMockClient({
221
- click: vi.fn().mockResolvedValue({
222
- id: '1',
223
- success: false,
224
- error: 'Failed',
225
- }),
226
- });
227
- const command: ParsedCommand = {
228
- name: 'click',
229
- args: ['@ref:1'],
230
- flags: {},
231
- raw: 'click @ref:1',
232
- };
233
- const result = await executeCommand(client, command);
234
- // Command handler transforms result, doesn't preserve raw data
235
- expect(result.success).toBe(false);
236
- expect(result.error).toContain('Failed');
237
- });
238
- });
239
- });
240
-
241
- describe('executeCommandString', () => {
242
- let client: CommandClient;
243
-
244
- beforeEach(() => {
245
- client = createMockClient();
246
- });
247
-
248
- it('parses and executes command string', async () => {
249
- const result = await executeCommandString(
250
- client,
251
- 'goto https://example.com',
252
- parseCommand
253
- );
254
- expect(result.success).toBe(true);
255
- expect(client.navigate).toHaveBeenCalledWith('https://example.com');
256
- });
257
-
258
- it('handles parse errors', async () => {
259
- await expect(
260
- executeCommandString(client, '', parseCommand)
261
- ).rejects.toThrow('Empty command');
262
- });
263
-
264
- it('uses custom parser', async () => {
265
- const customParser = vi.fn().mockReturnValue({
266
- name: 'snapshot',
267
- args: [],
268
- flags: {},
269
- raw: 'custom',
270
- });
271
- await executeCommandString(client, 'custom', customParser);
272
- expect(customParser).toHaveBeenCalledWith('custom');
273
- expect(client.snapshot).toHaveBeenCalled();
274
- });
275
- });
@@ -1,260 +0,0 @@
1
- /**
2
- * Formatter tests
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- formatResult,
8
- formatData,
9
- formatHelp,
10
- formatCommandHelp,
11
- formatScreenshot,
12
- formatErrorWithSuggestions,
13
- formatSuccessWithNextSteps,
14
- } from '../formatter.js';
15
-
16
- describe('formatResult', () => {
17
- describe('success results', () => {
18
- it('formats success with message', () => {
19
- const result = formatResult({ success: true, message: 'Navigated to example.com' });
20
- expect(result.type).toBe('success');
21
- expect(result.content).toBe('✓ Navigated to example.com');
22
- });
23
-
24
- it('formats success with data', () => {
25
- const result = formatResult({ success: true, data: 'Some data' });
26
- expect(result.type).toBe('data');
27
- expect(result.content).toBe('Some data');
28
- });
29
-
30
- it('formats success without message or data', () => {
31
- const result = formatResult({ success: true });
32
- expect(result.type).toBe('success');
33
- expect(result.content).toBe('✓ Done');
34
- });
35
-
36
- it('prefers message over data', () => {
37
- const result = formatResult({ success: true, message: 'Done!', data: 'ignored' });
38
- expect(result.content).toBe('✓ Done!');
39
- });
40
- });
41
-
42
- describe('error results', () => {
43
- it('formats error with message', () => {
44
- const result = formatResult({ success: false, error: 'Something went wrong' });
45
- expect(result.type).toBe('error');
46
- expect(result.content).toBe('✗ Error: Something went wrong');
47
- });
48
-
49
- it('formats error without message', () => {
50
- const result = formatResult({ success: false });
51
- expect(result.type).toBe('error');
52
- expect(result.content).toBe('✗ Error: Unknown error');
53
- });
54
- });
55
- });
56
-
57
- describe('formatData', () => {
58
- describe('primitive types', () => {
59
- it('formats null', () => {
60
- expect(formatData(null)).toBe('');
61
- });
62
-
63
- it('formats undefined', () => {
64
- expect(formatData(undefined)).toBe('');
65
- });
66
-
67
- it('formats string', () => {
68
- expect(formatData('hello')).toBe('hello');
69
- });
70
-
71
- it('formats number', () => {
72
- expect(formatData(42)).toBe('42');
73
- });
74
-
75
- it('formats boolean', () => {
76
- expect(formatData(true)).toBe('true');
77
- expect(formatData(false)).toBe('false');
78
- });
79
- });
80
-
81
- describe('arrays', () => {
82
- it('formats empty array', () => {
83
- expect(formatData([])).toBe('(empty)');
84
- });
85
-
86
- it('formats array of primitives', () => {
87
- expect(formatData(['a', 'b', 'c'])).toBe('a\nb\nc');
88
- });
89
-
90
- it('formats array of numbers', () => {
91
- expect(formatData([1, 2, 3])).toBe('1\n2\n3');
92
- });
93
-
94
- it('formats array of objects with indices', () => {
95
- const result = formatData([{ name: 'a' }, { name: 'b' }]);
96
- expect(result).toContain('[0]');
97
- expect(result).toContain('[1]');
98
- });
99
- });
100
-
101
- describe('objects', () => {
102
- it('formats empty object', () => {
103
- expect(formatData({})).toBe('{}');
104
- });
105
-
106
- it('formats simple object', () => {
107
- const result = formatData({ name: 'test', value: 42 });
108
- expect(result).toContain('name: test');
109
- expect(result).toContain('value: 42');
110
- });
111
-
112
- // Removed: snapshot now returns string directly, not object with tree field
113
- });
114
- });
115
-
116
- describe('formatHelp', () => {
117
- it('formats command list', () => {
118
- const commands = [
119
- { name: 'goto', description: 'Navigate to URL', usage: 'goto <url>' },
120
- { name: 'click', description: 'Click element', usage: 'click <selector>' },
121
- ];
122
- const result = formatHelp(commands);
123
- expect(result).toContain('goto');
124
- expect(result).toContain('Navigate to URL');
125
- expect(result).toContain('click');
126
- expect(result).toContain('Click element');
127
- });
128
-
129
- it('aligns descriptions', () => {
130
- const commands = [
131
- { name: 'a', description: 'Short', usage: 'a' },
132
- { name: 'longer', description: 'Longer name', usage: 'longer' },
133
- ];
134
- const result = formatHelp(commands);
135
- // Both descriptions should be aligned
136
- const lines = result.split('\n');
137
- expect(lines.length).toBe(2);
138
- });
139
- });
140
-
141
- describe('formatCommandHelp', () => {
142
- it('formats command with all fields', () => {
143
- const command = {
144
- name: 'click',
145
- description: 'Click an element',
146
- usage: 'click <selector>',
147
- examples: ['click @ref:5', 'click #submit'],
148
- };
149
- const result = formatCommandHelp(command);
150
- expect(result).toContain('click');
151
- expect(result).toContain('Click an element');
152
- expect(result).toContain('click <selector>');
153
- expect(result).toContain('click @ref:5');
154
- expect(result).toContain('click #submit');
155
- });
156
-
157
- it('formats command without examples', () => {
158
- const command = {
159
- name: 'back',
160
- description: 'Go back in history',
161
- usage: 'back',
162
- };
163
- const result = formatCommandHelp(command);
164
- expect(result).toContain('back');
165
- expect(result).toContain('Go back in history');
166
- expect(result).not.toContain('Examples');
167
- });
168
-
169
- it('includes selector help for interaction commands', () => {
170
- const command = {
171
- name: 'click',
172
- description: 'Click an element',
173
- usage: 'click <selector>',
174
- };
175
- const result = formatCommandHelp(command);
176
- expect(result).toContain('Selectors');
177
- expect(result).toContain('@ref:N');
178
- expect(result).toContain('#id');
179
- });
180
-
181
- it('excludes selector help for non-interaction commands', () => {
182
- const command = {
183
- name: 'goto',
184
- description: 'Navigate to URL',
185
- usage: 'goto <url>',
186
- };
187
- const result = formatCommandHelp(command);
188
- expect(result).not.toContain('Selectors');
189
- });
190
- });
191
-
192
- describe('formatScreenshot', () => {
193
- it('extracts format and size from PNG data URL', () => {
194
- // Create a small base64 string (~100 bytes)
195
- const base64 = 'A'.repeat(100);
196
- const dataUrl = `data:image/png;base64,${base64}`;
197
- const result = formatScreenshot(dataUrl);
198
- expect(result).toContain('png');
199
- expect(result).toContain('KB');
200
- });
201
-
202
- it('extracts format and size from JPEG data URL', () => {
203
- const base64 = 'A'.repeat(1000);
204
- const dataUrl = `data:image/jpeg;base64,${base64}`;
205
- const result = formatScreenshot(dataUrl);
206
- expect(result).toContain('jpeg');
207
- expect(result).toContain('KB');
208
- });
209
-
210
- it('handles invalid data URL', () => {
211
- const result = formatScreenshot('not-a-data-url');
212
- expect(result).toBe('Screenshot captured');
213
- });
214
- });
215
-
216
- describe('formatErrorWithSuggestions', () => {
217
- it('formats error without suggestions', () => {
218
- const result = formatErrorWithSuggestions('Something failed');
219
- expect(result).toBe('✗ Error: Something failed');
220
- });
221
-
222
- it('formats error with suggestions', () => {
223
- const result = formatErrorWithSuggestions('Element not found', [
224
- 'Run snapshot first',
225
- 'Check selector syntax',
226
- ]);
227
- expect(result).toContain('✗ Error: Element not found');
228
- expect(result).toContain('Suggestions:');
229
- expect(result).toContain('Run snapshot first');
230
- expect(result).toContain('Check selector syntax');
231
- });
232
-
233
- it('formats error with empty suggestions array', () => {
234
- const result = formatErrorWithSuggestions('Error', []);
235
- expect(result).toBe('✗ Error: Error');
236
- });
237
- });
238
-
239
- describe('formatSuccessWithNextSteps', () => {
240
- it('formats success without next steps', () => {
241
- const result = formatSuccessWithNextSteps('Operation completed');
242
- expect(result).toBe('✓ Operation completed');
243
- });
244
-
245
- it('formats success with next steps', () => {
246
- const result = formatSuccessWithNextSteps('Navigated successfully', [
247
- 'snapshot # See page elements',
248
- 'click @ref:N # Interact',
249
- ]);
250
- expect(result).toContain('✓ Navigated successfully');
251
- expect(result).toContain('Next steps:');
252
- expect(result).toContain('snapshot');
253
- expect(result).toContain('click');
254
- });
255
-
256
- it('formats success with empty next steps array', () => {
257
- const result = formatSuccessWithNextSteps('Done', []);
258
- expect(result).toBe('✓ Done');
259
- });
260
- });