| 1446 | } |
| 1447 | |
| 1448 | export function makeTestTextTree({ items, uriFilter, buffer = [], indent = 0, onlyFailures, onlyActive, sortByLabel }: { items?: vs.TestItemCollection, uriFilter?: vs.Uri, buffer?: string[]; indent?: number, onlyFailures?: boolean, onlyActive?: boolean, sortByLabel?: boolean } = {}): string[] { |
| 1449 | const controller = privateApi.testController!; |
| 1450 | const collection = items instanceof vs.Uri |
| 1451 | ? controller.controller.items |
| 1452 | : items ?? controller.controller.items; |
| 1453 | |
| 1454 | const testItems: vs.TestItem[] = []; |
| 1455 | collection.forEach((item) => testItems.push(item)); |
| 1456 | |
| 1457 | // Sort the items by their locations by default so we get stable results. Otherwise the order that items |
| 1458 | // are created would be used, which is usually source-order, but could be different if the user |
| 1459 | // selectively runs tests starting at the end of the file. |
| 1460 | // Allow overriding to sort by name for tests that are modifying files and running subsets of tests |
| 1461 | // and don't care about source order. |
| 1462 | sortBy(testItems, sortByLabel ? (item) => item.label : getSourceLine); |
| 1463 | |
| 1464 | for (const item of testItems) { |
| 1465 | const lastResult = controller.getLatestData(item); |
| 1466 | |
| 1467 | // Only include statuses on WF/Project nodes if we aren't filtering, otherwise |
| 1468 | // the status may look wrong (because it might be skewed by filtered out nodes). |
| 1469 | const isWorkspaceFolderOrProject = item.id.startsWith("WF:") || item.id.startsWith("PROJECT:"); |
| 1470 | const includeStatus = !isWorkspaceFolderOrProject || !uriFilter; |
| 1471 | |
| 1472 | let nodeString = item.label; |
| 1473 | if (item.description) |
| 1474 | nodeString += ` [${item.description}]`; |
| 1475 | |
| 1476 | let includeNode = true; |
| 1477 | if (lastResult) { |
| 1478 | const status = "status" in lastResult ? lastResult?.status as TestStatus : undefined; |
| 1479 | if (includeStatus) { |
| 1480 | if (status) |
| 1481 | nodeString += ` ${TestStatus[status]}`; |
| 1482 | else if (lastResult.children.length) |
| 1483 | nodeString += ` ${TestStatus[lastResult.getHighestChildStatus(true)]}`; |
| 1484 | } |
| 1485 | |
| 1486 | // If this node has a different file to the parent, include that in the output. |
| 1487 | // Don't include it if it's already in the label though. |
| 1488 | if (lastResult.path && lastResult.parent?.path && lastResult.path !== lastResult.parent?.path && !item.label.endsWith(path.basename(lastResult.path))) |
| 1489 | nodeString += ` (${path.basename(lastResult.path)})`; |
| 1490 | |
| 1491 | const isStale = lastResult.isStale; |
| 1492 | const isFailure = status === TestStatus.Failed; |
| 1493 | if ((isStale && onlyActive) || (!isFailure && onlyFailures)) |
| 1494 | includeNode = false; |
| 1495 | } else if (!isWorkspaceFolderOrProject) { |
| 1496 | nodeString += " (not found in model)"; |
| 1497 | } |
| 1498 | |
| 1499 | // Stop filtering children at the point that we match the filter (eg. we match the suite, include all children). |
| 1500 | const nodeMatches = uriFilter && item.uri?.toString() === uriFilter.toString(); |
| 1501 | const filterChildren = !nodeMatches; |
| 1502 | |
| 1503 | const childBuffer: string[] = []; |
| 1504 | makeTestTextTree({ items: item.children, uriFilter: filterChildren ? uriFilter : undefined, buffer: childBuffer, indent: indent + 1, onlyFailures, onlyActive, sortByLabel }); |
| 1505 | |