@nanocollective/nanocoder 1.13.7 → 1.13.9
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/app.js +6 -6
- package/dist/app.js.map +1 -1
- package/dist/components/status.d.ts.map +1 -1
- package/dist/components/status.js +8 -4
- package/dist/components/status.js.map +1 -1
- package/dist/components/user-input.d.ts.map +1 -1
- package/dist/components/user-input.js +35 -31
- package/dist/components/user-input.js.map +1 -1
- package/dist/components/user-message.d.ts.map +1 -1
- package/dist/components/user-message.js +0 -5
- package/dist/components/user-message.js.map +1 -1
- package/dist/components/welcome-message.d.ts.map +1 -1
- package/dist/components/welcome-message.js +9 -3
- package/dist/components/welcome-message.js.map +1 -1
- package/dist/hooks/__tests__/test-helpers.d.ts +5 -0
- package/dist/hooks/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/hooks/__tests__/test-helpers.js +32 -0
- package/dist/hooks/__tests__/test-helpers.js.map +1 -0
- package/dist/hooks/__tests__/useInputState.deletion.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useInputState.deletion.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useInputState.deletion.spec.js +328 -0
- package/dist/hooks/__tests__/useInputState.deletion.spec.js.map +1 -0
- package/dist/hooks/__tests__/useInputState.state-management.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useInputState.state-management.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useInputState.state-management.spec.js +367 -0
- package/dist/hooks/__tests__/useInputState.state-management.spec.js.map +1 -0
- package/dist/hooks/__tests__/useInputState.undo-redo.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useInputState.undo-redo.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useInputState.undo-redo.spec.js +285 -0
- package/dist/hooks/__tests__/useInputState.undo-redo.spec.js.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.cancellation.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useToolHandler.cancellation.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.cancellation.spec.js +227 -0
- package/dist/hooks/__tests__/useToolHandler.cancellation.spec.js.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.execution.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useToolHandler.execution.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.execution.spec.js +264 -0
- package/dist/hooks/__tests__/useToolHandler.execution.spec.js.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.validation.spec.d.ts +2 -0
- package/dist/hooks/__tests__/useToolHandler.validation.spec.d.ts.map +1 -0
- package/dist/hooks/__tests__/useToolHandler.validation.spec.js +149 -0
- package/dist/hooks/__tests__/useToolHandler.validation.spec.js.map +1 -0
- package/dist/{app/hooks → hooks}/useAppInitialization.d.ts +5 -5
- package/dist/hooks/useAppInitialization.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useAppInitialization.js +15 -15
- package/dist/hooks/useAppInitialization.js.map +1 -0
- package/dist/{app/hooks → hooks}/useAppState.d.ts +6 -6
- package/dist/hooks/useAppState.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useAppState.js +2 -2
- package/dist/hooks/useAppState.js.map +1 -0
- package/dist/{app/hooks → hooks}/useChatHandler.d.ts +2 -2
- package/dist/hooks/useChatHandler.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useChatHandler.js +17 -11
- package/dist/hooks/useChatHandler.js.map +1 -0
- package/dist/hooks/useDirectoryTrust.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useDirectoryTrust.js +3 -3
- package/dist/hooks/useDirectoryTrust.js.map +1 -0
- package/dist/hooks/useInputState.d.ts +10 -3
- package/dist/hooks/useInputState.d.ts.map +1 -1
- package/dist/hooks/useInputState.js +154 -15
- package/dist/hooks/useInputState.js.map +1 -1
- package/dist/{app/hooks → hooks}/useModeHandlers.d.ts +2 -2
- package/dist/hooks/useModeHandlers.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useModeHandlers.js +4 -4
- package/dist/hooks/useModeHandlers.js.map +1 -0
- package/dist/hooks/useTerminalWidth.d.ts +15 -0
- package/dist/hooks/useTerminalWidth.d.ts.map +1 -1
- package/dist/hooks/useTerminalWidth.js +39 -0
- package/dist/hooks/useTerminalWidth.js.map +1 -1
- package/dist/{app/hooks → hooks}/useToolHandler.d.ts +2 -2
- package/dist/hooks/useToolHandler.d.ts.map +1 -0
- package/dist/{app/hooks → hooks}/useToolHandler.js +65 -5
- package/dist/hooks/useToolHandler.js.map +1 -0
- package/dist/hooks/useUIState.d.ts +0 -2
- package/dist/hooks/useUIState.d.ts.map +1 -1
- package/dist/hooks/useUIState.js +1 -11
- package/dist/hooks/useUIState.js.map +1 -1
- package/dist/integration/paste-roundtrip.spec.d.ts +2 -0
- package/dist/integration/paste-roundtrip.spec.d.ts.map +1 -0
- package/dist/integration/paste-roundtrip.spec.js +327 -0
- package/dist/integration/paste-roundtrip.spec.js.map +1 -0
- package/dist/prompt-history.d.ts +9 -3
- package/dist/prompt-history.d.ts.map +1 -1
- package/dist/prompt-history.js +56 -14
- package/dist/prompt-history.js.map +1 -1
- package/dist/recommendations/model-database.d.ts.map +1 -1
- package/dist/recommendations/model-database.js +14 -0
- package/dist/recommendations/model-database.js.map +1 -1
- package/dist/types/hooks.d.ts +29 -0
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +10 -1
- package/dist/types/hooks.js.map +1 -1
- package/dist/utils/atomic-deletion.d.ts +17 -0
- package/dist/utils/atomic-deletion.d.ts.map +1 -0
- package/dist/utils/atomic-deletion.js +89 -0
- package/dist/utils/atomic-deletion.js.map +1 -0
- package/dist/utils/atomic-deletion.spec.d.ts +2 -0
- package/dist/utils/atomic-deletion.spec.d.ts.map +1 -0
- package/dist/utils/atomic-deletion.spec.js +153 -0
- package/dist/utils/atomic-deletion.spec.js.map +1 -0
- package/dist/utils/paste-detection.d.ts +36 -0
- package/dist/utils/paste-detection.d.ts.map +1 -0
- package/dist/utils/paste-detection.js +83 -0
- package/dist/utils/paste-detection.js.map +1 -0
- package/dist/utils/paste-detection.spec.d.ts +2 -0
- package/dist/utils/paste-detection.spec.d.ts.map +1 -0
- package/dist/utils/paste-detection.spec.js +44 -0
- package/dist/utils/paste-detection.spec.js.map +1 -0
- package/dist/utils/paste-utils.d.ts +3 -0
- package/dist/utils/paste-utils.d.ts.map +1 -0
- package/dist/utils/paste-utils.js +33 -0
- package/dist/utils/paste-utils.js.map +1 -0
- package/dist/utils/paste-utils.spec.d.ts +2 -0
- package/dist/utils/paste-utils.spec.d.ts.map +1 -0
- package/dist/utils/paste-utils.spec.js +61 -0
- package/dist/utils/paste-utils.spec.js.map +1 -0
- package/dist/utils/prompt-assembly.spec.d.ts +2 -0
- package/dist/utils/prompt-assembly.spec.d.ts.map +1 -0
- package/dist/utils/prompt-assembly.spec.js +80 -0
- package/dist/utils/prompt-assembly.spec.js.map +1 -0
- package/dist/utils/prompt-processor.d.ts +11 -0
- package/dist/utils/prompt-processor.d.ts.map +1 -1
- package/dist/utils/prompt-processor.js +57 -0
- package/dist/utils/prompt-processor.js.map +1 -1
- package/package.json +18 -8
- package/source/app/prompts/main-prompt.md +18 -0
- package/dist/app/hooks/useAppInitialization.d.ts.map +0 -1
- package/dist/app/hooks/useAppInitialization.js.map +0 -1
- package/dist/app/hooks/useAppState.d.ts.map +0 -1
- package/dist/app/hooks/useAppState.js.map +0 -1
- package/dist/app/hooks/useChatHandler.d.ts.map +0 -1
- package/dist/app/hooks/useChatHandler.js.map +0 -1
- package/dist/app/hooks/useDirectoryTrust.d.ts.map +0 -1
- package/dist/app/hooks/useDirectoryTrust.js.map +0 -1
- package/dist/app/hooks/useModeHandlers.d.ts.map +0 -1
- package/dist/app/hooks/useModeHandlers.js.map +0 -1
- package/dist/app/hooks/useToolHandler.d.ts.map +0 -1
- package/dist/app/hooks/useToolHandler.js.map +0 -1
- /package/dist/{app/hooks → hooks}/useDirectoryTrust.d.ts +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { PlaceholderType } from '../../types/hooks.js';
|
|
3
|
+
import { handlePaste } from '../../utils/paste-utils.js';
|
|
4
|
+
import { PasteDetector } from '../../utils/paste-detection.js';
|
|
5
|
+
// State Management Tests
|
|
6
|
+
// Tests for state transitions, reset operations, and state derivations
|
|
7
|
+
test('createEmptyInputState: creates initial empty state', t => {
|
|
8
|
+
const emptyState = {
|
|
9
|
+
displayValue: '',
|
|
10
|
+
placeholderContent: {},
|
|
11
|
+
};
|
|
12
|
+
t.is(emptyState.displayValue, '');
|
|
13
|
+
t.deepEqual(emptyState.placeholderContent, {});
|
|
14
|
+
t.is(Object.keys(emptyState.placeholderContent).length, 0);
|
|
15
|
+
});
|
|
16
|
+
test('createInputStateFromString: converts string to InputState', t => {
|
|
17
|
+
const text = 'some input text';
|
|
18
|
+
const state = {
|
|
19
|
+
displayValue: text,
|
|
20
|
+
placeholderContent: {},
|
|
21
|
+
};
|
|
22
|
+
t.is(state.displayValue, text);
|
|
23
|
+
t.deepEqual(state.placeholderContent, {});
|
|
24
|
+
});
|
|
25
|
+
test('resetInput: clears all state', t => {
|
|
26
|
+
// Simulate state before reset
|
|
27
|
+
const beforeReset = {
|
|
28
|
+
displayValue: '[Paste #123: 500 chars] with text',
|
|
29
|
+
placeholderContent: {
|
|
30
|
+
'123': {
|
|
31
|
+
type: PlaceholderType.PASTE,
|
|
32
|
+
displayText: '[Paste #123: 500 chars]',
|
|
33
|
+
content: 'data',
|
|
34
|
+
originalSize: 500,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
// After reset
|
|
39
|
+
const afterReset = {
|
|
40
|
+
displayValue: '',
|
|
41
|
+
placeholderContent: {},
|
|
42
|
+
};
|
|
43
|
+
t.is(afterReset.displayValue, '');
|
|
44
|
+
t.is(Object.keys(afterReset.placeholderContent).length, 0);
|
|
45
|
+
});
|
|
46
|
+
test('setInputState: updates entire state', t => {
|
|
47
|
+
const oldState = {
|
|
48
|
+
displayValue: 'old',
|
|
49
|
+
placeholderContent: {},
|
|
50
|
+
};
|
|
51
|
+
const newState = {
|
|
52
|
+
displayValue: 'new value',
|
|
53
|
+
placeholderContent: {
|
|
54
|
+
'999': {
|
|
55
|
+
type: PlaceholderType.PASTE,
|
|
56
|
+
displayText: '[Paste #999: 100 chars]',
|
|
57
|
+
content: 'content',
|
|
58
|
+
originalSize: 100,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
// Simulate setInputState
|
|
63
|
+
const currentState = newState;
|
|
64
|
+
t.is(currentState.displayValue, 'new value');
|
|
65
|
+
t.true('999' in currentState.placeholderContent);
|
|
66
|
+
});
|
|
67
|
+
test('setInput (legacy): updates only displayValue', t => {
|
|
68
|
+
const state = {
|
|
69
|
+
displayValue: 'old',
|
|
70
|
+
placeholderContent: {
|
|
71
|
+
'123': {
|
|
72
|
+
type: PlaceholderType.PASTE,
|
|
73
|
+
displayText: '[Paste #123: 100 chars]',
|
|
74
|
+
content: 'preserved',
|
|
75
|
+
originalSize: 100,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
// Simulate legacy setInput
|
|
80
|
+
const updatedState = {
|
|
81
|
+
...state,
|
|
82
|
+
displayValue: 'new display value',
|
|
83
|
+
};
|
|
84
|
+
t.is(updatedState.displayValue, 'new display value');
|
|
85
|
+
t.true('123' in updatedState.placeholderContent, 'Placeholder should be preserved');
|
|
86
|
+
t.is(updatedState.placeholderContent['123'].content, 'preserved');
|
|
87
|
+
});
|
|
88
|
+
test('line count calculation: single line', t => {
|
|
89
|
+
const input = 'single line';
|
|
90
|
+
const lineCount = Math.max(1, input.split(/\r\n|\r|\n/).length);
|
|
91
|
+
t.is(lineCount, 1);
|
|
92
|
+
});
|
|
93
|
+
test('line count calculation: multiple lines', t => {
|
|
94
|
+
const input = 'line 1\nline 2\nline 3';
|
|
95
|
+
const lineCount = Math.max(1, input.split(/\r\n|\r|\n/).length);
|
|
96
|
+
t.is(lineCount, 3);
|
|
97
|
+
});
|
|
98
|
+
test('line count calculation: empty string', t => {
|
|
99
|
+
const input = '';
|
|
100
|
+
const lineCount = Math.max(1, input.split(/\r\n|\r|\n/).length);
|
|
101
|
+
t.is(lineCount, 1); // Minimum of 1
|
|
102
|
+
});
|
|
103
|
+
test('hasLargeContent flag: small content', t => {
|
|
104
|
+
const input = 'small';
|
|
105
|
+
const hasLargeContent = input.length > 150;
|
|
106
|
+
t.false(hasLargeContent);
|
|
107
|
+
});
|
|
108
|
+
test('hasLargeContent flag: large content', t => {
|
|
109
|
+
const input = 'x'.repeat(200);
|
|
110
|
+
const hasLargeContent = input.length > 150;
|
|
111
|
+
t.true(hasLargeContent);
|
|
112
|
+
});
|
|
113
|
+
test('legacy pastedContent: computed from placeholderContent', t => {
|
|
114
|
+
const state = {
|
|
115
|
+
displayValue: '[Paste #1: 100 chars] [Paste #2: 200 chars]',
|
|
116
|
+
placeholderContent: {
|
|
117
|
+
'1': {
|
|
118
|
+
type: PlaceholderType.PASTE,
|
|
119
|
+
displayText: '[Paste #1: 100 chars]',
|
|
120
|
+
content: 'first content',
|
|
121
|
+
originalSize: 100,
|
|
122
|
+
},
|
|
123
|
+
'2': {
|
|
124
|
+
type: PlaceholderType.PASTE,
|
|
125
|
+
displayText: '[Paste #2: 200 chars]',
|
|
126
|
+
content: 'second content',
|
|
127
|
+
originalSize: 200,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
// Simulate legacy computed property
|
|
132
|
+
const legacyPastedContent = {};
|
|
133
|
+
Object.entries(state.placeholderContent).forEach(([id, content]) => {
|
|
134
|
+
if (content.type === PlaceholderType.PASTE) {
|
|
135
|
+
legacyPastedContent[id] = content.content;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
t.is(Object.keys(legacyPastedContent).length, 2);
|
|
139
|
+
t.is(legacyPastedContent['1'], 'first content');
|
|
140
|
+
t.is(legacyPastedContent['2'], 'second content');
|
|
141
|
+
});
|
|
142
|
+
test('legacy pastedContent: empty when no placeholders', t => {
|
|
143
|
+
const state = {
|
|
144
|
+
displayValue: 'no placeholders',
|
|
145
|
+
placeholderContent: {},
|
|
146
|
+
};
|
|
147
|
+
const legacyPastedContent = {};
|
|
148
|
+
Object.entries(state.placeholderContent).forEach(([id, content]) => {
|
|
149
|
+
if (content.type === PlaceholderType.PASTE) {
|
|
150
|
+
legacyPastedContent[id] = content.content;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
t.deepEqual(legacyPastedContent, {});
|
|
154
|
+
});
|
|
155
|
+
test('updateInput: normal typing updates display value', t => {
|
|
156
|
+
const currentState = {
|
|
157
|
+
displayValue: 'hello',
|
|
158
|
+
placeholderContent: {},
|
|
159
|
+
};
|
|
160
|
+
const newInput = 'hello world';
|
|
161
|
+
// Simulate normal typing (no paste detection, no atomic deletion)
|
|
162
|
+
const newState = {
|
|
163
|
+
displayValue: newInput,
|
|
164
|
+
placeholderContent: currentState.placeholderContent,
|
|
165
|
+
};
|
|
166
|
+
t.is(newState.displayValue, 'hello world');
|
|
167
|
+
t.deepEqual(newState.placeholderContent, {});
|
|
168
|
+
});
|
|
169
|
+
test('updateInput: preserves placeholders on normal edits', t => {
|
|
170
|
+
const currentState = {
|
|
171
|
+
displayValue: '[Paste #123: 500 chars] text',
|
|
172
|
+
placeholderContent: {
|
|
173
|
+
'123': {
|
|
174
|
+
type: PlaceholderType.PASTE,
|
|
175
|
+
displayText: '[Paste #123: 500 chars]',
|
|
176
|
+
content: 'data',
|
|
177
|
+
originalSize: 500,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
const newInput = '[Paste #123: 500 chars] text more';
|
|
182
|
+
const newState = {
|
|
183
|
+
displayValue: newInput,
|
|
184
|
+
placeholderContent: currentState.placeholderContent,
|
|
185
|
+
};
|
|
186
|
+
t.is(newState.displayValue, '[Paste #123: 500 chars] text more');
|
|
187
|
+
t.true('123' in newState.placeholderContent);
|
|
188
|
+
});
|
|
189
|
+
test('paste detection: detects large paste and creates placeholder', t => {
|
|
190
|
+
const largePaste = 'a'.repeat(600);
|
|
191
|
+
const currentDisplayValue = '';
|
|
192
|
+
const currentPlaceholderContent = {};
|
|
193
|
+
const result = handlePaste(largePaste, currentDisplayValue, currentPlaceholderContent);
|
|
194
|
+
t.truthy(result);
|
|
195
|
+
t.true(result.displayValue.includes('[Paste #'));
|
|
196
|
+
t.is(Object.keys(result.placeholderContent).length, 1);
|
|
197
|
+
});
|
|
198
|
+
test('paste detection: small paste returns null', t => {
|
|
199
|
+
const smallPaste = 'small';
|
|
200
|
+
const currentDisplayValue = '';
|
|
201
|
+
const currentPlaceholderContent = {};
|
|
202
|
+
const result = handlePaste(smallPaste, currentDisplayValue, currentPlaceholderContent);
|
|
203
|
+
t.is(result, null);
|
|
204
|
+
});
|
|
205
|
+
test('paste detector: state updates on input changes', t => {
|
|
206
|
+
const detector = new PasteDetector();
|
|
207
|
+
// First input
|
|
208
|
+
detector.detectPaste('hello');
|
|
209
|
+
// Update state manually (simulates paste detector state update)
|
|
210
|
+
detector.updateState('hello world');
|
|
211
|
+
// Next detection should calculate diff from 'hello world'
|
|
212
|
+
const result = detector.detectPaste('hello world!');
|
|
213
|
+
t.is(result.addedText, '!');
|
|
214
|
+
});
|
|
215
|
+
test('paste detector: reset clears state', t => {
|
|
216
|
+
const detector = new PasteDetector();
|
|
217
|
+
detector.detectPaste('some text');
|
|
218
|
+
detector.reset();
|
|
219
|
+
const result = detector.detectPaste('new text');
|
|
220
|
+
t.is(result.addedText, 'new text'); // Full text, not diff
|
|
221
|
+
});
|
|
222
|
+
test('integration: complete state lifecycle', t => {
|
|
223
|
+
// 1. Start with empty state
|
|
224
|
+
let state = { displayValue: '', placeholderContent: {} };
|
|
225
|
+
t.is(state.displayValue, '');
|
|
226
|
+
// 2. Add some text
|
|
227
|
+
state = { displayValue: 'analyze: ', placeholderContent: {} };
|
|
228
|
+
t.is(state.displayValue, 'analyze: ');
|
|
229
|
+
// 3. Large paste creates placeholder
|
|
230
|
+
const largePaste = 'code'.repeat(200);
|
|
231
|
+
const pasteResult = handlePaste(largePaste, state.displayValue, state.placeholderContent);
|
|
232
|
+
t.truthy(pasteResult);
|
|
233
|
+
state = pasteResult;
|
|
234
|
+
const placeholderId = Object.keys(state.placeholderContent)[0];
|
|
235
|
+
t.true(state.displayValue.includes('[Paste #'));
|
|
236
|
+
t.is(Object.keys(state.placeholderContent).length, 1);
|
|
237
|
+
// 4. Add more text
|
|
238
|
+
state = { ...state, displayValue: state.displayValue + ' and review' };
|
|
239
|
+
t.true(state.displayValue.includes('and review'));
|
|
240
|
+
t.is(Object.keys(state.placeholderContent).length, 1);
|
|
241
|
+
// 5. Delete placeholder
|
|
242
|
+
const regex = new RegExp(`\\[Paste #${placeholderId}: \\d+ chars\\]`, 'g');
|
|
243
|
+
state = {
|
|
244
|
+
displayValue: state.displayValue.replace(regex, ''),
|
|
245
|
+
placeholderContent: {},
|
|
246
|
+
};
|
|
247
|
+
t.false(state.displayValue.includes('[Paste #'));
|
|
248
|
+
t.is(Object.keys(state.placeholderContent).length, 0);
|
|
249
|
+
// 6. Reset to empty
|
|
250
|
+
state = { displayValue: '', placeholderContent: {} };
|
|
251
|
+
t.is(state.displayValue, '');
|
|
252
|
+
t.is(Object.keys(state.placeholderContent).length, 0);
|
|
253
|
+
});
|
|
254
|
+
test('state immutability: modifications create new objects', t => {
|
|
255
|
+
const originalState = {
|
|
256
|
+
displayValue: 'original',
|
|
257
|
+
placeholderContent: {
|
|
258
|
+
'123': {
|
|
259
|
+
type: PlaceholderType.PASTE,
|
|
260
|
+
displayText: '[Paste #123: 100 chars]',
|
|
261
|
+
content: 'data',
|
|
262
|
+
originalSize: 100,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
// Create new state by spreading
|
|
267
|
+
const newState = {
|
|
268
|
+
...originalState,
|
|
269
|
+
displayValue: 'modified',
|
|
270
|
+
};
|
|
271
|
+
t.is(originalState.displayValue, 'original', 'Original should not change');
|
|
272
|
+
t.is(newState.displayValue, 'modified');
|
|
273
|
+
t.is(originalState.placeholderContent, newState.placeholderContent, 'Same reference is ok if not modified');
|
|
274
|
+
});
|
|
275
|
+
test('state immutability: placeholderContent modifications', t => {
|
|
276
|
+
const originalState = {
|
|
277
|
+
displayValue: 'text',
|
|
278
|
+
placeholderContent: {
|
|
279
|
+
'1': {
|
|
280
|
+
type: PlaceholderType.PASTE,
|
|
281
|
+
displayText: '[Paste #1: 100 chars]',
|
|
282
|
+
content: 'first',
|
|
283
|
+
originalSize: 100,
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
// Create new state with modified placeholderContent
|
|
288
|
+
const newState = {
|
|
289
|
+
...originalState,
|
|
290
|
+
placeholderContent: {
|
|
291
|
+
...originalState.placeholderContent,
|
|
292
|
+
'2': {
|
|
293
|
+
type: PlaceholderType.PASTE,
|
|
294
|
+
displayText: '[Paste #2: 200 chars]',
|
|
295
|
+
content: 'second',
|
|
296
|
+
originalSize: 200,
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
t.is(Object.keys(originalState.placeholderContent).length, 1, 'Original should have 1');
|
|
301
|
+
t.is(Object.keys(newState.placeholderContent).length, 2, 'New should have 2');
|
|
302
|
+
t.true('1' in newState.placeholderContent);
|
|
303
|
+
t.true('2' in newState.placeholderContent);
|
|
304
|
+
});
|
|
305
|
+
test('edge case: very long display value', t => {
|
|
306
|
+
const longText = 'x'.repeat(10000);
|
|
307
|
+
const state = {
|
|
308
|
+
displayValue: longText,
|
|
309
|
+
placeholderContent: {},
|
|
310
|
+
};
|
|
311
|
+
t.is(state.displayValue.length, 10000);
|
|
312
|
+
const lineCount = Math.max(1, state.displayValue.split(/\r\n|\r|\n/).length);
|
|
313
|
+
t.is(lineCount, 1);
|
|
314
|
+
});
|
|
315
|
+
test('edge case: many placeholders', t => {
|
|
316
|
+
const placeholders = {};
|
|
317
|
+
for (let i = 1; i <= 10; i++) {
|
|
318
|
+
placeholders[String(i)] = {
|
|
319
|
+
type: PlaceholderType.PASTE,
|
|
320
|
+
displayText: `[Paste #${i}: ${i * 100} chars]`,
|
|
321
|
+
content: `content${i}`,
|
|
322
|
+
originalSize: i * 100,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const state = {
|
|
326
|
+
displayValue: Object.keys(placeholders)
|
|
327
|
+
.map(id => `[Paste #${id}: ${Number(id) * 100} chars]`)
|
|
328
|
+
.join(' '),
|
|
329
|
+
placeholderContent: placeholders,
|
|
330
|
+
};
|
|
331
|
+
t.is(Object.keys(state.placeholderContent).length, 10);
|
|
332
|
+
// Legacy conversion
|
|
333
|
+
const legacyPastedContent = {};
|
|
334
|
+
Object.entries(state.placeholderContent).forEach(([id, content]) => {
|
|
335
|
+
if (content.type === PlaceholderType.PASTE) {
|
|
336
|
+
legacyPastedContent[id] = content.content;
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
t.is(Object.keys(legacyPastedContent).length, 10);
|
|
340
|
+
});
|
|
341
|
+
test('edge case: Unicode and special characters in display value', t => {
|
|
342
|
+
const state = {
|
|
343
|
+
displayValue: '🎉 Hello 世界 \n\t Special chars: <>[]{}',
|
|
344
|
+
placeholderContent: {},
|
|
345
|
+
};
|
|
346
|
+
t.is(state.displayValue, '🎉 Hello 世界 \n\t Special chars: <>[]{}');
|
|
347
|
+
const lineCount = Math.max(1, state.displayValue.split(/\r\n|\r|\n/).length);
|
|
348
|
+
t.is(lineCount, 2); // Has a newline
|
|
349
|
+
});
|
|
350
|
+
test('edge case: placeholder content with newlines', t => {
|
|
351
|
+
const content = 'line1\nline2\nline3';
|
|
352
|
+
const state = {
|
|
353
|
+
displayValue: '[Paste #777: 50 chars]',
|
|
354
|
+
placeholderContent: {
|
|
355
|
+
'777': {
|
|
356
|
+
type: PlaceholderType.PASTE,
|
|
357
|
+
displayText: '[Paste #777: 50 chars]',
|
|
358
|
+
content: content,
|
|
359
|
+
originalSize: 50,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
const placeholder = state.placeholderContent['777'];
|
|
364
|
+
t.true(placeholder.content.includes('\n'));
|
|
365
|
+
t.is(placeholder.content.split('\n').length, 3);
|
|
366
|
+
});
|
|
367
|
+
//# sourceMappingURL=useInputState.state-management.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInputState.state-management.spec.js","sourceRoot":"","sources":["../../../source/hooks/__tests__/useInputState.state-management.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInputState.undo-redo.spec.d.ts","sourceRoot":"","sources":["../../../source/hooks/__tests__/useInputState.undo-redo.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { PlaceholderType } from '../../types/hooks.js';
|
|
3
|
+
// Undo/Redo Stack Logic Tests
|
|
4
|
+
// These tests verify the undo/redo stack behavior that the hook implements
|
|
5
|
+
// Helper to simulate pushing to undo stack
|
|
6
|
+
function pushToUndoStack(undoStack, redoStack, currentState, newState) {
|
|
7
|
+
return {
|
|
8
|
+
newUndoStack: [...undoStack, currentState],
|
|
9
|
+
newRedoStack: [], // Clear redo stack on new action
|
|
10
|
+
newCurrentState: newState,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
// Helper to simulate undo
|
|
14
|
+
function performUndo(undoStack, redoStack, currentState) {
|
|
15
|
+
if (undoStack.length === 0) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const previousState = undoStack[undoStack.length - 1];
|
|
19
|
+
const newUndoStack = undoStack.slice(0, -1);
|
|
20
|
+
return {
|
|
21
|
+
newUndoStack,
|
|
22
|
+
newRedoStack: [...redoStack, currentState],
|
|
23
|
+
newCurrentState: previousState,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// Helper to simulate redo
|
|
27
|
+
function performRedo(undoStack, redoStack, currentState) {
|
|
28
|
+
if (redoStack.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const nextState = redoStack[redoStack.length - 1];
|
|
32
|
+
const newRedoStack = redoStack.slice(0, -1);
|
|
33
|
+
return {
|
|
34
|
+
newUndoStack: [...undoStack, currentState],
|
|
35
|
+
newRedoStack,
|
|
36
|
+
newCurrentState: nextState,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
test('undo stack: pushing new state adds current to undo stack', t => {
|
|
40
|
+
const undoStack = [];
|
|
41
|
+
const redoStack = [];
|
|
42
|
+
const currentState = {
|
|
43
|
+
displayValue: 'first',
|
|
44
|
+
placeholderContent: {},
|
|
45
|
+
};
|
|
46
|
+
const newState = { displayValue: 'second', placeholderContent: {} };
|
|
47
|
+
const result = pushToUndoStack(undoStack, redoStack, currentState, newState);
|
|
48
|
+
t.is(result.newUndoStack.length, 1);
|
|
49
|
+
t.is(result.newUndoStack[0].displayValue, 'first');
|
|
50
|
+
t.is(result.newCurrentState.displayValue, 'second');
|
|
51
|
+
t.is(result.newRedoStack.length, 0); // Redo stack cleared
|
|
52
|
+
});
|
|
53
|
+
test('undo stack: new change clears redo stack', t => {
|
|
54
|
+
const undoStack = [
|
|
55
|
+
{ displayValue: 'old', placeholderContent: {} },
|
|
56
|
+
];
|
|
57
|
+
const redoStack = [
|
|
58
|
+
{ displayValue: 'future1', placeholderContent: {} },
|
|
59
|
+
{ displayValue: 'future2', placeholderContent: {} },
|
|
60
|
+
];
|
|
61
|
+
const currentState = {
|
|
62
|
+
displayValue: 'current',
|
|
63
|
+
placeholderContent: {},
|
|
64
|
+
};
|
|
65
|
+
const newState = { displayValue: 'new', placeholderContent: {} };
|
|
66
|
+
const result = pushToUndoStack(undoStack, redoStack, currentState, newState);
|
|
67
|
+
t.is(result.newRedoStack.length, 0, 'Redo stack should be cleared');
|
|
68
|
+
});
|
|
69
|
+
test('undo: returns null when undo stack is empty', t => {
|
|
70
|
+
const undoStack = [];
|
|
71
|
+
const redoStack = [];
|
|
72
|
+
const currentState = {
|
|
73
|
+
displayValue: 'current',
|
|
74
|
+
placeholderContent: {},
|
|
75
|
+
};
|
|
76
|
+
const result = performUndo(undoStack, redoStack, currentState);
|
|
77
|
+
t.is(result, null);
|
|
78
|
+
});
|
|
79
|
+
test('undo: pops from undo stack and pushes current to redo stack', t => {
|
|
80
|
+
const state1 = { displayValue: 'first', placeholderContent: {} };
|
|
81
|
+
const state2 = { displayValue: 'second', placeholderContent: {} };
|
|
82
|
+
const currentState = {
|
|
83
|
+
displayValue: 'third',
|
|
84
|
+
placeholderContent: {},
|
|
85
|
+
};
|
|
86
|
+
const undoStack = [state1, state2];
|
|
87
|
+
const redoStack = [];
|
|
88
|
+
const result = performUndo(undoStack, redoStack, currentState);
|
|
89
|
+
t.truthy(result);
|
|
90
|
+
t.is(result.newUndoStack.length, 1);
|
|
91
|
+
t.is(result.newUndoStack[0].displayValue, 'first');
|
|
92
|
+
t.is(result.newCurrentState.displayValue, 'second');
|
|
93
|
+
t.is(result.newRedoStack.length, 1);
|
|
94
|
+
t.is(result.newRedoStack[0].displayValue, 'third');
|
|
95
|
+
});
|
|
96
|
+
test('redo: returns null when redo stack is empty', t => {
|
|
97
|
+
const undoStack = [
|
|
98
|
+
{ displayValue: 'old', placeholderContent: {} },
|
|
99
|
+
];
|
|
100
|
+
const redoStack = [];
|
|
101
|
+
const currentState = {
|
|
102
|
+
displayValue: 'current',
|
|
103
|
+
placeholderContent: {},
|
|
104
|
+
};
|
|
105
|
+
const result = performRedo(undoStack, redoStack, currentState);
|
|
106
|
+
t.is(result, null);
|
|
107
|
+
});
|
|
108
|
+
test('redo: pops from redo stack and pushes current to undo stack', t => {
|
|
109
|
+
const currentState = {
|
|
110
|
+
displayValue: 'current',
|
|
111
|
+
placeholderContent: {},
|
|
112
|
+
};
|
|
113
|
+
const nextState = { displayValue: 'next', placeholderContent: {} };
|
|
114
|
+
const undoStack = [
|
|
115
|
+
{ displayValue: 'old', placeholderContent: {} },
|
|
116
|
+
];
|
|
117
|
+
const redoStack = [nextState];
|
|
118
|
+
const result = performRedo(undoStack, redoStack, currentState);
|
|
119
|
+
t.truthy(result);
|
|
120
|
+
t.is(result.newRedoStack.length, 0);
|
|
121
|
+
t.is(result.newCurrentState.displayValue, 'next');
|
|
122
|
+
t.is(result.newUndoStack.length, 2);
|
|
123
|
+
t.is(result.newUndoStack[1].displayValue, 'current');
|
|
124
|
+
});
|
|
125
|
+
test('undo preserves placeholder content', t => {
|
|
126
|
+
const stateWithPlaceholder = {
|
|
127
|
+
displayValue: '[Paste #123: 100 chars]',
|
|
128
|
+
placeholderContent: {
|
|
129
|
+
'123': {
|
|
130
|
+
type: PlaceholderType.PASTE,
|
|
131
|
+
displayText: '[Paste #123: 100 chars]',
|
|
132
|
+
content: 'large content here',
|
|
133
|
+
originalSize: 100,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const currentState = {
|
|
138
|
+
displayValue: 'changed',
|
|
139
|
+
placeholderContent: {},
|
|
140
|
+
};
|
|
141
|
+
const undoStack = [stateWithPlaceholder];
|
|
142
|
+
const redoStack = [];
|
|
143
|
+
const result = performUndo(undoStack, redoStack, currentState);
|
|
144
|
+
t.truthy(result);
|
|
145
|
+
t.is(Object.keys(result.newCurrentState.placeholderContent).length, 1);
|
|
146
|
+
t.true('123' in result.newCurrentState.placeholderContent);
|
|
147
|
+
});
|
|
148
|
+
test('redo preserves placeholder content', t => {
|
|
149
|
+
const stateWithPlaceholder = {
|
|
150
|
+
displayValue: '[Paste #456: 200 chars]',
|
|
151
|
+
placeholderContent: {
|
|
152
|
+
'456': {
|
|
153
|
+
type: PlaceholderType.PASTE,
|
|
154
|
+
displayText: '[Paste #456: 200 chars]',
|
|
155
|
+
content: 'restored content',
|
|
156
|
+
originalSize: 200,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const currentState = {
|
|
161
|
+
displayValue: 'current',
|
|
162
|
+
placeholderContent: {},
|
|
163
|
+
};
|
|
164
|
+
const undoStack = [];
|
|
165
|
+
const redoStack = [stateWithPlaceholder];
|
|
166
|
+
const result = performRedo(undoStack, redoStack, currentState);
|
|
167
|
+
t.truthy(result);
|
|
168
|
+
t.is(Object.keys(result.newCurrentState.placeholderContent).length, 1);
|
|
169
|
+
t.true('456' in result.newCurrentState.placeholderContent);
|
|
170
|
+
});
|
|
171
|
+
test('integration: multiple undo/redo operations', t => {
|
|
172
|
+
const states = [
|
|
173
|
+
{ displayValue: 'state1', placeholderContent: {} },
|
|
174
|
+
{ displayValue: 'state2', placeholderContent: {} },
|
|
175
|
+
{ displayValue: 'state3', placeholderContent: {} },
|
|
176
|
+
];
|
|
177
|
+
// Start at state3 with state1 and state2 in undo stack
|
|
178
|
+
let undoStack = [states[0], states[1]];
|
|
179
|
+
let redoStack = [];
|
|
180
|
+
let currentState = states[2];
|
|
181
|
+
// Undo to state2
|
|
182
|
+
let result = performUndo(undoStack, redoStack, currentState);
|
|
183
|
+
t.truthy(result);
|
|
184
|
+
undoStack = result.newUndoStack;
|
|
185
|
+
redoStack = result.newRedoStack;
|
|
186
|
+
currentState = result.newCurrentState;
|
|
187
|
+
t.is(currentState.displayValue, 'state2');
|
|
188
|
+
// Undo to state1
|
|
189
|
+
result = performUndo(undoStack, redoStack, currentState);
|
|
190
|
+
t.truthy(result);
|
|
191
|
+
undoStack = result.newUndoStack;
|
|
192
|
+
redoStack = result.newRedoStack;
|
|
193
|
+
currentState = result.newCurrentState;
|
|
194
|
+
t.is(currentState.displayValue, 'state1');
|
|
195
|
+
// Redo to state2
|
|
196
|
+
result = performRedo(undoStack, redoStack, currentState);
|
|
197
|
+
t.truthy(result);
|
|
198
|
+
undoStack = result.newUndoStack;
|
|
199
|
+
redoStack = result.newRedoStack;
|
|
200
|
+
currentState = result.newCurrentState;
|
|
201
|
+
t.is(currentState.displayValue, 'state2');
|
|
202
|
+
// Redo to state3
|
|
203
|
+
result = performRedo(undoStack, redoStack, currentState);
|
|
204
|
+
t.truthy(result);
|
|
205
|
+
currentState = result.newCurrentState;
|
|
206
|
+
t.is(currentState.displayValue, 'state3');
|
|
207
|
+
});
|
|
208
|
+
test('integration: new change after undo clears redo path', t => {
|
|
209
|
+
const state1 = { displayValue: 'first', placeholderContent: {} };
|
|
210
|
+
const state2 = { displayValue: 'second', placeholderContent: {} };
|
|
211
|
+
const state3 = { displayValue: 'third', placeholderContent: {} };
|
|
212
|
+
// Start at state3
|
|
213
|
+
let undoStack = [state1, state2];
|
|
214
|
+
let redoStack = [];
|
|
215
|
+
let currentState = state3;
|
|
216
|
+
// Undo to state2
|
|
217
|
+
const undoResult = performUndo(undoStack, redoStack, currentState);
|
|
218
|
+
t.truthy(undoResult);
|
|
219
|
+
undoStack = undoResult.newUndoStack;
|
|
220
|
+
redoStack = undoResult.newRedoStack;
|
|
221
|
+
currentState = undoResult.newCurrentState;
|
|
222
|
+
t.is(redoStack.length, 1, 'Should have state3 in redo stack');
|
|
223
|
+
// Make new change
|
|
224
|
+
const newState = {
|
|
225
|
+
displayValue: 'alternate',
|
|
226
|
+
placeholderContent: {},
|
|
227
|
+
};
|
|
228
|
+
const pushResult = pushToUndoStack(undoStack, redoStack, currentState, newState);
|
|
229
|
+
undoStack = pushResult.newUndoStack;
|
|
230
|
+
redoStack = pushResult.newRedoStack;
|
|
231
|
+
currentState = pushResult.newCurrentState;
|
|
232
|
+
t.is(redoStack.length, 0, 'Redo stack should be cleared');
|
|
233
|
+
t.is(currentState.displayValue, 'alternate');
|
|
234
|
+
});
|
|
235
|
+
test('edge case: undo/redo with empty initial state', t => {
|
|
236
|
+
const emptyState = { displayValue: '', placeholderContent: {} };
|
|
237
|
+
const filledState = {
|
|
238
|
+
displayValue: 'text',
|
|
239
|
+
placeholderContent: {},
|
|
240
|
+
};
|
|
241
|
+
// Start with filled state, empty in undo stack
|
|
242
|
+
let undoStack = [emptyState];
|
|
243
|
+
let redoStack = [];
|
|
244
|
+
let currentState = filledState;
|
|
245
|
+
// Undo to empty
|
|
246
|
+
const undoResult = performUndo(undoStack, redoStack, currentState);
|
|
247
|
+
t.truthy(undoResult);
|
|
248
|
+
currentState = undoResult.newCurrentState;
|
|
249
|
+
t.is(currentState.displayValue, '');
|
|
250
|
+
// Redo back to filled
|
|
251
|
+
const redoResult = performRedo(undoResult.newUndoStack, undoResult.newRedoStack, currentState);
|
|
252
|
+
t.truthy(redoResult);
|
|
253
|
+
t.is(redoResult.newCurrentState.displayValue, 'text');
|
|
254
|
+
});
|
|
255
|
+
test('edge case: multiple placeholders in undo/redo', t => {
|
|
256
|
+
const stateWithTwoPlaceholders = {
|
|
257
|
+
displayValue: '[Paste #1: 100 chars] and [Paste #2: 200 chars]',
|
|
258
|
+
placeholderContent: {
|
|
259
|
+
'1': {
|
|
260
|
+
type: PlaceholderType.PASTE,
|
|
261
|
+
displayText: '[Paste #1: 100 chars]',
|
|
262
|
+
content: 'first',
|
|
263
|
+
originalSize: 100,
|
|
264
|
+
},
|
|
265
|
+
'2': {
|
|
266
|
+
type: PlaceholderType.PASTE,
|
|
267
|
+
displayText: '[Paste #2: 200 chars]',
|
|
268
|
+
content: 'second',
|
|
269
|
+
originalSize: 200,
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
const currentState = {
|
|
274
|
+
displayValue: 'no placeholders',
|
|
275
|
+
placeholderContent: {},
|
|
276
|
+
};
|
|
277
|
+
const undoStack = [stateWithTwoPlaceholders];
|
|
278
|
+
const redoStack = [];
|
|
279
|
+
const result = performUndo(undoStack, redoStack, currentState);
|
|
280
|
+
t.truthy(result);
|
|
281
|
+
t.is(Object.keys(result.newCurrentState.placeholderContent).length, 2);
|
|
282
|
+
t.true('1' in result.newCurrentState.placeholderContent);
|
|
283
|
+
t.true('2' in result.newCurrentState.placeholderContent);
|
|
284
|
+
});
|
|
285
|
+
//# sourceMappingURL=useInputState.undo-redo.spec.js.map
|