Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/test/browser/planReviewFeedbackService.test.ts
13406 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { IPlanReviewFeedbackService, PlanReviewFeedbackService } from '../../browser/planReviewFeedback/planReviewFeedbackService.js';
8
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
9
import { URI } from '../../../../../base/common/uri.js';
10
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
11
12
function feedbackSummary(items: readonly { line: number; column: number }[]): string[] {
13
return items.map(f => `${f.line}:${f.column}`);
14
}
15
16
suite('PlanReviewFeedbackService - Ordering', () => {
17
18
const store = new DisposableStore();
19
let service: IPlanReviewFeedbackService;
20
let planUri: URI;
21
22
setup(() => {
23
service = store.add(new PlanReviewFeedbackService());
24
planUri = URI.parse('file:///plan.md');
25
store.add(service.registerPlanReview(planUri, () => { }));
26
});
27
28
teardown(() => {
29
store.clear();
30
});
31
32
ensureNoDisposablesAreLeakedInTestSuite();
33
34
test('items sorted by line number', () => {
35
service.addFeedback(planUri, 20, 1, 'line 20');
36
service.addFeedback(planUri, 5, 1, 'line 5');
37
service.addFeedback(planUri, 10, 1, 'line 10');
38
39
assert.deepStrictEqual(feedbackSummary(service.getFeedback(planUri)), [
40
'5:1',
41
'10:1',
42
'20:1',
43
]);
44
});
45
46
test('items sorted by line then column', () => {
47
service.addFeedback(planUri, 10, 20, 'col 20');
48
service.addFeedback(planUri, 10, 5, 'col 5');
49
service.addFeedback(planUri, 10, 10, 'col 10');
50
51
assert.deepStrictEqual(feedbackSummary(service.getFeedback(planUri)), [
52
'10:5',
53
'10:10',
54
'10:20',
55
]);
56
});
57
58
test('removing feedback preserves ordering', () => {
59
const id1 = service.addFeedback(planUri, 30, 1, 'line 30');
60
service.addFeedback(planUri, 10, 1, 'line 10');
61
service.addFeedback(planUri, 20, 1, 'line 20');
62
63
assert.deepStrictEqual(feedbackSummary(service.getFeedback(planUri)), [
64
'10:1',
65
'20:1',
66
'30:1',
67
]);
68
69
service.removeFeedback(planUri, id1);
70
assert.deepStrictEqual(feedbackSummary(service.getFeedback(planUri)), [
71
'10:1',
72
'20:1',
73
]);
74
});
75
76
test('same line number items are stable', () => {
77
const id1 = service.addFeedback(planUri, 10, 1, 'first');
78
const id2 = service.addFeedback(planUri, 10, 1, 'second');
79
80
const items = service.getFeedback(planUri);
81
assert.strictEqual(items[0].id, id1);
82
assert.strictEqual(items[1].id, id2);
83
});
84
85
test('clear removes all items', () => {
86
service.addFeedback(planUri, 1, 1, 'a');
87
service.addFeedback(planUri, 2, 1, 'b');
88
service.addFeedback(planUri, 3, 1, 'c');
89
90
assert.strictEqual(service.getFeedback(planUri).length, 3);
91
service.clearFeedback(planUri);
92
assert.strictEqual(service.getFeedback(planUri).length, 0);
93
});
94
95
test('update feedback changes text', () => {
96
const id = service.addFeedback(planUri, 10, 1, 'original');
97
service.updateFeedback(planUri, id, 'updated');
98
99
const items = service.getFeedback(planUri);
100
assert.strictEqual(items.length, 1);
101
assert.strictEqual(items[0].text, 'updated');
102
assert.strictEqual(items[0].line, 10);
103
});
104
});
105
106
suite('PlanReviewFeedbackService - Navigation', () => {
107
108
const store = new DisposableStore();
109
let service: IPlanReviewFeedbackService;
110
let planUri: URI;
111
112
setup(() => {
113
service = store.add(new PlanReviewFeedbackService());
114
planUri = URI.parse('file:///plan.md');
115
store.add(service.registerPlanReview(planUri, () => { }));
116
});
117
118
teardown(() => {
119
store.clear();
120
});
121
122
ensureNoDisposablesAreLeakedInTestSuite();
123
124
test('navigation follows sorted order', () => {
125
service.addFeedback(planUri, 20, 1, 'line 20');
126
service.addFeedback(planUri, 5, 1, 'line 5');
127
service.addFeedback(planUri, 10, 1, 'line 10');
128
129
// Expected order: 5, 10, 20
130
const first = service.getNextFeedback(planUri, true)!;
131
assert.strictEqual(first.line, 5);
132
133
const second = service.getNextFeedback(planUri, true)!;
134
assert.strictEqual(second.line, 10);
135
136
const third = service.getNextFeedback(planUri, true)!;
137
assert.strictEqual(third.line, 20);
138
139
// Wraps around
140
const fourth = service.getNextFeedback(planUri, true)!;
141
assert.strictEqual(fourth.line, 5);
142
});
143
144
test('navigation backwards', () => {
145
service.addFeedback(planUri, 5, 1, 'line 5');
146
service.addFeedback(planUri, 10, 1, 'line 10');
147
service.addFeedback(planUri, 20, 1, 'line 20');
148
149
// First backward nav goes to last item
150
const first = service.getNextFeedback(planUri, false)!;
151
assert.strictEqual(first.line, 20);
152
153
const second = service.getNextFeedback(planUri, false)!;
154
assert.strictEqual(second.line, 10);
155
156
const third = service.getNextFeedback(planUri, false)!;
157
assert.strictEqual(third.line, 5);
158
159
// Wraps around
160
const fourth = service.getNextFeedback(planUri, false)!;
161
assert.strictEqual(fourth.line, 20);
162
});
163
164
test('navigation bearings reflect sorted position', () => {
165
service.addFeedback(planUri, 20, 1, 'line 20');
166
service.addFeedback(planUri, 5, 1, 'line 5');
167
service.addFeedback(planUri, 10, 1, 'line 10');
168
169
// Before navigation, no anchor
170
let bearing = service.getNavigationBearing(planUri);
171
assert.strictEqual(bearing.activeIdx, -1);
172
assert.strictEqual(bearing.totalCount, 3);
173
174
// Navigate to first (5)
175
service.getNextFeedback(planUri, true);
176
bearing = service.getNavigationBearing(planUri);
177
assert.strictEqual(bearing.activeIdx, 0);
178
179
// Navigate to second (10)
180
service.getNextFeedback(planUri, true);
181
bearing = service.getNavigationBearing(planUri);
182
assert.strictEqual(bearing.activeIdx, 1);
183
184
// Navigate to third (20)
185
service.getNextFeedback(planUri, true);
186
bearing = service.getNavigationBearing(planUri);
187
assert.strictEqual(bearing.activeIdx, 2);
188
});
189
190
test('navigation returns undefined for empty feedback', () => {
191
const result = service.getNextFeedback(planUri, true);
192
assert.strictEqual(result, undefined);
193
});
194
195
test('setNavigationAnchor updates the anchor', () => {
196
const id = service.addFeedback(planUri, 10, 1, 'line 10');
197
service.addFeedback(planUri, 20, 1, 'line 20');
198
199
service.setNavigationAnchor(planUri, id);
200
const bearing = service.getNavigationBearing(planUri);
201
assert.strictEqual(bearing.activeIdx, 0);
202
});
203
});
204
205
suite('PlanReviewFeedbackService - Registration', () => {
206
207
const store = new DisposableStore();
208
let service: IPlanReviewFeedbackService;
209
210
setup(() => {
211
service = store.add(new PlanReviewFeedbackService());
212
});
213
214
teardown(() => {
215
store.clear();
216
});
217
218
ensureNoDisposablesAreLeakedInTestSuite();
219
220
test('isActivePlanReview returns false before registration', () => {
221
const planUri = URI.parse('file:///plan.md');
222
assert.strictEqual(service.isActivePlanReview(planUri), false);
223
});
224
225
test('isActivePlanReview returns true after registration', () => {
226
const planUri = URI.parse('file:///plan.md');
227
store.add(service.registerPlanReview(planUri, () => { }));
228
assert.strictEqual(service.isActivePlanReview(planUri), true);
229
});
230
231
test('isActivePlanReview returns false after dispose', () => {
232
const planUri = URI.parse('file:///plan.md');
233
const registration = service.registerPlanReview(planUri, () => { });
234
assert.strictEqual(service.isActivePlanReview(planUri), true);
235
registration.dispose();
236
assert.strictEqual(service.isActivePlanReview(planUri), false);
237
});
238
239
test('feedback cannot be added to unregistered plan', () => {
240
const planUri = URI.parse('file:///plan.md');
241
const id = service.addFeedback(planUri, 1, 1, 'text');
242
assert.strictEqual(id, '');
243
assert.strictEqual(service.getFeedback(planUri).length, 0);
244
});
245
246
test('dispose clears feedback items', () => {
247
const planUri = URI.parse('file:///plan.md');
248
const registration = service.registerPlanReview(planUri, () => { });
249
service.addFeedback(planUri, 1, 1, 'text');
250
assert.strictEqual(service.getFeedback(planUri).length, 1);
251
registration.dispose();
252
assert.strictEqual(service.getFeedback(planUri).length, 0);
253
});
254
255
test('onDidChangeRegistrations fires on register and dispose', () => {
256
const planUri = URI.parse('file:///plan.md');
257
let fireCount = 0;
258
store.add(service.onDidChangeRegistrations(() => fireCount++));
259
260
const registration = service.registerPlanReview(planUri, () => { });
261
assert.strictEqual(fireCount, 1);
262
263
registration.dispose();
264
assert.strictEqual(fireCount, 2);
265
});
266
267
test('onDidChangeFeedback fires on add and remove', () => {
268
const planUri = URI.parse('file:///plan.md');
269
store.add(service.registerPlanReview(planUri, () => { }));
270
271
let fireCount = 0;
272
store.add(service.onDidChangeFeedback(() => fireCount++));
273
274
const id = service.addFeedback(planUri, 1, 1, 'text');
275
assert.strictEqual(fireCount, 1);
276
277
service.removeFeedback(planUri, id);
278
assert.strictEqual(fireCount, 2);
279
});
280
});
281
282
suite('PlanReviewFeedbackService - Submit', () => {
283
284
const store = new DisposableStore();
285
let service: IPlanReviewFeedbackService;
286
287
setup(() => {
288
service = store.add(new PlanReviewFeedbackService());
289
});
290
291
teardown(() => {
292
store.clear();
293
});
294
295
ensureNoDisposablesAreLeakedInTestSuite();
296
297
test('submitAllFeedback calls onSubmit with formatted feedback', () => {
298
const planUri = URI.parse('file:///plan.md');
299
let submittedResult: { rejected: boolean; feedback?: string } | undefined;
300
store.add(service.registerPlanReview(planUri, (result) => { submittedResult = result; }));
301
302
service.addFeedback(planUri, 1, 1, 'fix this');
303
service.addFeedback(planUri, 45, 45, 'change that');
304
305
service.submitAllFeedback(planUri);
306
307
assert.ok(submittedResult);
308
assert.strictEqual(submittedResult!.rejected, false);
309
assert.strictEqual(submittedResult!.feedback, [
310
'Here\'s the feedback:',
311
'Line 1: fix this',
312
'Line 45: Column 45: change that',
313
].join('\n'));
314
});
315
316
test('submitAllFeedback does nothing when no items', () => {
317
const planUri = URI.parse('file:///plan.md');
318
let called = false;
319
store.add(service.registerPlanReview(planUri, () => { called = true; }));
320
321
service.submitAllFeedback(planUri);
322
assert.strictEqual(called, false);
323
});
324
325
test('feedback at column 1 omits column', () => {
326
const planUri = URI.parse('file:///plan.md');
327
let submittedResult: { feedback?: string } | undefined;
328
store.add(service.registerPlanReview(planUri, (result) => { submittedResult = result; }));
329
330
service.addFeedback(planUri, 10, 1, 'at start');
331
332
service.submitAllFeedback(planUri);
333
334
assert.ok(submittedResult);
335
assert.strictEqual(submittedResult!.feedback, [
336
'Here\'s the feedback:',
337
'Line 10: at start',
338
].join('\n'));
339
});
340
341
test('feedback at column > 1 includes column', () => {
342
const planUri = URI.parse('file:///plan.md');
343
let submittedResult: { feedback?: string } | undefined;
344
store.add(service.registerPlanReview(planUri, (result) => { submittedResult = result; }));
345
346
service.addFeedback(planUri, 10, 15, 'mid line');
347
348
service.submitAllFeedback(planUri);
349
350
assert.ok(submittedResult);
351
assert.strictEqual(submittedResult!.feedback, [
352
'Here\'s the feedback:',
353
'Line 10: Column 15: mid line',
354
].join('\n'));
355
});
356
});
357
358