GeezerVison is showing github.com — fix(ui): simplify explorer filtering + fix nested tests when search m… · vitest-dev/vitest@a627be4
File treeExpand file treeCollapse file tree Open diff view settings packages/ui/client/composables/explorer
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: packages/ui/client/composables/explorer/collapse.ts +3-38Lines changed: 3 additions & 38 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | import type { UITaskTreeNode } from '~/composables/explorer/types' | 2 | 2 | import { explorerTree } from '~/composables/explorer/index' | 3 | 3 | import { openedTreeItems, treeFilter, uiEntries } from '~/composables/explorer/state' | 4 | | -import { isFileNode, isParentNode } from '~/composables/explorer/utils' | | 4 | +import { isFileNode, isParentNode, replaceSubtreeEntries } from '~/composables/explorer/utils' | 5 | 5 |
| 6 | 6 | /** | 7 | 7 | * Collapse all nodes: all children collapsed. | @@ -24,9 +24,10 @@ export function runCollapseNode(id: string) { | 24 | 24 | return | 25 | 25 | } | 26 | 26 |
| | 27 | + node.expanded = false | 27 | 28 | const treeItems = new Set(openedTreeItems.value) | 28 | 29 | treeItems.delete(node.id) | 29 | | - const entries = [...collectCollapseNode(node)] | | 30 | + const entries = replaceSubtreeEntries(uiEntries.value, node, [node]) | 30 | 31 | openedTreeItems.value = Array.from(treeItems) | 31 | 32 | // Keep expandAll state as it is: collapsing individual shouldn't prevent collapsing all the nodes ("collapse all" button) | 32 | 33 | // There is a watcher on composable search.ts to reset to undefined expandAll if there are no opened items | @@ -68,39 +69,3 @@ function collapseAllNodes(nodes: UITaskTreeNode[]) { | 68 | 69 | } | 69 | 70 | } | 70 | 71 | } | 71 | | - | 72 | | -function* collectChildNodes(node: UITaskTreeNode, itself: boolean): Generator<string> { | 73 | | - if (itself) { | 74 | | - yield node.id | 75 | | - } | 76 | | - | 77 | | - if (isParentNode(node)) { | 78 | | - for (let i = 0; i < node.tasks.length; i++) { | 79 | | - yield* collectChildNodes(node.tasks[i], true) | 80 | | - } | 81 | | - } | 82 | | -} | 83 | | - | 84 | | -function* collectCollapseNode(node: UITaskTreeNode) { | 85 | | - const id = node.id | 86 | | - // collect children to remove from the list | 87 | | - const childNodes = new Set<string>(collectChildNodes(node, false)) | 88 | | - for (let i = 0; i < uiEntries.value.length; i++) { | 89 | | - const child = uiEntries.value[i] | 90 | | - // collapse current node and return it | 91 | | - if (child.id === id) { | 92 | | - child.expanded = false | 93 | | - yield child | 94 | | - continue | 95 | | - } | 96 | | - | 97 | | - // remove children from the list | 98 | | - if (childNodes.has(child.id)) { | 99 | | - childNodes.delete(child.id) | 100 | | - continue | 101 | | - } | 102 | | - | 103 | | - // return the node | 104 | | - yield child | 105 | | - } | 106 | | -} |
Collapse file Copy file name to clipboardExpand all lines: packages/ui/client/composables/explorer/collector.ts +8-10Lines changed: 8 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -14,7 +14,7 @@ import type { | 14 | 14 | SearchMatcher, | 15 | 15 | } from '~/composables/explorer/types' | 16 | 16 | import { toArray } from '@vitest/utils/helpers' | 17 | | -import { client, findById } from '~/composables/client' | | 17 | +import { client, config, findById } from '~/composables/client' | 18 | 18 | import { testRunState } from '~/composables/client/state' | 19 | 19 | import { expandNodesOnEndRun } from '~/composables/explorer/expand' | 20 | 20 | import { runFilter, testMatcher } from '~/composables/explorer/filter' | @@ -128,14 +128,10 @@ export function runCollect( | 128 | 128 | doRunFilter(search, filter, end) | 129 | 129 | } | 130 | 130 |
| 131 | | -function* collectRunningTodoTests() { | 132 | | - yield* uiEntries.value.filter(isRunningTestNode) | 133 | | -} | 134 | | - | 135 | 131 | function updateRunningTodoTests() { | 136 | 132 | const idMap = client.state.idMap | 137 | 133 | let task: Task | undefined | 138 | | - for (const test of collectRunningTodoTests()) { | | 134 | + for (const test of uiEntries.value.filter(isRunningTestNode)) { | 139 | 135 | // lookup the parent | 140 | 136 | task = idMap.get(test.parentId) | 141 | 137 | if (task && isSuite(task) && task.mode === 'todo') { | @@ -372,7 +368,7 @@ function collectTests(file: File, search: SearchMatcher = () => true, filter?: F | 372 | 368 | } satisfies CollectFilteredTests | 373 | 369 |
| 374 | 370 | for (const t of testsCollector(file)) { | 375 | | - if (!filter || testMatcher(t, search, filter)) { | | 371 | + if (!filter || testMatcher(t, search, filter, config.value.slowTestThreshold)) { | 376 | 372 | data.total++ | 377 | 373 | if (isSlowTestTask(t)) { | 378 | 374 | data.slow++ | @@ -448,15 +444,17 @@ export function collectTestsTotalData( | 448 | 444 | return filesSummary | 449 | 445 | } | 450 | 446 |
| 451 | | -function* testsCollector(suite: Arrayable<Task>): Generator<Test> { | | 447 | +function testsCollector(suite: Arrayable<Task>, collectedTests: Test[] = []) { | 452 | 448 | const arraySuites = toArray(suite) | 453 | 449 | let s: Task | 454 | 450 | for (let i = 0; i < arraySuites.length; i++) { | 455 | 451 | s = arraySuites[i] | 456 | 452 | if (s.type === 'test') { | 457 | | - yield s | | 453 | + collectedTests.push(s) | 458 | 454 | } else { | 459 | | - yield* testsCollector(s.tasks) | | 455 | + testsCollector(s.tasks, collectedTests) | 460 | 456 | } | 461 | 457 | } | | 458 | + | | 459 | + return collectedTests | 462 | 460 | } |
Collapse file Copy file name to clipboardExpand all lines: packages/ui/client/composables/explorer/expand.ts +13-39Lines changed: 13 additions & 39 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | import type { Filter, SearchMatcher, UITaskTreeNode } from '~/composables/explorer/types' | 2 | | -import { findById } from '~/composables/client' | 3 | | -import { filterAll, filterNode } from '~/composables/explorer/filter' | | 2 | +import { client, config } from '~/composables/client' | | 3 | +import { filterNode, runFilter } from '~/composables/explorer/filter' | 4 | 4 | import { explorerTree } from '~/composables/explorer/index' | 5 | | -import { filteredFiles, openedTreeItems, treeFilter, uiEntries } from '~/composables/explorer/state' | | 5 | +import { openedTreeItems, treeFilter, uiEntries } from '~/composables/explorer/state' | 6 | 6 | import { | 7 | 7 | createOrUpdateNode, | 8 | 8 | createOrUpdateSuiteTask, | 9 | 9 | isFileNode, | 10 | 10 | isParentNode, | | 11 | + replaceSubtreeEntries, | 11 | 12 | } from '~/composables/explorer/utils' | 12 | 13 |
| 13 | 14 | /** | @@ -46,11 +47,15 @@ export function runExpandNode(id: string, search: SearchMatcher, filter: Filter) | 46 | 47 |
| 47 | 48 | const treeItems = new Set(openedTreeItems.value) | 48 | 49 | treeItems.add(node.id) | 49 | | - // collect children | 50 | | - // the first node is itself only when it is a file | 51 | | - const children = new Set(filterNode(node, search, filter)) | | 50 | + const subtree = filterNode(node, { | | 51 | + nodes: explorerTree.nodes, | | 52 | + taskIdMap: client.state.idMap, | | 53 | + search, | | 54 | + filter, | | 55 | + slowTestThreshold: config.value.slowTestThreshold, | | 56 | + }) | 52 | 57 |
| 53 | | - const entries = [...collectExpandedNode(node, children)] | | 58 | + const entries = replaceSubtreeEntries(uiEntries.value, node, subtree) | 54 | 59 | openedTreeItems.value = Array.from(treeItems) | 55 | 60 | // Keep expandAll state as it is: expanding individual shouldn't prevent expanding all the nodes ("expand all" button) | 56 | 61 | // There is a watcher on composable search.ts to reset to undefined expandAll if there are no opened items | @@ -81,11 +86,9 @@ export function runExpandNode(id: string, search: SearchMatcher, filter: Filter) | 81 | 86 | */ | 82 | 87 | export function runExpandAll(search: SearchMatcher, filter: Filter) { | 83 | 88 | expandAllNodes(explorerTree.root.tasks, false) | 84 | | - const entries = [...filterAll(search, filter)] | 85 | 89 | treeFilter.value.expandAll = false | 86 | 90 | openedTreeItems.value = [] | 87 | | - uiEntries.value = entries | 88 | | - filteredFiles.value = entries.filter(isFileNode).map((f) => findById(f.id)!) | | 91 | + runFilter(search, filter) | 89 | 92 | } | 90 | 93 |
| 91 | 94 | export function expandNodesOnEndRun(ids: Set<string>, end: boolean) { | @@ -113,32 +116,3 @@ function expandAllNodes(nodes: UITaskTreeNode[], updateState: boolean) { | 113 | 116 | openedTreeItems.value = [] | 114 | 117 | } | 115 | 118 | } | 116 | | - | 117 | | -/** | 118 | | - * Build the complete next explorer entry list by emitting an expanded node and its filtered | 119 | | - * subtree at the node's current position. | 120 | | - * | 121 | | - * `children` contains only entries produced by filtering the expanded subtree. This function | 122 | | - * walks the complete current `uiEntries`. When it reaches `node`, it emits the node unless | 123 | | - * `children` already contains it, followed by the children. Unrelated entries are copied | 124 | | - * unchanged, while existing entries with the same IDs are skipped to avoid duplicates. | 125 | | - * | 126 | | - * TODO: Make this a pure splice over explicit entries and keep expansion state changes in | 127 | | - * `runExpandNode`. | 128 | | - */ | 129 | | -function* collectExpandedNode(node: UITaskTreeNode, children: Set<UITaskTreeNode>) { | 130 | | - const id = node.id | 131 | | - const ids = new Set(Array.from(children).map((n) => n.id)) | 132 | | - | 133 | | - for (const child of uiEntries.value) { | 134 | | - if (child.id === id) { | 135 | | - child.expanded = true | 136 | | - if (!ids.has(child.id)) { | 137 | | - yield node | 138 | | - } | 139 | | - yield* children | 140 | | - } else if (!ids.has(child.id)) { | 141 | | - yield child | 142 | | - } | 143 | | - } | 144 | | -} |
|