Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/test/browser/chatStatusDashboard.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 { mainWindow } from '../../../../../base/browser/window.js';
8
import { CancellationToken } from '../../../../../base/common/cancellation.js';
9
import { Event } from '../../../../../base/common/event.js';
10
import { observableValue } from '../../../../../base/common/observable.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
12
import { IInlineCompletionsService } from '../../../../../editor/browser/services/inlineCompletionsService.js';
13
import { IMarkdownRendererService } from '../../../../../platform/markdown/browser/markdownRenderer.js';
14
import { ChatEntitlement, IChatEntitlementService } from '../../../../services/chat/common/chatEntitlementService.js';
15
import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';
16
import { ChatStatusDashboard, IChatStatusDashboardOptions } from '../../../chat/browser/chatStatus/chatStatusDashboard.js';
17
import { IChatStatusItemService } from '../../../chat/browser/chatStatus/chatStatusItemService.js';
18
19
interface IQuotaConfig {
20
percentRemaining: number;
21
unlimited: boolean;
22
usageBasedBilling?: boolean;
23
resetAt?: number;
24
}
25
26
function createEntitlementService(opts: {
27
chat?: IQuotaConfig;
28
completions?: IQuotaConfig;
29
premiumChat?: IQuotaConfig;
30
additionalUsageEnabled?: boolean;
31
entitlement?: ChatEntitlement;
32
}): IChatEntitlementService {
33
return {
34
_serviceBrand: undefined,
35
organisations: undefined,
36
isInternal: false,
37
sku: undefined,
38
copilotTrackingId: undefined,
39
onDidChangeQuotaExceeded: Event.None,
40
onDidChangeQuotaRemaining: Event.None,
41
quotas: {
42
chat: opts.chat,
43
completions: opts.completions,
44
premiumChat: opts.premiumChat,
45
additionalUsageEnabled: opts.additionalUsageEnabled,
46
},
47
update: (_token: CancellationToken) => Promise.resolve(),
48
onDidChangeSentiment: Event.None,
49
sentimentObs: observableValue({}, {}),
50
sentiment: { completed: true },
51
onDidChangeEntitlement: Event.None,
52
entitlement: opts.entitlement ?? ChatEntitlement.Free,
53
entitlementObs: observableValue({}, opts.entitlement ?? ChatEntitlement.Free),
54
anonymous: false,
55
onDidChangeAnonymous: Event.None,
56
anonymousObs: observableValue({}, false),
57
markAnonymousRateLimited: () => { },
58
setForceHidden: () => { },
59
previewFeaturesDisabled: false,
60
clientByokEnabled: false,
61
} as IChatEntitlementService;
62
}
63
64
function getQuotaLabels(element: HTMLElement): string[] {
65
const indicators = element.querySelectorAll('.quota-indicator:not(.included) .quota-title');
66
return Array.from(indicators).map(el => el.textContent ?? '');
67
}
68
69
function getIncludedLabels(element: HTMLElement): string[] {
70
const indicators = element.querySelectorAll('.quota-indicator.included .quota-title');
71
return Array.from(indicators).map(el => el.textContent ?? '');
72
}
73
74
function getQuotaValues(element: HTMLElement): string[] {
75
const values = element.querySelectorAll('.quota-indicator:not(.included) .quota-value');
76
return Array.from(values).map(el => el.textContent ?? '');
77
}
78
79
const dashboardOptions: IChatStatusDashboardOptions = {
80
disableInlineSuggestionsSettings: true,
81
disableModelSelection: true,
82
disableProviderOptions: true,
83
disableCompletionsSnooze: true,
84
};
85
86
suite('ChatStatusDashboard', () => {
87
const store = ensureNoDisposablesAreLeakedInTestSuite();
88
89
function createDashboard(entitlementService: IChatEntitlementService): ChatStatusDashboard {
90
const instantiationService = workbenchInstantiationService(undefined, store);
91
92
instantiationService.stub(IChatEntitlementService, entitlementService);
93
instantiationService.stub(IChatStatusItemService, {
94
_serviceBrand: undefined,
95
onDidChange: Event.None,
96
setOrUpdateEntry: () => { },
97
deleteEntry: () => { },
98
getEntries: () => [],
99
});
100
instantiationService.stub(IInlineCompletionsService, {
101
_serviceBrand: undefined,
102
onDidChangeIsSnoozing: Event.None,
103
snoozeTimeLeft: 0,
104
snooze: () => { },
105
setSnoozeDuration: () => { },
106
});
107
instantiationService.stub(IMarkdownRendererService, {
108
_serviceBrand: undefined,
109
});
110
111
const dashboard = store.add(instantiationService.createInstance(ChatStatusDashboard, dashboardOptions));
112
113
mainWindow.document.body.appendChild(dashboard.element);
114
store.add({ dispose: () => dashboard.element.remove() });
115
116
return dashboard;
117
}
118
119
// --- COPILOT FREE ---
120
121
test('Free — PRU: shows Chat messages and Inline Suggestions', () => {
122
const dashboard = createDashboard(createEntitlementService({
123
chat: { percentRemaining: 80, unlimited: false },
124
completions: { percentRemaining: 70, unlimited: false },
125
entitlement: ChatEntitlement.Free,
126
}));
127
128
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Chat messages', 'Inline Suggestions']);
129
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['20%', '30%']);
130
});
131
132
test('Free — PRU exhausted: shows Chat messages and Inline Suggestions at 0%', () => {
133
const dashboard = createDashboard(createEntitlementService({
134
chat: { percentRemaining: 0, unlimited: false },
135
completions: { percentRemaining: 0, unlimited: false },
136
entitlement: ChatEntitlement.Free,
137
}));
138
139
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Chat messages', 'Inline Suggestions']);
140
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['100%', '100%']);
141
});
142
143
test('Free — TBB: shows Monthly Limit and Inline Suggestions, not Chat messages', () => {
144
const dashboard = createDashboard(createEntitlementService({
145
chat: { percentRemaining: 80, unlimited: false },
146
premiumChat: { percentRemaining: 60, unlimited: false, usageBasedBilling: true },
147
completions: { percentRemaining: 70, unlimited: false },
148
entitlement: ChatEntitlement.Free,
149
}));
150
151
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit', 'Inline Suggestions']);
152
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['40%', '30%']);
153
});
154
155
test('Free — TBB exhausted: shows Monthly Limit and Inline Suggestions at 0%', () => {
156
const dashboard = createDashboard(createEntitlementService({
157
chat: { percentRemaining: 0, unlimited: false },
158
premiumChat: { percentRemaining: 0, unlimited: false, usageBasedBilling: true },
159
completions: { percentRemaining: 0, unlimited: false },
160
entitlement: ChatEntitlement.Free,
161
}));
162
163
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit', 'Inline Suggestions']);
164
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['100%', '100%']);
165
});
166
167
// --- COPILOT PRO (EDU/Pro) ---
168
169
test('EDU/Pro — PRU: shows Chat messages, Premium requests, and Inline Suggestions', () => {
170
const dashboard = createDashboard(createEntitlementService({
171
chat: { percentRemaining: 80, unlimited: false },
172
premiumChat: { percentRemaining: 60, unlimited: false },
173
completions: { percentRemaining: 90, unlimited: false },
174
entitlement: ChatEntitlement.Pro,
175
}));
176
177
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Chat messages', 'Premium requests', 'Inline Suggestions']);
178
});
179
180
test('EDU/Pro — TBB: shows only Monthly Limit, not Chat messages or Inline Suggestions', () => {
181
const dashboard = createDashboard(createEntitlementService({
182
chat: { percentRemaining: 80, unlimited: false },
183
premiumChat: { percentRemaining: 60, unlimited: false, usageBasedBilling: true },
184
completions: { percentRemaining: 90, unlimited: false },
185
entitlement: ChatEntitlement.Pro,
186
}));
187
188
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
189
});
190
191
test('EDU/Pro — TBB exhausted (no overages): shows only Monthly Limit', () => {
192
const dashboard = createDashboard(createEntitlementService({
193
chat: { percentRemaining: 0, unlimited: false },
194
premiumChat: { percentRemaining: 0, unlimited: false, usageBasedBilling: true },
195
completions: { percentRemaining: 90, unlimited: false },
196
additionalUsageEnabled: false,
197
entitlement: ChatEntitlement.Pro,
198
}));
199
200
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
201
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['100%']);
202
});
203
204
test('EDU/Pro — TBB exhausted (with overages): shows only Monthly Limit', () => {
205
const dashboard = createDashboard(createEntitlementService({
206
chat: { percentRemaining: 0, unlimited: false },
207
premiumChat: { percentRemaining: 0, unlimited: false, usageBasedBilling: true },
208
completions: { percentRemaining: 90, unlimited: false },
209
additionalUsageEnabled: true,
210
entitlement: ChatEntitlement.Pro,
211
}));
212
213
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
214
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['100%']);
215
});
216
217
// --- COPILOT PRO+ ---
218
219
test('Pro+ — PRU: shows Premium requests and Inline Suggestions', () => {
220
const dashboard = createDashboard(createEntitlementService({
221
premiumChat: { percentRemaining: 60, unlimited: false },
222
completions: { percentRemaining: 90, unlimited: false },
223
entitlement: ChatEntitlement.ProPlus,
224
}));
225
226
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Premium requests', 'Inline Suggestions']);
227
});
228
229
test('Pro+ — TBB with quota: shows only Monthly Limit', () => {
230
const dashboard = createDashboard(createEntitlementService({
231
chat: { percentRemaining: 80, unlimited: false },
232
premiumChat: { percentRemaining: 60, unlimited: false, usageBasedBilling: true },
233
completions: { percentRemaining: 90, unlimited: false },
234
entitlement: ChatEntitlement.ProPlus,
235
}));
236
237
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
238
});
239
240
test('Pro+ — TBB out of quota: shows only Monthly Limit', () => {
241
const dashboard = createDashboard(createEntitlementService({
242
chat: { percentRemaining: 0, unlimited: false },
243
premiumChat: { percentRemaining: 0, unlimited: false, usageBasedBilling: true },
244
completions: { percentRemaining: 90, unlimited: false },
245
entitlement: ChatEntitlement.ProPlus,
246
}));
247
248
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
249
assert.deepStrictEqual(getQuotaValues(dashboard.element), ['100%']);
250
});
251
252
// --- COPILOT MAX ---
253
254
test('Max Yearly — no TBB: shows unlimited Premium Requests included indicator', () => {
255
const dashboard = createDashboard(createEntitlementService({
256
premiumChat: { percentRemaining: 100, unlimited: true },
257
completions: { percentRemaining: 100, unlimited: true },
258
entitlement: ChatEntitlement.Max,
259
}));
260
261
// Unlimited quotas are not shown as quota indicators
262
assert.deepStrictEqual(getQuotaLabels(dashboard.element), []);
263
// Instead shown as "included" indicator
264
assert.deepStrictEqual(getIncludedLabels(dashboard.element), ['Premium Requests']);
265
});
266
267
test('Max Monthly — TBB: shows unlimited Monthly Limit included indicator', () => {
268
const dashboard = createDashboard(createEntitlementService({
269
premiumChat: { percentRemaining: 100, unlimited: true, usageBasedBilling: true },
270
completions: { percentRemaining: 100, unlimited: true },
271
entitlement: ChatEntitlement.Max,
272
}));
273
274
assert.deepStrictEqual(getQuotaLabels(dashboard.element), []);
275
assert.deepStrictEqual(getIncludedLabels(dashboard.element), ['Monthly Limit']);
276
});
277
278
// --- BUSINESS / ENTERPRISE ---
279
280
test('Enterprise Managed — PRU: shows Premium requests with unlimited included', () => {
281
const dashboard = createDashboard(createEntitlementService({
282
premiumChat: { percentRemaining: 100, unlimited: true },
283
completions: { percentRemaining: 100, unlimited: true },
284
entitlement: ChatEntitlement.Business,
285
}));
286
287
assert.deepStrictEqual(getQuotaLabels(dashboard.element), []);
288
assert.deepStrictEqual(getIncludedLabels(dashboard.element), ['Premium Requests']);
289
});
290
291
test('Enterprise — TBB (multi-quota): shows only Monthly Limit, not Chat messages or Inline Suggestions', () => {
292
const dashboard = createDashboard(createEntitlementService({
293
chat: { percentRemaining: 80, unlimited: false },
294
premiumChat: { percentRemaining: 60, unlimited: false, usageBasedBilling: true },
295
completions: { percentRemaining: 70, unlimited: false },
296
entitlement: ChatEntitlement.Enterprise,
297
}));
298
299
assert.deepStrictEqual(getQuotaLabels(dashboard.element), ['Monthly Limit']);
300
});
301
});
302
303