ccmanager 2.11.2 → 2.11.4
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/services/__tests__/stateDetector.claude.test.js +312 -0
- package/dist/services/__tests__/stateDetector.cline.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.cline.test.js +115 -0
- package/dist/services/__tests__/stateDetector.codex.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.codex.test.js +77 -0
- package/dist/services/__tests__/stateDetector.cursor.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.cursor.test.js +130 -0
- package/dist/services/__tests__/stateDetector.gemini.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.gemini.test.js +101 -0
- package/dist/services/__tests__/stateDetector.github-copilot.test.d.ts +1 -0
- package/dist/services/__tests__/stateDetector.github-copilot.test.js +55 -0
- package/dist/services/__tests__/testUtils.d.ts +7 -0
- package/dist/services/__tests__/testUtils.js +22 -0
- package/dist/services/stateDetector.js +9 -0
- package/package.json +2 -2
- package/dist/services/stateDetector.test.js +0 -705
- /package/dist/services/{stateDetector.test.d.ts → __tests__/stateDetector.claude.test.d.ts} +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { ClaudeStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('ClaudeStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new ClaudeStateDetector();
|
|
9
|
+
});
|
|
10
|
+
describe('detectState', () => {
|
|
11
|
+
it('should detect waiting_input when "Do you want" prompt is present', () => {
|
|
12
|
+
// Arrange
|
|
13
|
+
terminal = createMockTerminal([
|
|
14
|
+
'Some previous output',
|
|
15
|
+
'│ Do you want to continue? (y/n)',
|
|
16
|
+
'│ > ',
|
|
17
|
+
]);
|
|
18
|
+
// Act
|
|
19
|
+
const state = detector.detectState(terminal, 'idle');
|
|
20
|
+
// Assert
|
|
21
|
+
expect(state).toBe('waiting_input');
|
|
22
|
+
});
|
|
23
|
+
it('should detect waiting_input when "Would you like" prompt is present', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
terminal = createMockTerminal([
|
|
26
|
+
'Some output',
|
|
27
|
+
'│ Would you like to save changes?',
|
|
28
|
+
'│ > ',
|
|
29
|
+
]);
|
|
30
|
+
// Act
|
|
31
|
+
const state = detector.detectState(terminal, 'idle');
|
|
32
|
+
// Assert
|
|
33
|
+
expect(state).toBe('waiting_input');
|
|
34
|
+
});
|
|
35
|
+
it('should detect busy when "ESC to interrupt" is present', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
terminal = createMockTerminal([
|
|
38
|
+
'Processing...',
|
|
39
|
+
'Press ESC to interrupt',
|
|
40
|
+
]);
|
|
41
|
+
// Act
|
|
42
|
+
const state = detector.detectState(terminal, 'idle');
|
|
43
|
+
// Assert
|
|
44
|
+
expect(state).toBe('busy');
|
|
45
|
+
});
|
|
46
|
+
it('should detect busy when "esc to interrupt" is present (case insensitive)', () => {
|
|
47
|
+
// Arrange
|
|
48
|
+
terminal = createMockTerminal([
|
|
49
|
+
'Running command...',
|
|
50
|
+
'press esc to interrupt the process',
|
|
51
|
+
]);
|
|
52
|
+
// Act
|
|
53
|
+
const state = detector.detectState(terminal, 'idle');
|
|
54
|
+
// Assert
|
|
55
|
+
expect(state).toBe('busy');
|
|
56
|
+
});
|
|
57
|
+
it('should detect idle when no specific patterns are found', () => {
|
|
58
|
+
// Arrange
|
|
59
|
+
terminal = createMockTerminal([
|
|
60
|
+
'Command completed successfully',
|
|
61
|
+
'Ready for next command',
|
|
62
|
+
'> ',
|
|
63
|
+
]);
|
|
64
|
+
// Act
|
|
65
|
+
const state = detector.detectState(terminal, 'idle');
|
|
66
|
+
// Assert
|
|
67
|
+
expect(state).toBe('idle');
|
|
68
|
+
});
|
|
69
|
+
it('should handle empty terminal', () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
terminal = createMockTerminal([]);
|
|
72
|
+
// Act
|
|
73
|
+
const state = detector.detectState(terminal, 'idle');
|
|
74
|
+
// Assert
|
|
75
|
+
expect(state).toBe('idle');
|
|
76
|
+
});
|
|
77
|
+
it('should only consider last 30 lines', () => {
|
|
78
|
+
// Arrange
|
|
79
|
+
const lines = [];
|
|
80
|
+
// Add more than 30 lines
|
|
81
|
+
for (let i = 0; i < 40; i++) {
|
|
82
|
+
lines.push(`Line ${i}`);
|
|
83
|
+
}
|
|
84
|
+
// The "Do you want" should be outside the 30 line window
|
|
85
|
+
lines.push('│ Do you want to continue?');
|
|
86
|
+
// Add 30 more lines to push it out
|
|
87
|
+
for (let i = 0; i < 30; i++) {
|
|
88
|
+
lines.push(`Recent line ${i}`);
|
|
89
|
+
}
|
|
90
|
+
terminal = createMockTerminal(lines);
|
|
91
|
+
// Act
|
|
92
|
+
const state = detector.detectState(terminal, 'idle');
|
|
93
|
+
// Assert
|
|
94
|
+
expect(state).toBe('idle'); // Should not detect the old prompt
|
|
95
|
+
});
|
|
96
|
+
it('should prioritize waiting_input over busy state', () => {
|
|
97
|
+
// Arrange
|
|
98
|
+
terminal = createMockTerminal([
|
|
99
|
+
'Press ESC to interrupt',
|
|
100
|
+
'│ Do you want to continue?',
|
|
101
|
+
'│ > ',
|
|
102
|
+
]);
|
|
103
|
+
// Act
|
|
104
|
+
const state = detector.detectState(terminal, 'idle');
|
|
105
|
+
// Assert
|
|
106
|
+
expect(state).toBe('waiting_input'); // waiting_input should take precedence
|
|
107
|
+
});
|
|
108
|
+
it('should maintain current state when "ctrl+r to toggle" is present', () => {
|
|
109
|
+
// Arrange
|
|
110
|
+
terminal = createMockTerminal([
|
|
111
|
+
'Some output',
|
|
112
|
+
'Press Ctrl+R to toggle history search',
|
|
113
|
+
'More output',
|
|
114
|
+
]);
|
|
115
|
+
// Act - test with different current states
|
|
116
|
+
const idleState = detector.detectState(terminal, 'idle');
|
|
117
|
+
const busyState = detector.detectState(terminal, 'busy');
|
|
118
|
+
const waitingState = detector.detectState(terminal, 'waiting_input');
|
|
119
|
+
// Assert - should maintain whatever the current state was
|
|
120
|
+
expect(idleState).toBe('idle');
|
|
121
|
+
expect(busyState).toBe('busy');
|
|
122
|
+
expect(waitingState).toBe('waiting_input');
|
|
123
|
+
});
|
|
124
|
+
it('should maintain current state for various "ctrl+r" patterns', () => {
|
|
125
|
+
// Arrange - test different case variations
|
|
126
|
+
const patterns = [
|
|
127
|
+
'ctrl+r to toggle',
|
|
128
|
+
'CTRL+R TO TOGGLE',
|
|
129
|
+
'Ctrl+R to toggle history',
|
|
130
|
+
'Press ctrl+r to toggle the search',
|
|
131
|
+
];
|
|
132
|
+
for (const pattern of patterns) {
|
|
133
|
+
terminal = createMockTerminal(['Some output', pattern]);
|
|
134
|
+
// Act
|
|
135
|
+
const state = detector.detectState(terminal, 'busy');
|
|
136
|
+
// Assert - should maintain the current state
|
|
137
|
+
expect(state).toBe('busy');
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
it('should detect waiting_input when "Do you want" with options prompt is present', () => {
|
|
141
|
+
// Arrange
|
|
142
|
+
terminal = createMockTerminal([
|
|
143
|
+
'Some previous output',
|
|
144
|
+
'Do you want to make this edit to test.txt?',
|
|
145
|
+
'❯ 1. Yes',
|
|
146
|
+
'2. Yes, allow all edits during this session (shift+tab)',
|
|
147
|
+
'3. No, and tell Claude what to do differently (esc)',
|
|
148
|
+
]);
|
|
149
|
+
// Act
|
|
150
|
+
const state = detector.detectState(terminal, 'idle');
|
|
151
|
+
// Assert
|
|
152
|
+
expect(state).toBe('waiting_input');
|
|
153
|
+
});
|
|
154
|
+
it('should detect waiting_input when "Do you want" with options prompt is present (case insensitive)', () => {
|
|
155
|
+
// Arrange
|
|
156
|
+
terminal = createMockTerminal([
|
|
157
|
+
'Some output',
|
|
158
|
+
'DO YOU WANT to make this edit?',
|
|
159
|
+
'❯ 1. YES',
|
|
160
|
+
'2. NO',
|
|
161
|
+
]);
|
|
162
|
+
// Act
|
|
163
|
+
const state = detector.detectState(terminal, 'idle');
|
|
164
|
+
// Assert
|
|
165
|
+
expect(state).toBe('waiting_input');
|
|
166
|
+
});
|
|
167
|
+
it('should prioritize "Do you want" with options over busy state', () => {
|
|
168
|
+
// Arrange
|
|
169
|
+
terminal = createMockTerminal([
|
|
170
|
+
'Press ESC to interrupt',
|
|
171
|
+
'Do you want to continue?',
|
|
172
|
+
'❯ 1. Yes',
|
|
173
|
+
'2. No',
|
|
174
|
+
]);
|
|
175
|
+
// Act
|
|
176
|
+
const state = detector.detectState(terminal, 'idle');
|
|
177
|
+
// Assert
|
|
178
|
+
expect(state).toBe('waiting_input'); // waiting_input should take precedence
|
|
179
|
+
});
|
|
180
|
+
it('should detect waiting_input with "Would you like" and multiple numbered options', () => {
|
|
181
|
+
// Arrange
|
|
182
|
+
terminal = createMockTerminal([
|
|
183
|
+
'Some previous output',
|
|
184
|
+
'Would you like to proceed?',
|
|
185
|
+
'',
|
|
186
|
+
'❯ 1. Yes, and auto-accept edits',
|
|
187
|
+
' 2. Yes, and manually approve edits',
|
|
188
|
+
' 3. No, keep planning',
|
|
189
|
+
]);
|
|
190
|
+
// Act
|
|
191
|
+
const state = detector.detectState(terminal, 'idle');
|
|
192
|
+
// Assert
|
|
193
|
+
expect(state).toBe('waiting_input');
|
|
194
|
+
});
|
|
195
|
+
it('should detect waiting_input with complex multi-line prompt and cursor indicator', () => {
|
|
196
|
+
// Arrange
|
|
197
|
+
terminal = createMockTerminal([
|
|
198
|
+
'Processing complete.',
|
|
199
|
+
'Would you like to apply these changes?',
|
|
200
|
+
'',
|
|
201
|
+
'❯ 1. Yes, apply all changes',
|
|
202
|
+
' 2. Yes, review changes first',
|
|
203
|
+
' 3. No, discard changes',
|
|
204
|
+
' 4. Cancel operation',
|
|
205
|
+
]);
|
|
206
|
+
// Act
|
|
207
|
+
const state = detector.detectState(terminal, 'idle');
|
|
208
|
+
// Assert
|
|
209
|
+
expect(state).toBe('waiting_input');
|
|
210
|
+
});
|
|
211
|
+
it('should detect waiting_input when cursor indicator is present without explicit "yes" text', () => {
|
|
212
|
+
// Arrange
|
|
213
|
+
terminal = createMockTerminal([
|
|
214
|
+
'Do you want to proceed?',
|
|
215
|
+
'',
|
|
216
|
+
'❯ 1. Apply all',
|
|
217
|
+
' 2. Review first',
|
|
218
|
+
' 3. Skip',
|
|
219
|
+
]);
|
|
220
|
+
// Act
|
|
221
|
+
const state = detector.detectState(terminal, 'idle');
|
|
222
|
+
// Assert
|
|
223
|
+
expect(state).toBe('waiting_input');
|
|
224
|
+
});
|
|
225
|
+
it('should detect waiting_input when "enter to select" is present', () => {
|
|
226
|
+
// Arrange
|
|
227
|
+
terminal = createMockTerminal([
|
|
228
|
+
'Select an option:',
|
|
229
|
+
'',
|
|
230
|
+
'❯ Option 1',
|
|
231
|
+
' Option 2',
|
|
232
|
+
'',
|
|
233
|
+
'Enter to select',
|
|
234
|
+
]);
|
|
235
|
+
// Act
|
|
236
|
+
const state = detector.detectState(terminal, 'idle');
|
|
237
|
+
// Assert
|
|
238
|
+
expect(state).toBe('waiting_input');
|
|
239
|
+
});
|
|
240
|
+
it('should detect waiting_input when "tab/arrow keys to navigate" is present', () => {
|
|
241
|
+
// Arrange
|
|
242
|
+
terminal = createMockTerminal([
|
|
243
|
+
'Choose your action:',
|
|
244
|
+
'',
|
|
245
|
+
'❯ Continue',
|
|
246
|
+
' Skip',
|
|
247
|
+
'',
|
|
248
|
+
'Tab/arrow keys to navigate',
|
|
249
|
+
]);
|
|
250
|
+
// Act
|
|
251
|
+
const state = detector.detectState(terminal, 'idle');
|
|
252
|
+
// Assert
|
|
253
|
+
expect(state).toBe('waiting_input');
|
|
254
|
+
});
|
|
255
|
+
it('should detect waiting_input when "esc to cancel" is present', () => {
|
|
256
|
+
// Arrange
|
|
257
|
+
terminal = createMockTerminal([
|
|
258
|
+
'Interactive selection:',
|
|
259
|
+
'',
|
|
260
|
+
'❯ Yes',
|
|
261
|
+
' No',
|
|
262
|
+
'',
|
|
263
|
+
'Esc to cancel',
|
|
264
|
+
]);
|
|
265
|
+
// Act
|
|
266
|
+
const state = detector.detectState(terminal, 'idle');
|
|
267
|
+
// Assert
|
|
268
|
+
expect(state).toBe('waiting_input');
|
|
269
|
+
});
|
|
270
|
+
it('should detect waiting_input when "ready to submit your answers?" is present', () => {
|
|
271
|
+
// Arrange
|
|
272
|
+
terminal = createMockTerminal([
|
|
273
|
+
'Review your selections:',
|
|
274
|
+
'',
|
|
275
|
+
'Choice 1: Yes',
|
|
276
|
+
'Choice 2: No',
|
|
277
|
+
'',
|
|
278
|
+
'Ready to submit your answers?',
|
|
279
|
+
]);
|
|
280
|
+
// Act
|
|
281
|
+
const state = detector.detectState(terminal, 'idle');
|
|
282
|
+
// Assert
|
|
283
|
+
expect(state).toBe('waiting_input');
|
|
284
|
+
});
|
|
285
|
+
it('should detect waiting_input with mixed case interactive patterns', () => {
|
|
286
|
+
// Arrange
|
|
287
|
+
terminal = createMockTerminal([
|
|
288
|
+
'Select options:',
|
|
289
|
+
'',
|
|
290
|
+
'ENTER TO SELECT',
|
|
291
|
+
'TAB/ARROW KEYS TO NAVIGATE',
|
|
292
|
+
]);
|
|
293
|
+
// Act
|
|
294
|
+
const state = detector.detectState(terminal, 'idle');
|
|
295
|
+
// Assert
|
|
296
|
+
expect(state).toBe('waiting_input');
|
|
297
|
+
});
|
|
298
|
+
it('should prioritize interactive patterns over busy state', () => {
|
|
299
|
+
// Arrange
|
|
300
|
+
terminal = createMockTerminal([
|
|
301
|
+
'Press ESC to interrupt',
|
|
302
|
+
'',
|
|
303
|
+
'Select an option:',
|
|
304
|
+
'Enter to select',
|
|
305
|
+
]);
|
|
306
|
+
// Act
|
|
307
|
+
const state = detector.detectState(terminal, 'idle');
|
|
308
|
+
// Assert
|
|
309
|
+
expect(state).toBe('waiting_input'); // Interactive pattern should take precedence
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { ClineStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('ClineStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new ClineStateDetector();
|
|
9
|
+
});
|
|
10
|
+
it('should detect waiting_input when "Let Cline use this tool?" is present', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
terminal = createMockTerminal([
|
|
13
|
+
'┃ [act mode] Let Cline use this tool?',
|
|
14
|
+
'┃ > Yes',
|
|
15
|
+
"┃ Yes, and don't ask again for this task",
|
|
16
|
+
'┃ No, with feedback',
|
|
17
|
+
]);
|
|
18
|
+
// Act
|
|
19
|
+
const state = detector.detectState(terminal, 'idle');
|
|
20
|
+
// Assert
|
|
21
|
+
expect(state).toBe('waiting_input');
|
|
22
|
+
});
|
|
23
|
+
it('should detect waiting_input when "let cline use this tool?" is present (case insensitive)', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
terminal = createMockTerminal([
|
|
26
|
+
'Some output',
|
|
27
|
+
'LET CLINE USE THIS TOOL?',
|
|
28
|
+
'> Yes',
|
|
29
|
+
]);
|
|
30
|
+
// Act
|
|
31
|
+
const state = detector.detectState(terminal, 'idle');
|
|
32
|
+
// Assert
|
|
33
|
+
expect(state).toBe('waiting_input');
|
|
34
|
+
});
|
|
35
|
+
it('should detect idle when "Cline is ready for your message" is present in act mode', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
terminal = createMockTerminal([
|
|
38
|
+
'┃ [act mode] Cline is ready for your message...',
|
|
39
|
+
'┃ /plan or /act to switch modes',
|
|
40
|
+
'┃ ctrl+e to open editor',
|
|
41
|
+
]);
|
|
42
|
+
// Act
|
|
43
|
+
const state = detector.detectState(terminal, 'idle');
|
|
44
|
+
// Assert
|
|
45
|
+
expect(state).toBe('idle');
|
|
46
|
+
});
|
|
47
|
+
it('should detect idle when "Cline is ready for your message" is present in plan mode', () => {
|
|
48
|
+
// Arrange
|
|
49
|
+
terminal = createMockTerminal([
|
|
50
|
+
'┃ [plan mode] Cline is ready for your message...',
|
|
51
|
+
'┃ /plan or /act to switch modes',
|
|
52
|
+
'┃ ctrl+e to open editor',
|
|
53
|
+
]);
|
|
54
|
+
// Act
|
|
55
|
+
const state = detector.detectState(terminal, 'idle');
|
|
56
|
+
// Assert
|
|
57
|
+
expect(state).toBe('idle');
|
|
58
|
+
});
|
|
59
|
+
it('should detect idle when "cline is ready" is present (case insensitive)', () => {
|
|
60
|
+
// Arrange
|
|
61
|
+
terminal = createMockTerminal([
|
|
62
|
+
'Some output',
|
|
63
|
+
'CLINE IS READY FOR YOUR MESSAGE',
|
|
64
|
+
'Ready to go',
|
|
65
|
+
]);
|
|
66
|
+
// Act
|
|
67
|
+
const state = detector.detectState(terminal, 'idle');
|
|
68
|
+
// Assert
|
|
69
|
+
expect(state).toBe('idle');
|
|
70
|
+
});
|
|
71
|
+
it('should detect busy when no specific patterns are found', () => {
|
|
72
|
+
// Arrange
|
|
73
|
+
terminal = createMockTerminal([
|
|
74
|
+
'Processing your request...',
|
|
75
|
+
'Running analysis...',
|
|
76
|
+
'Working on it...',
|
|
77
|
+
]);
|
|
78
|
+
// Act
|
|
79
|
+
const state = detector.detectState(terminal, 'idle');
|
|
80
|
+
// Assert
|
|
81
|
+
expect(state).toBe('busy');
|
|
82
|
+
});
|
|
83
|
+
it('should handle empty terminal as busy', () => {
|
|
84
|
+
// Arrange
|
|
85
|
+
terminal = createMockTerminal([]);
|
|
86
|
+
// Act
|
|
87
|
+
const state = detector.detectState(terminal, 'idle');
|
|
88
|
+
// Assert
|
|
89
|
+
expect(state).toBe('busy');
|
|
90
|
+
});
|
|
91
|
+
it('should prioritize waiting_input over idle', () => {
|
|
92
|
+
// Arrange
|
|
93
|
+
terminal = createMockTerminal([
|
|
94
|
+
'┃ [act mode] Cline is ready for your message...',
|
|
95
|
+
'┃ Let Cline use this tool?',
|
|
96
|
+
'┃ > Yes',
|
|
97
|
+
]);
|
|
98
|
+
// Act
|
|
99
|
+
const state = detector.detectState(terminal, 'idle');
|
|
100
|
+
// Assert
|
|
101
|
+
expect(state).toBe('waiting_input'); // waiting_input should take precedence
|
|
102
|
+
});
|
|
103
|
+
it('should prioritize idle over busy', () => {
|
|
104
|
+
// Arrange
|
|
105
|
+
terminal = createMockTerminal([
|
|
106
|
+
'Processing...',
|
|
107
|
+
'Working...',
|
|
108
|
+
'┃ [act mode] Cline is ready for your message...',
|
|
109
|
+
]);
|
|
110
|
+
// Act
|
|
111
|
+
const state = detector.detectState(terminal, 'idle');
|
|
112
|
+
// Assert
|
|
113
|
+
expect(state).toBe('idle'); // idle should take precedence over busy
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { CodexStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('CodexStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new CodexStateDetector();
|
|
9
|
+
});
|
|
10
|
+
it('should detect waiting_input state for Allow command? pattern', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
terminal = createMockTerminal(['Some output', 'Allow command?', '│ > ']);
|
|
13
|
+
// Act
|
|
14
|
+
const state = detector.detectState(terminal, 'idle');
|
|
15
|
+
// Assert
|
|
16
|
+
expect(state).toBe('waiting_input');
|
|
17
|
+
});
|
|
18
|
+
it('should detect waiting_input state for [y/n] pattern', () => {
|
|
19
|
+
// Arrange
|
|
20
|
+
terminal = createMockTerminal(['Some output', 'Continue? [y/n]', '> ']);
|
|
21
|
+
// Act
|
|
22
|
+
const state = detector.detectState(terminal, 'idle');
|
|
23
|
+
// Assert
|
|
24
|
+
expect(state).toBe('waiting_input');
|
|
25
|
+
});
|
|
26
|
+
it('should detect waiting_input state for yes (y) pattern', () => {
|
|
27
|
+
// Arrange
|
|
28
|
+
terminal = createMockTerminal([
|
|
29
|
+
'Some output',
|
|
30
|
+
'Apply changes? yes (y) / no (n)',
|
|
31
|
+
]);
|
|
32
|
+
// Act
|
|
33
|
+
const state = detector.detectState(terminal, 'idle');
|
|
34
|
+
// Assert
|
|
35
|
+
expect(state).toBe('waiting_input');
|
|
36
|
+
});
|
|
37
|
+
it('should detect busy state for Esc to interrupt pattern', () => {
|
|
38
|
+
// Arrange
|
|
39
|
+
terminal = createMockTerminal([
|
|
40
|
+
'Processing...',
|
|
41
|
+
'Esc to interrupt',
|
|
42
|
+
'Working...',
|
|
43
|
+
]);
|
|
44
|
+
// Act
|
|
45
|
+
const state = detector.detectState(terminal, 'idle');
|
|
46
|
+
// Assert
|
|
47
|
+
expect(state).toBe('busy');
|
|
48
|
+
});
|
|
49
|
+
it('should detect busy state for ESC INTERRUPT (uppercase)', () => {
|
|
50
|
+
// Arrange
|
|
51
|
+
terminal = createMockTerminal([
|
|
52
|
+
'Processing...',
|
|
53
|
+
'PRESS ESC TO INTERRUPT',
|
|
54
|
+
'Working...',
|
|
55
|
+
]);
|
|
56
|
+
// Act
|
|
57
|
+
const state = detector.detectState(terminal, 'idle');
|
|
58
|
+
// Assert
|
|
59
|
+
expect(state).toBe('busy');
|
|
60
|
+
});
|
|
61
|
+
it('should detect idle state when no patterns match', () => {
|
|
62
|
+
// Arrange
|
|
63
|
+
terminal = createMockTerminal(['Normal output', 'Some message', 'Ready']);
|
|
64
|
+
// Act
|
|
65
|
+
const state = detector.detectState(terminal, 'idle');
|
|
66
|
+
// Assert
|
|
67
|
+
expect(state).toBe('idle');
|
|
68
|
+
});
|
|
69
|
+
it('should prioritize waiting_input over busy', () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
terminal = createMockTerminal(['press esc to interrupt', '[y/n]']);
|
|
72
|
+
// Act
|
|
73
|
+
const state = detector.detectState(terminal, 'idle');
|
|
74
|
+
// Assert
|
|
75
|
+
expect(state).toBe('waiting_input');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { CursorStateDetector } from '../stateDetector.js';
|
|
3
|
+
import { createMockTerminal } from './testUtils.js';
|
|
4
|
+
describe('CursorStateDetector', () => {
|
|
5
|
+
let detector;
|
|
6
|
+
let terminal;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
detector = new CursorStateDetector();
|
|
9
|
+
});
|
|
10
|
+
it('should detect waiting_input state for (y) (enter) pattern', () => {
|
|
11
|
+
// Arrange
|
|
12
|
+
terminal = createMockTerminal([
|
|
13
|
+
'Some output',
|
|
14
|
+
'Apply changes? (y) (enter)',
|
|
15
|
+
'> ',
|
|
16
|
+
]);
|
|
17
|
+
// Act
|
|
18
|
+
const state = detector.detectState(terminal, 'idle');
|
|
19
|
+
// Assert
|
|
20
|
+
expect(state).toBe('waiting_input');
|
|
21
|
+
});
|
|
22
|
+
it('should detect waiting_input state for (Y) (ENTER) pattern (case insensitive)', () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
terminal = createMockTerminal([
|
|
25
|
+
'Some output',
|
|
26
|
+
'Continue? (Y) (ENTER)',
|
|
27
|
+
'> ',
|
|
28
|
+
]);
|
|
29
|
+
// Act
|
|
30
|
+
const state = detector.detectState(terminal, 'idle');
|
|
31
|
+
// Assert
|
|
32
|
+
expect(state).toBe('waiting_input');
|
|
33
|
+
});
|
|
34
|
+
it('should detect waiting_input state for Keep (n) pattern', () => {
|
|
35
|
+
// Arrange
|
|
36
|
+
terminal = createMockTerminal([
|
|
37
|
+
'Changes detected',
|
|
38
|
+
'Keep (n) or replace?',
|
|
39
|
+
'> ',
|
|
40
|
+
]);
|
|
41
|
+
// Act
|
|
42
|
+
const state = detector.detectState(terminal, 'idle');
|
|
43
|
+
// Assert
|
|
44
|
+
expect(state).toBe('waiting_input');
|
|
45
|
+
});
|
|
46
|
+
it('should detect waiting_input state for KEEP (N) pattern (case insensitive)', () => {
|
|
47
|
+
// Arrange
|
|
48
|
+
terminal = createMockTerminal([
|
|
49
|
+
'Some output',
|
|
50
|
+
'KEEP (N) current version?',
|
|
51
|
+
'> ',
|
|
52
|
+
]);
|
|
53
|
+
// Act
|
|
54
|
+
const state = detector.detectState(terminal, 'idle');
|
|
55
|
+
// Assert
|
|
56
|
+
expect(state).toBe('waiting_input');
|
|
57
|
+
});
|
|
58
|
+
it('should detect waiting_input state for Auto pattern with shift+tab', () => {
|
|
59
|
+
// Arrange
|
|
60
|
+
terminal = createMockTerminal([
|
|
61
|
+
'Some output',
|
|
62
|
+
'Auto apply changes (shift+tab)',
|
|
63
|
+
'> ',
|
|
64
|
+
]);
|
|
65
|
+
// Act
|
|
66
|
+
const state = detector.detectState(terminal, 'idle');
|
|
67
|
+
// Assert
|
|
68
|
+
expect(state).toBe('waiting_input');
|
|
69
|
+
});
|
|
70
|
+
it('should detect waiting_input state for AUTO with SHIFT+TAB (case insensitive)', () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
terminal = createMockTerminal([
|
|
73
|
+
'Some output',
|
|
74
|
+
'AUTO COMPLETE (SHIFT+TAB)',
|
|
75
|
+
'> ',
|
|
76
|
+
]);
|
|
77
|
+
// Act
|
|
78
|
+
const state = detector.detectState(terminal, 'idle');
|
|
79
|
+
// Assert
|
|
80
|
+
expect(state).toBe('waiting_input');
|
|
81
|
+
});
|
|
82
|
+
it('should detect busy state for ctrl+c to stop pattern', () => {
|
|
83
|
+
// Arrange
|
|
84
|
+
terminal = createMockTerminal([
|
|
85
|
+
'Processing...',
|
|
86
|
+
'Press ctrl+c to stop',
|
|
87
|
+
'Working...',
|
|
88
|
+
]);
|
|
89
|
+
// Act
|
|
90
|
+
const state = detector.detectState(terminal, 'idle');
|
|
91
|
+
// Assert
|
|
92
|
+
expect(state).toBe('busy');
|
|
93
|
+
});
|
|
94
|
+
it('should detect busy state for CTRL+C TO STOP (case insensitive)', () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
terminal = createMockTerminal([
|
|
97
|
+
'Running...',
|
|
98
|
+
'PRESS CTRL+C TO STOP',
|
|
99
|
+
'Processing...',
|
|
100
|
+
]);
|
|
101
|
+
// Act
|
|
102
|
+
const state = detector.detectState(terminal, 'idle');
|
|
103
|
+
// Assert
|
|
104
|
+
expect(state).toBe('busy');
|
|
105
|
+
});
|
|
106
|
+
it('should detect idle state when no patterns match', () => {
|
|
107
|
+
// Arrange
|
|
108
|
+
terminal = createMockTerminal(['Normal output', 'Some message', 'Ready']);
|
|
109
|
+
// Act
|
|
110
|
+
const state = detector.detectState(terminal, 'idle');
|
|
111
|
+
// Assert
|
|
112
|
+
expect(state).toBe('idle');
|
|
113
|
+
});
|
|
114
|
+
it('should prioritize waiting_input over busy (Priority 1)', () => {
|
|
115
|
+
// Arrange
|
|
116
|
+
terminal = createMockTerminal(['ctrl+c to stop', '(y) (enter)']);
|
|
117
|
+
// Act
|
|
118
|
+
const state = detector.detectState(terminal, 'idle');
|
|
119
|
+
// Assert
|
|
120
|
+
expect(state).toBe('waiting_input'); // waiting_input should take precedence
|
|
121
|
+
});
|
|
122
|
+
it('should handle empty terminal', () => {
|
|
123
|
+
// Arrange
|
|
124
|
+
terminal = createMockTerminal([]);
|
|
125
|
+
// Act
|
|
126
|
+
const state = detector.detectState(terminal, 'idle');
|
|
127
|
+
// Assert
|
|
128
|
+
expect(state).toBe('idle');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|