@codingame/monaco-vscode-testing-service-override 28.4.0 → 29.0.0
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/package.json +4 -4
- package/vscode/src/vs/workbench/contrib/debug/browser/callStackWidget.js +5 -5
- package/vscode/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.js +21 -21
- package/vscode/src/vs/workbench/contrib/testing/browser/codeCoverageDisplayUtils.js +5 -5
- package/vscode/src/vs/workbench/contrib/testing/browser/icons.js +29 -29
- package/vscode/src/vs/workbench/contrib/testing/browser/testCoverageBars.js +3 -3
- package/vscode/src/vs/workbench/contrib/testing/browser/testCoverageView.js +15 -15
- package/vscode/src/vs/workbench/contrib/testing/browser/testExplorerActions.js +72 -72
- package/vscode/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.js +5 -5
- package/vscode/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.js +22 -22
- package/vscode/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.js +3 -3
- package/vscode/src/vs/workbench/contrib/testing/browser/testing.contribution.js +8 -8
- package/vscode/src/vs/workbench/contrib/testing/browser/testingConfigurationUi.js +2 -2
- package/vscode/src/vs/workbench/contrib/testing/browser/testingDecorations.js +19 -19
- package/vscode/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.js +11 -11
- package/vscode/src/vs/workbench/contrib/testing/browser/testingExplorerView.js +15 -15
- package/vscode/src/vs/workbench/contrib/testing/browser/testingOutputPeek.js +11 -11
- package/vscode/src/vs/workbench/contrib/testing/browser/testingViewPaneContainer.js +1 -1
- package/vscode/src/vs/workbench/contrib/testing/browser/theme.js +32 -32
- package/vscode/src/vs/workbench/contrib/testing/common/configuration.js +24 -24
- package/vscode/src/vs/workbench/contrib/testing/common/testResultService.d.ts +1 -1
- package/vscode/src/vs/workbench/contrib/testing/common/testServiceImpl.js +4 -4
- package/vscode/src/vs/workbench/contrib/testing/common/testingChatAgentTool.d.ts +51 -0
- package/vscode/src/vs/workbench/contrib/testing/common/testingChatAgentTool.js +191 -126
- package/vscode/src/vs/workbench/contrib/testing/common/testingContentProvider.js +1 -1
- package/vscode/src/vs/workbench/contrib/testing/common/testingContextKeys.js +31 -31
- package/vscode/src/vs/workbench/contrib/testing/common/testingProgressMessages.js +5 -5
|
@@ -23,7 +23,7 @@ import { isFailedState } from '@codingame/monaco-vscode-api/vscode/vs/workbench/
|
|
|
23
23
|
import { ITestResultService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/testing/common/testResultService.service';
|
|
24
24
|
import { waitForTestToBeIdle, testsInFile } from './testService.js';
|
|
25
25
|
import { ITestService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/testing/common/testService.service';
|
|
26
|
-
import { TestRunProfileBitset, TestResultState,
|
|
26
|
+
import { TestRunProfileBitset, TestItemExpandState, TestResultState, DetailType, TestMessageType } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/testing/common/testTypes';
|
|
27
27
|
import { Position } from '@codingame/monaco-vscode-api/vscode/vs/editor/common/core/position';
|
|
28
28
|
import { ITestProfileService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/testing/common/testProfileService.service';
|
|
29
29
|
|
|
@@ -80,11 +80,11 @@ let RunTestTool = class RunTestTool {
|
|
|
80
80
|
items: {
|
|
81
81
|
type: "string"
|
|
82
82
|
},
|
|
83
|
-
description: "When mode=\"coverage\": absolute file paths to include detailed coverage info for.
|
|
83
|
+
description: "When mode=\"coverage\": absolute file paths to include detailed coverage info for. If not provided, a file-level summary of all files with incomplete coverage is shown."
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
userDescription: ( localize(
|
|
87
|
+
userDescription: ( localize(14583, "Run unit tests (optionally with coverage)")),
|
|
88
88
|
source: ToolDataSource.Internal,
|
|
89
89
|
tags: [
|
|
90
90
|
"vscode_editing_with_tests",
|
|
@@ -123,11 +123,11 @@ let RunTestTool = class RunTestTool {
|
|
|
123
123
|
kind: "text",
|
|
124
124
|
value: "No tests found in the files. Ensure the correct absolute paths are passed to the tool."
|
|
125
125
|
}],
|
|
126
|
-
toolResultError: ( localize(
|
|
126
|
+
toolResultError: ( localize(14584, "No tests found in the files"))
|
|
127
127
|
};
|
|
128
128
|
}
|
|
129
129
|
progress.report({
|
|
130
|
-
message: ( localize(
|
|
130
|
+
message: ( localize(14585, "Starting test run..."))
|
|
131
131
|
});
|
|
132
132
|
if (group === TestRunProfileBitset.Coverage) {
|
|
133
133
|
if (!( testCases.some(
|
|
@@ -144,7 +144,7 @@ let RunTestTool = class RunTestTool {
|
|
|
144
144
|
value: "No test run was started. Instruct the user to ensure their test runner is correctly configured"
|
|
145
145
|
}],
|
|
146
146
|
toolResultError: ( localize(
|
|
147
|
-
|
|
147
|
+
14586,
|
|
148
148
|
"No test run was started. This may be an issue with your test runner or extension."
|
|
149
149
|
))
|
|
150
150
|
};
|
|
@@ -155,12 +155,12 @@ let RunTestTool = class RunTestTool {
|
|
|
155
155
|
return {
|
|
156
156
|
content: [{
|
|
157
157
|
kind: "text",
|
|
158
|
-
value: ( localize(
|
|
158
|
+
value: ( localize(14587, "Test run was cancelled."))
|
|
159
159
|
}],
|
|
160
|
-
toolResultMessage: ( localize(
|
|
160
|
+
toolResultMessage: ( localize(14587, "Test run was cancelled."))
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
|
-
const summary = await
|
|
163
|
+
const summary = await buildTestRunSummary(result, mode, coverageFiles);
|
|
164
164
|
const content = [{
|
|
165
165
|
kind: "text",
|
|
166
166
|
value: summary
|
|
@@ -170,117 +170,6 @@ let RunTestTool = class RunTestTool {
|
|
|
170
170
|
toolResultMessage: getTestProgressText(collectTestStateCounts(false, [result]))
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
|
-
async _buildSummary(result, mode, coverageFiles) {
|
|
174
|
-
const failures = result.counts[TestResultState.Errored] + result.counts[TestResultState.Failed];
|
|
175
|
-
let str = `<summary passed=${result.counts[TestResultState.Passed]} failed=${failures} />\n`;
|
|
176
|
-
if (failures !== 0) {
|
|
177
|
-
str += await this._getFailureDetails(result);
|
|
178
|
-
}
|
|
179
|
-
if (mode === "coverage") {
|
|
180
|
-
str += await this._getCoverageSummary(result, coverageFiles);
|
|
181
|
-
}
|
|
182
|
-
return str;
|
|
183
|
-
}
|
|
184
|
-
async _getCoverageSummary(result, coverageFiles) {
|
|
185
|
-
if (!coverageFiles || !coverageFiles.length) {
|
|
186
|
-
return "";
|
|
187
|
-
}
|
|
188
|
-
for (const task of result.tasks) {
|
|
189
|
-
const coverage = task.coverage.get();
|
|
190
|
-
if (!coverage) {
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
const normalized = ( coverageFiles.map(file => URI.file(file).fsPath));
|
|
194
|
-
const coveredFilesMap = ( new Map());
|
|
195
|
-
for (const file of ( coverage.getAllFiles().values())) {
|
|
196
|
-
coveredFilesMap.set(file.uri.fsPath, file);
|
|
197
|
-
}
|
|
198
|
-
for (const path of normalized) {
|
|
199
|
-
const file = coveredFilesMap.get(path);
|
|
200
|
-
if (!file) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
let summary = `<coverage task=${JSON.stringify(task.name || "")}>\n`;
|
|
204
|
-
const pct = getTotalCoveragePercent(file.statement, file.branch, file.declaration) * 100;
|
|
205
|
-
summary += `<firstUncoveredFile path=${JSON.stringify(path)} statementsCovered=${file.statement.covered} statementsTotal=${file.statement.total}`;
|
|
206
|
-
if (file.branch) {
|
|
207
|
-
summary += ` branchesCovered=${file.branch.covered} branchesTotal=${file.branch.total}`;
|
|
208
|
-
}
|
|
209
|
-
if (file.declaration) {
|
|
210
|
-
summary += ` declarationsCovered=${file.declaration.covered} declarationsTotal=${file.declaration.total}`;
|
|
211
|
-
}
|
|
212
|
-
summary += ` percent=${pct.toFixed(2)}`;
|
|
213
|
-
try {
|
|
214
|
-
const details = await file.details();
|
|
215
|
-
for (const detail of details) {
|
|
216
|
-
if (detail.count || !detail.location) {
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
let startLine;
|
|
220
|
-
let endLine;
|
|
221
|
-
if (Position.isIPosition(detail.location)) {
|
|
222
|
-
startLine = endLine = detail.location.lineNumber;
|
|
223
|
-
} else {
|
|
224
|
-
startLine = detail.location.startLineNumber;
|
|
225
|
-
endLine = detail.location.endLineNumber;
|
|
226
|
-
}
|
|
227
|
-
summary += ` firstUncoveredStart=${startLine} firstUncoveredEnd=${endLine}`;
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
} catch {}
|
|
231
|
-
summary += ` />\n`;
|
|
232
|
-
summary += `</coverage>\n`;
|
|
233
|
-
return summary;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return "";
|
|
237
|
-
}
|
|
238
|
-
async _getFailureDetails(result) {
|
|
239
|
-
let str = "";
|
|
240
|
-
let hadMessages = false;
|
|
241
|
-
for (const failure of result.tests) {
|
|
242
|
-
if (!isFailedState(failure.ownComputedState)) {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
const [, ...testPath] = TestId.split(failure.item.extId);
|
|
246
|
-
const testName = testPath.pop();
|
|
247
|
-
str += `<testFailure name=${JSON.stringify(testName)} path=${JSON.stringify(testPath.join(" > "))}>\n`;
|
|
248
|
-
for (const task of failure.tasks) {
|
|
249
|
-
for (const message of task.messages.filter(m => m.type === TestMessageType.Error)) {
|
|
250
|
-
hadMessages = true;
|
|
251
|
-
if (message.expected !== undefined && message.actual !== undefined) {
|
|
252
|
-
str += `<expectedOutput>\n${message.expected}\n</expectedOutput>\n`;
|
|
253
|
-
str += `<actualOutput>\n${message.actual}\n</actualOutput>\n`;
|
|
254
|
-
} else {
|
|
255
|
-
const messageText = typeof message.message === "string" ? message.message : message.message.value;
|
|
256
|
-
str += `<message>\n${messageText}\n</message>\n`;
|
|
257
|
-
}
|
|
258
|
-
if (message.stackTrace && message.stackTrace.length > 0) {
|
|
259
|
-
for (const frame of message.stackTrace.slice(0, 10)) {
|
|
260
|
-
if (frame.uri && frame.position) {
|
|
261
|
-
str += `<stackFrame path="${frame.uri.fsPath}" line="${frame.position.lineNumber}" col="${frame.position.column}" />\n`;
|
|
262
|
-
} else if (frame.uri) {
|
|
263
|
-
str += `<stackFrame path="${frame.uri.fsPath}">${frame.label}</stackFrame>\n`;
|
|
264
|
-
} else {
|
|
265
|
-
str += `<stackFrame>${frame.label}</stackFrame>\n`;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (message.location) {
|
|
270
|
-
str += `<location path="${message.location.uri.fsPath}" line="${message.location.range.startLineNumber}" col="${message.location.range.startColumn}" />\n`;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
str += `</testFailure>\n`;
|
|
275
|
-
}
|
|
276
|
-
if (!hadMessages) {
|
|
277
|
-
const output = ( result.tasks.map(t => ( t.output.getRange(0, t.output.length).toString()).trim())).join("\n");
|
|
278
|
-
if (output) {
|
|
279
|
-
str += `<output>\n${output}\n</output>\n`;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return str;
|
|
283
|
-
}
|
|
284
173
|
async _monitorRunProgress(result, progress, token) {
|
|
285
174
|
const store = ( new DisposableStore());
|
|
286
175
|
const update = () => {
|
|
@@ -339,7 +228,7 @@ let RunTestTool = class RunTestTool {
|
|
|
339
228
|
return tests;
|
|
340
229
|
}
|
|
341
230
|
progress.report({
|
|
342
|
-
message: ( localize(
|
|
231
|
+
message: ( localize(14588, "Filtering tests..."))
|
|
343
232
|
});
|
|
344
233
|
const testNames = ( params.testNames.map(t => t.toLowerCase().trim()));
|
|
345
234
|
const filtered = [];
|
|
@@ -368,7 +257,7 @@ let RunTestTool = class RunTestTool {
|
|
|
368
257
|
return [...this._testService.collection.rootItems];
|
|
369
258
|
}
|
|
370
259
|
progress.report({
|
|
371
|
-
message: ( localize(
|
|
260
|
+
message: ( localize(14589, "Discovering tests..."))
|
|
372
261
|
});
|
|
373
262
|
const firstWorkspaceFolder = this._workspaceContextService.getWorkspace().folders.at(0)?.uri;
|
|
374
263
|
const uris = ( params.files.map(f => {
|
|
@@ -392,18 +281,194 @@ let RunTestTool = class RunTestTool {
|
|
|
392
281
|
}
|
|
393
282
|
prepareToolInvocation(context, token) {
|
|
394
283
|
const params = context.parameters;
|
|
395
|
-
const title = ( localize(
|
|
284
|
+
const title = ( localize(14590, "Allow test run?"));
|
|
396
285
|
const inFiles = params.files?.map(f => "`" + basename(f) + "`");
|
|
397
286
|
return Promise.resolve({
|
|
398
|
-
invocationMessage: ( localize(
|
|
287
|
+
invocationMessage: ( localize(14591, "Running tests...")),
|
|
399
288
|
confirmationMessages: {
|
|
400
289
|
title,
|
|
401
|
-
message: inFiles?.length ? ( new MarkdownString()).appendMarkdown(( localize(
|
|
290
|
+
message: inFiles?.length ? ( new MarkdownString()).appendMarkdown(( localize(14592, "The model wants to run tests in {0}.", inFiles.join(", ")))) : ( localize(14593, "The model wants to run all tests.")),
|
|
402
291
|
allowAutoConfirm: true
|
|
403
292
|
}
|
|
404
293
|
});
|
|
405
294
|
}
|
|
406
295
|
};
|
|
407
296
|
RunTestTool = ( __decorate([( __param(0, ITestService)), ( __param(1, IUriIdentityService)), ( __param(2, IWorkspaceContextService)), ( __param(3, ITestResultService)), ( __param(4, ITestProfileService))], RunTestTool));
|
|
297
|
+
async function buildTestRunSummary(result, mode, coverageFiles) {
|
|
298
|
+
const failures = result.counts[TestResultState.Errored] + result.counts[TestResultState.Failed];
|
|
299
|
+
let str = `<summary passed=${result.counts[TestResultState.Passed]} failed=${failures} />\n`;
|
|
300
|
+
if (failures !== 0) {
|
|
301
|
+
str += await getFailureDetails(result);
|
|
302
|
+
}
|
|
303
|
+
if (mode === "coverage") {
|
|
304
|
+
str += await getCoverageSummary(result, coverageFiles);
|
|
305
|
+
}
|
|
306
|
+
return str;
|
|
307
|
+
}
|
|
308
|
+
async function getCoverageSummary(result, coverageFiles) {
|
|
309
|
+
let str = "";
|
|
310
|
+
for (const task of result.tasks) {
|
|
311
|
+
const coverage = task.coverage.get();
|
|
312
|
+
if (!coverage) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (!coverageFiles || !coverageFiles.length) {
|
|
316
|
+
str += getOverallCoverageSummary(coverage);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const normalized = ( coverageFiles.map(file => URI.file(file).fsPath));
|
|
320
|
+
const coveredFilesMap = ( new Map());
|
|
321
|
+
for (const file of ( coverage.getAllFiles().values())) {
|
|
322
|
+
coveredFilesMap.set(file.uri.fsPath, file);
|
|
323
|
+
}
|
|
324
|
+
for (const path of normalized) {
|
|
325
|
+
const file = coveredFilesMap.get(path);
|
|
326
|
+
if (!file) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
str += await getFileCoverageDetails(file, path);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return str;
|
|
333
|
+
}
|
|
334
|
+
function getOverallCoverageSummary(coverage) {
|
|
335
|
+
const files = ( [...( coverage.getAllFiles().values())].map(f => ({
|
|
336
|
+
path: f.uri.fsPath,
|
|
337
|
+
pct: getTotalCoveragePercent(f.statement, f.branch, f.declaration) * 100
|
|
338
|
+
}))).filter(f => f.pct < 100).sort((a, b) => a.pct - b.pct);
|
|
339
|
+
if (!files.length) {
|
|
340
|
+
return "<coverageSummary>All files have 100% coverage.</coverageSummary>\n";
|
|
341
|
+
}
|
|
342
|
+
let str = "<coverageSummary>\n";
|
|
343
|
+
for (const f of files) {
|
|
344
|
+
str += `<file path="${f.path}" percent=${f.pct.toFixed(1)} />\n`;
|
|
345
|
+
}
|
|
346
|
+
str += "</coverageSummary>\n";
|
|
347
|
+
return str;
|
|
348
|
+
}
|
|
349
|
+
async function getFileCoverageDetails(file, path) {
|
|
350
|
+
const pct = getTotalCoveragePercent(file.statement, file.branch, file.declaration) * 100;
|
|
351
|
+
let str = `<coverage path="${path}" percent=${pct.toFixed(1)} statements=${file.statement.covered}/${file.statement.total}`;
|
|
352
|
+
if (file.branch) {
|
|
353
|
+
str += ` branches=${file.branch.covered}/${file.branch.total}`;
|
|
354
|
+
}
|
|
355
|
+
if (file.declaration) {
|
|
356
|
+
str += ` declarations=${file.declaration.covered}/${file.declaration.total}`;
|
|
357
|
+
}
|
|
358
|
+
str += ">\n";
|
|
359
|
+
try {
|
|
360
|
+
const details = await file.details();
|
|
361
|
+
const uncoveredDeclarations = [];
|
|
362
|
+
const uncoveredBranches = [];
|
|
363
|
+
const uncoveredLines = [];
|
|
364
|
+
for (const detail of details) {
|
|
365
|
+
if (detail.type === DetailType.Declaration) {
|
|
366
|
+
if (!detail.count) {
|
|
367
|
+
const line = Position.isIPosition(detail.location) ? detail.location.lineNumber : detail.location.startLineNumber;
|
|
368
|
+
uncoveredDeclarations.push({
|
|
369
|
+
name: detail.name,
|
|
370
|
+
line
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
} else {
|
|
374
|
+
if (!detail.count) {
|
|
375
|
+
const startLine = Position.isIPosition(detail.location) ? detail.location.lineNumber : detail.location.startLineNumber;
|
|
376
|
+
const endLine = Position.isIPosition(detail.location) ? detail.location.lineNumber : detail.location.endLineNumber;
|
|
377
|
+
uncoveredLines.push([startLine, endLine]);
|
|
378
|
+
}
|
|
379
|
+
if (detail.branches) {
|
|
380
|
+
for (const branch of detail.branches) {
|
|
381
|
+
if (!branch.count) {
|
|
382
|
+
let line;
|
|
383
|
+
if (branch.location) {
|
|
384
|
+
line = Position.isIPosition(branch.location) ? branch.location.lineNumber : branch.location.startLineNumber;
|
|
385
|
+
} else {
|
|
386
|
+
line = Position.isIPosition(detail.location) ? detail.location.lineNumber : detail.location.startLineNumber;
|
|
387
|
+
}
|
|
388
|
+
uncoveredBranches.push({
|
|
389
|
+
line,
|
|
390
|
+
label: branch.label
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (uncoveredDeclarations.length) {
|
|
398
|
+
str += "uncovered functions: " + ( uncoveredDeclarations.map(d => `${d.name}(L${d.line})`)).join(", ") + "\n";
|
|
399
|
+
}
|
|
400
|
+
if (uncoveredBranches.length) {
|
|
401
|
+
str += "uncovered branches: " + ( uncoveredBranches.map(b => b.label ? `L${b.line}(${b.label})` : `L${b.line}`)).join(", ") + "\n";
|
|
402
|
+
}
|
|
403
|
+
if (uncoveredLines.length) {
|
|
404
|
+
str += "uncovered lines: " + mergeLineRanges(uncoveredLines) + "\n";
|
|
405
|
+
}
|
|
406
|
+
} catch {}
|
|
407
|
+
str += "</coverage>\n";
|
|
408
|
+
return str;
|
|
409
|
+
}
|
|
410
|
+
function mergeLineRanges(ranges) {
|
|
411
|
+
if (!ranges.length) {
|
|
412
|
+
return "";
|
|
413
|
+
}
|
|
414
|
+
ranges.sort((a, b) => a[0] - b[0]);
|
|
415
|
+
const merged = [ranges[0]];
|
|
416
|
+
for (let i = 1; i < ranges.length; i++) {
|
|
417
|
+
const last = merged[merged.length - 1];
|
|
418
|
+
const [start, end] = ranges[i];
|
|
419
|
+
if (start <= last[1] + 1) {
|
|
420
|
+
last[1] = Math.max(last[1], end);
|
|
421
|
+
} else {
|
|
422
|
+
merged.push([start, end]);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return ( merged.map(([s, e]) => s === e ? `${s}` : `${s}-${e}`)).join(", ");
|
|
426
|
+
}
|
|
427
|
+
async function getFailureDetails(result) {
|
|
428
|
+
let str = "";
|
|
429
|
+
let hadMessages = false;
|
|
430
|
+
for (const failure of result.tests) {
|
|
431
|
+
if (!isFailedState(failure.ownComputedState)) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const [, ...testPath] = TestId.split(failure.item.extId);
|
|
435
|
+
const testName = testPath.pop();
|
|
436
|
+
str += `<testFailure name=${JSON.stringify(testName)} path=${JSON.stringify(testPath.join(" > "))}>\n`;
|
|
437
|
+
for (const task of failure.tasks) {
|
|
438
|
+
for (const message of task.messages.filter(m => m.type === TestMessageType.Error)) {
|
|
439
|
+
hadMessages = true;
|
|
440
|
+
if (message.expected !== undefined && message.actual !== undefined) {
|
|
441
|
+
str += `<expectedOutput>\n${message.expected}\n</expectedOutput>\n`;
|
|
442
|
+
str += `<actualOutput>\n${message.actual}\n</actualOutput>\n`;
|
|
443
|
+
} else {
|
|
444
|
+
const messageText = typeof message.message === "string" ? message.message : message.message.value;
|
|
445
|
+
str += `<message>\n${messageText}\n</message>\n`;
|
|
446
|
+
}
|
|
447
|
+
if (message.stackTrace && message.stackTrace.length > 0) {
|
|
448
|
+
for (const frame of message.stackTrace.slice(0, 10)) {
|
|
449
|
+
if (frame.uri && frame.position) {
|
|
450
|
+
str += `<stackFrame path="${frame.uri.fsPath}" line="${frame.position.lineNumber}" col="${frame.position.column}" />\n`;
|
|
451
|
+
} else if (frame.uri) {
|
|
452
|
+
str += `<stackFrame path="${frame.uri.fsPath}">${frame.label}</stackFrame>\n`;
|
|
453
|
+
} else {
|
|
454
|
+
str += `<stackFrame>${frame.label}</stackFrame>\n`;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (message.location) {
|
|
459
|
+
str += `<location path="${message.location.uri.fsPath}" line="${message.location.range.startLineNumber}" col="${message.location.range.startColumn}" />\n`;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
str += `</testFailure>\n`;
|
|
464
|
+
}
|
|
465
|
+
if (!hadMessages) {
|
|
466
|
+
const output = ( result.tasks.map(t => ( t.output.getRange(0, t.output.length).toString()).trim())).join("\n");
|
|
467
|
+
if (output) {
|
|
468
|
+
str += `<output>\n${output}\n</output>\n`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return str;
|
|
472
|
+
}
|
|
408
473
|
|
|
409
|
-
export { TestingChatAgentToolContribution };
|
|
474
|
+
export { RunTestTool, TestingChatAgentToolContribution, buildTestRunSummary, getCoverageSummary, getFailureDetails, getFileCoverageDetails, getOverallCoverageSummary, mergeLineRanges };
|
|
@@ -59,7 +59,7 @@ let TestingContentProvider = class TestingContentProvider {
|
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
if (!hadContent) {
|
|
62
|
-
append(( localize(
|
|
62
|
+
append(( localize(14594, "The test run did not record any output.")));
|
|
63
63
|
dispose.dispose();
|
|
64
64
|
}
|
|
65
65
|
});
|
|
@@ -10,120 +10,120 @@ var TestingContextKeys;
|
|
|
10
10
|
TestingContextKeys.canRefreshTests = ( new RawContextKey("testing.canRefresh", false, {
|
|
11
11
|
type: "boolean",
|
|
12
12
|
description: ( localize(
|
|
13
|
-
|
|
13
|
+
14595,
|
|
14
14
|
"Indicates whether any test controller has an attached refresh handler."
|
|
15
15
|
))
|
|
16
16
|
}));
|
|
17
17
|
TestingContextKeys.isRefreshingTests = ( new RawContextKey("testing.isRefreshing", false, {
|
|
18
18
|
type: "boolean",
|
|
19
19
|
description: ( localize(
|
|
20
|
-
|
|
20
|
+
14596,
|
|
21
21
|
"Indicates whether any test controller is currently refreshing tests."
|
|
22
22
|
))
|
|
23
23
|
}));
|
|
24
24
|
TestingContextKeys.isContinuousModeOn = ( new RawContextKey("testing.isContinuousModeOn", false, {
|
|
25
25
|
type: "boolean",
|
|
26
|
-
description: ( localize(
|
|
26
|
+
description: ( localize(14597, "Indicates whether continuous test mode is on."))
|
|
27
27
|
}));
|
|
28
28
|
TestingContextKeys.hasDebuggableTests = ( new RawContextKey("testing.hasDebuggableTests", false, {
|
|
29
29
|
type: "boolean",
|
|
30
30
|
description: ( localize(
|
|
31
|
-
|
|
31
|
+
14598,
|
|
32
32
|
"Indicates whether any test controller has registered a debug configuration"
|
|
33
33
|
))
|
|
34
34
|
}));
|
|
35
35
|
TestingContextKeys.hasRunnableTests = ( new RawContextKey("testing.hasRunnableTests", false, {
|
|
36
36
|
type: "boolean",
|
|
37
37
|
description: ( localize(
|
|
38
|
-
|
|
38
|
+
14599,
|
|
39
39
|
"Indicates whether any test controller has registered a run configuration"
|
|
40
40
|
))
|
|
41
41
|
}));
|
|
42
42
|
TestingContextKeys.hasCoverableTests = ( new RawContextKey("testing.hasCoverableTests", false, {
|
|
43
43
|
type: "boolean",
|
|
44
44
|
description: ( localize(
|
|
45
|
-
|
|
45
|
+
14600,
|
|
46
46
|
"Indicates whether any test controller has registered a coverage configuration"
|
|
47
47
|
))
|
|
48
48
|
}));
|
|
49
49
|
TestingContextKeys.hasNonDefaultProfile = ( new RawContextKey("testing.hasNonDefaultProfile", false, {
|
|
50
50
|
type: "boolean",
|
|
51
51
|
description: ( localize(
|
|
52
|
-
|
|
52
|
+
14601,
|
|
53
53
|
"Indicates whether any test controller has registered a non-default configuration"
|
|
54
54
|
))
|
|
55
55
|
}));
|
|
56
56
|
TestingContextKeys.hasConfigurableProfile = ( new RawContextKey("testing.hasConfigurableProfile", false, {
|
|
57
57
|
type: "boolean",
|
|
58
|
-
description: ( localize(
|
|
58
|
+
description: ( localize(14602, "Indicates whether any test configuration can be configured"))
|
|
59
59
|
}));
|
|
60
60
|
TestingContextKeys.supportsContinuousRun = ( new RawContextKey("testing.supportsContinuousRun", false, {
|
|
61
61
|
type: "boolean",
|
|
62
|
-
description: ( localize(
|
|
62
|
+
description: ( localize(14603, "Indicates whether continous test running is supported"))
|
|
63
63
|
}));
|
|
64
64
|
TestingContextKeys.isParentRunningContinuously = ( new RawContextKey("testing.isParentRunningContinuously", false, {
|
|
65
65
|
type: "boolean",
|
|
66
66
|
description: ( localize(
|
|
67
|
-
|
|
67
|
+
14604,
|
|
68
68
|
"Indicates whether the parent of a test is continuously running, set in the menu context of test items"
|
|
69
69
|
))
|
|
70
70
|
}));
|
|
71
71
|
TestingContextKeys.activeEditorHasTests = ( new RawContextKey("testing.activeEditorHasTests", false, {
|
|
72
72
|
type: "boolean",
|
|
73
|
-
description: ( localize(
|
|
73
|
+
description: ( localize(14605, "Indicates whether any tests are present in the current editor"))
|
|
74
74
|
}));
|
|
75
75
|
TestingContextKeys.cursorInsideTestRange = ( new RawContextKey("testing.cursorInsideTestRange", false, {
|
|
76
76
|
type: "boolean",
|
|
77
|
-
description: ( localize(
|
|
77
|
+
description: ( localize(14606, "Whether the cursor is currently inside a test range"))
|
|
78
78
|
}));
|
|
79
79
|
TestingContextKeys.isTestCoverageOpen = ( new RawContextKey("testing.isTestCoverageOpen", false, {
|
|
80
80
|
type: "boolean",
|
|
81
|
-
description: ( localize(
|
|
81
|
+
description: ( localize(14607, "Indicates whether a test coverage report is open"))
|
|
82
82
|
}));
|
|
83
83
|
TestingContextKeys.hasCoverageInFile = ( new RawContextKey("testing.hasCoverageInFile", false, {
|
|
84
84
|
type: "boolean",
|
|
85
|
-
description: ( localize(
|
|
85
|
+
description: ( localize(14608, "Indicates coverage has been reported in the curent editor."))
|
|
86
86
|
}));
|
|
87
87
|
TestingContextKeys.hasPerTestCoverage = ( new RawContextKey("testing.hasPerTestCoverage", false, {
|
|
88
88
|
type: "boolean",
|
|
89
|
-
description: ( localize(
|
|
89
|
+
description: ( localize(14609, "Indicates whether per-test coverage is available"))
|
|
90
90
|
}));
|
|
91
91
|
TestingContextKeys.hasInlineCoverageDetails = ( new RawContextKey("testing.hasInlineCoverageDetails", false, {
|
|
92
92
|
type: "boolean",
|
|
93
93
|
description: ( localize(
|
|
94
|
-
|
|
94
|
+
14610,
|
|
95
95
|
"Indicates whether detailed per-line coverage is available for inline display"
|
|
96
96
|
))
|
|
97
97
|
}));
|
|
98
98
|
TestingContextKeys.isCoverageFilteredToTest = ( new RawContextKey("testing.isCoverageFilteredToTest", false, {
|
|
99
99
|
type: "boolean",
|
|
100
|
-
description: ( localize(
|
|
100
|
+
description: ( localize(14611, "Indicates whether coverage has been filterd to a single test"))
|
|
101
101
|
}));
|
|
102
102
|
TestingContextKeys.coverageToolbarEnabled = ( new RawContextKey("testing.coverageToolbarEnabled", true, {
|
|
103
103
|
type: "boolean",
|
|
104
|
-
description: ( localize(
|
|
104
|
+
description: ( localize(14612, "Indicates whether the coverage toolbar is enabled"))
|
|
105
105
|
}));
|
|
106
106
|
TestingContextKeys.inlineCoverageEnabled = ( new RawContextKey("testing.inlineCoverageEnabled", false, {
|
|
107
107
|
type: "boolean",
|
|
108
|
-
description: ( localize(
|
|
108
|
+
description: ( localize(14613, "Indicates whether inline coverage is shown"))
|
|
109
109
|
}));
|
|
110
110
|
TestingContextKeys.canGoToRelatedCode = ( new RawContextKey("testing.canGoToRelatedCode", false, {
|
|
111
111
|
type: "boolean",
|
|
112
112
|
description: ( localize(
|
|
113
|
-
|
|
113
|
+
14614,
|
|
114
114
|
"Whether a controller implements a capability to find code related to a test"
|
|
115
115
|
))
|
|
116
116
|
}));
|
|
117
117
|
TestingContextKeys.canGoToRelatedTest = ( new RawContextKey("testing.canGoToRelatedTest", false, {
|
|
118
118
|
type: "boolean",
|
|
119
119
|
description: ( localize(
|
|
120
|
-
|
|
120
|
+
14615,
|
|
121
121
|
"Whether a controller implements a capability to find tests related to code"
|
|
122
122
|
))
|
|
123
123
|
}));
|
|
124
124
|
TestingContextKeys.peekHasStack = ( new RawContextKey("testing.peekHasStack", false, {
|
|
125
125
|
type: "boolean",
|
|
126
|
-
description: ( localize(
|
|
126
|
+
description: ( localize(14616, "Whether the message shown in a peek view has a stack trace"))
|
|
127
127
|
}));
|
|
128
128
|
TestingContextKeys.capabilityToContextKey = {
|
|
129
129
|
[TestRunProfileBitset.Run]: TestingContextKeys.hasRunnableTests,
|
|
@@ -142,54 +142,54 @@ var TestingContextKeys;
|
|
|
142
142
|
TestingContextKeys.peekItemType = ( new RawContextKey("peekItemType", undefined, {
|
|
143
143
|
type: "string",
|
|
144
144
|
description: ( localize(
|
|
145
|
-
|
|
145
|
+
14617,
|
|
146
146
|
"Type of the item in the output peek view. Either a \"test\", \"message\", \"task\", or \"result\"."
|
|
147
147
|
))
|
|
148
148
|
}));
|
|
149
149
|
TestingContextKeys.controllerId = ( new RawContextKey("controllerId", undefined, {
|
|
150
150
|
type: "string",
|
|
151
|
-
description: ( localize(
|
|
151
|
+
description: ( localize(14618, "Controller ID of the current test item"))
|
|
152
152
|
}));
|
|
153
153
|
TestingContextKeys.testItemExtId = ( new RawContextKey("testId", undefined, {
|
|
154
154
|
type: "string",
|
|
155
155
|
description: ( localize(
|
|
156
|
-
|
|
156
|
+
14619,
|
|
157
157
|
"ID of the current test item, set when creating or opening menus on test items"
|
|
158
158
|
))
|
|
159
159
|
}));
|
|
160
160
|
TestingContextKeys.testItemHasUri = ( new RawContextKey("testing.testItemHasUri", false, {
|
|
161
161
|
type: "boolean",
|
|
162
|
-
description: ( localize(
|
|
162
|
+
description: ( localize(14620, "Boolean indicating whether the test item has a URI defined"))
|
|
163
163
|
}));
|
|
164
164
|
TestingContextKeys.testItemIsHidden = ( new RawContextKey("testing.testItemIsHidden", false, {
|
|
165
165
|
type: "boolean",
|
|
166
|
-
description: ( localize(
|
|
166
|
+
description: ( localize(14621, "Boolean indicating whether the test item is hidden"))
|
|
167
167
|
}));
|
|
168
168
|
TestingContextKeys.testMessageContext = ( new RawContextKey("testMessage", undefined, {
|
|
169
169
|
type: "string",
|
|
170
170
|
description: ( localize(
|
|
171
|
-
|
|
171
|
+
14622,
|
|
172
172
|
"Value set in `testMessage.contextValue`, available in editor/content and testing/message/context"
|
|
173
173
|
))
|
|
174
174
|
}));
|
|
175
175
|
TestingContextKeys.testResultOutdated = ( new RawContextKey("testResultOutdated", undefined, {
|
|
176
176
|
type: "boolean",
|
|
177
177
|
description: ( localize(
|
|
178
|
-
|
|
178
|
+
14623,
|
|
179
179
|
"Value available in editor/content and testing/message/context when the result is outdated"
|
|
180
180
|
))
|
|
181
181
|
}));
|
|
182
182
|
TestingContextKeys.testResultState = ( new RawContextKey("testResultState", undefined, {
|
|
183
183
|
type: "string",
|
|
184
184
|
description: ( localize(
|
|
185
|
-
|
|
185
|
+
14624,
|
|
186
186
|
"Value available testing/item/result indicating the state of the item."
|
|
187
187
|
))
|
|
188
188
|
}));
|
|
189
189
|
TestingContextKeys.testProfileContextGroup = ( new RawContextKey("testing.profile.context.group", undefined, {
|
|
190
190
|
type: "string",
|
|
191
191
|
description: ( localize(
|
|
192
|
-
|
|
192
|
+
14625,
|
|
193
193
|
"Type of menu where the configure testing profile submenu exists. Either \"run\", \"debug\", or \"coverage\""
|
|
194
194
|
))
|
|
195
195
|
}));
|
|
@@ -43,10 +43,10 @@ const getTestProgressText = (
|
|
|
43
43
|
}
|
|
44
44
|
if (isRunning) {
|
|
45
45
|
if (runSoFar === 0) {
|
|
46
|
-
return localize(
|
|
46
|
+
return localize(14626, "Running tests...");
|
|
47
47
|
} else if (skipped === 0) {
|
|
48
48
|
return localize(
|
|
49
|
-
|
|
49
|
+
14627,
|
|
50
50
|
"Running tests, {0}/{1} passed ({2}%)",
|
|
51
51
|
passed,
|
|
52
52
|
totalWillBeRun,
|
|
@@ -54,7 +54,7 @@ const getTestProgressText = (
|
|
|
54
54
|
);
|
|
55
55
|
} else {
|
|
56
56
|
return localize(
|
|
57
|
-
|
|
57
|
+
14628,
|
|
58
58
|
"Running tests, {0}/{1} tests passed ({2}%, {3} skipped)",
|
|
59
59
|
passed,
|
|
60
60
|
totalWillBeRun,
|
|
@@ -65,7 +65,7 @@ const getTestProgressText = (
|
|
|
65
65
|
} else {
|
|
66
66
|
if (skipped === 0) {
|
|
67
67
|
return localize(
|
|
68
|
-
|
|
68
|
+
14629,
|
|
69
69
|
"{0}/{1} tests passed ({2}%)",
|
|
70
70
|
passed,
|
|
71
71
|
runSoFar,
|
|
@@ -73,7 +73,7 @@ const getTestProgressText = (
|
|
|
73
73
|
);
|
|
74
74
|
} else {
|
|
75
75
|
return localize(
|
|
76
|
-
|
|
76
|
+
14630,
|
|
77
77
|
"{0}/{1} tests passed ({2}%, {3} skipped)",
|
|
78
78
|
passed,
|
|
79
79
|
runSoFar,
|