Skip to content

Commit 1e365c4

Browse files
committed
feat(ui): implement filter for slow tests
Also highlight slow tests with yellow color for duration text
1 parent 395d1a2 commit 1e365c4

13 files changed

Lines changed: 79 additions & 7 deletions

File tree

packages/ui/client/components/FilterStatus.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function toggle() {
1616

1717
<template>
1818
<label
19-
class="font-light text-sm checkbox flex items-center py-1 text-sm w-full gap-y-1 mb-1px"
19+
class="font-light text-sm checkbox flex items-center py-1 w-full gap-y-1 mb-1px"
2020
:class="disabled ? 'cursor-not-allowed op50' : 'cursor-pointer'"
2121
v-bind="$attrs"
2222
@click.prevent="toggle"
@@ -26,6 +26,7 @@ function toggle() {
2626
modelValue ? 'i-carbon:checkbox-checked-filled' : 'i-carbon:checkbox',
2727
]"
2828
text-lg
29+
flex-shrink-0
2930
aria-hidden="true"
3031
/>
3132
<input
@@ -34,7 +35,7 @@ function toggle() {
3435
:disabled="disabled"
3536
sr-only
3637
>
37-
<span flex-1 ms-2 select-none>{{ label }}</span>
38+
<span flex-1 ms-2 select-none whitespace-nowrap>{{ label }}</span>
3839
</label>
3940
</template>
4041

packages/ui/client/components/explorer/Explorer.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
242242
<FilterStatus v-model="filter.success" label="Pass" />
243243
<FilterStatus v-model="filter.skipped" label="Skip" />
244244
<FilterStatus v-model="filter.onlyTests" label="Only Tests" />
245+
<FilterStatus v-model="filter.slow" label="Slow" />
245246
</div>
246247
</div>
247248
<div class="scrolls" flex-auto py-1 @scroll.passive="hideAllPoppers">
@@ -343,6 +344,7 @@ useResizeObserver(() => testExplorerRef.value, ([{ contentRect }]) => {
343344
:project-name-color="item.projectNameColor ?? ''"
344345
:state="item.state"
345346
:duration="item.duration"
347+
:slow="item.slow === true"
346348
:opened="item.expanded"
347349
:disable-task-location="!includeTaskLocation"
348350
:class="selectedTest === item.id || (!selectedTest && activeFileId === item.id) ? 'bg-active' : ''"

packages/ui/client/components/explorer/ExplorerItem.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
indent,
2121
name,
2222
duration,
23+
slow,
2324
current,
2425
opened,
2526
expandable,
@@ -35,6 +36,7 @@ const {
3536
indent: number
3637
typecheck?: boolean
3738
duration?: number
39+
slow?: boolean
3840
state?: TaskState
3941
current: boolean
4042
type: TaskTreeNodeType
@@ -220,7 +222,12 @@ const tagsBgGradient = computed(() => {
220222
</span>
221223
<span :text="state === 'fail' ? 'red-500' : ''" v-html="highlighted" />
222224
</span>
223-
<span v-if="typeof duration === 'number'" text="xs" op20 style="white-space: nowrap">
225+
<span
226+
v-if="typeof duration === 'number'"
227+
text="xs"
228+
:class="slow ? 'text-yellow5 op80' : 'op20'"
229+
style="white-space: nowrap"
230+
>
224231
{{ duration > 0 ? duration : '< 1' }}ms
225232
</span>
226233
</div>

packages/ui/client/composables/explorer/collector.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function runLoadFiles(
4141
failed: filter.failed,
4242
success: filter.success,
4343
skipped: filter.skipped,
44+
slow: filter.slow,
4445
onlyTests: filter.onlyTests,
4546
})
4647
}

packages/ui/client/composables/explorer/filter.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Task } from '@vitest/runner'
22
import type { FileTreeNode, Filter, FilterResult, ParentTreeNode, SearchMatcher, UITaskTreeNode } from '~/composables/explorer/types'
3-
import { client, findById } from '~/composables/client'
3+
import { client, config, findById } from '~/composables/client'
44
import { explorerTree } from '~/composables/explorer/index'
55
import { currentProjectName, filteredFiles, projectSort, uiEntries } from '~/composables/explorer/state'
66
import {
@@ -220,6 +220,15 @@ function* filterParents(
220220
}
221221

222222
function matchState(task: Task, filter: Filter) {
223+
if (filter.slow) {
224+
if (task.type === 'test') {
225+
const threshold = config.value.slowTestThreshold
226+
if (typeof threshold === 'number' && typeof task.result?.duration === 'number' && task.result.duration > threshold) {
227+
return true
228+
}
229+
}
230+
}
231+
223232
if (filter.success || filter.failed) {
224233
if ('result' in task) {
225234
if (filter.success && task.result?.state === 'pass') {
@@ -245,7 +254,8 @@ function matchTask(
245254
) {
246255
// search and filter will apply together
247256
if (search(task)) {
248-
if (filter.success || filter.failed || filter.skipped) {
257+
const hasStatusFilter = filter.success || filter.failed || filter.skipped || filter.slow
258+
if (hasStatusFilter) {
249259
if (matchState(task, filter)) {
250260
return true
251261
}

packages/ui/client/composables/explorer/search.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export function useSearch(
6262
filter.failed = false
6363
filter.success = false
6464
filter.skipped = false
65+
filter.slow = false
6566
filter.onlyTests = false
6667
if (focus) {
6768
searchBox.value?.focus()
@@ -94,6 +95,7 @@ export function useSearch(
9495
failedValue: boolean,
9596
successValue: boolean,
9697
skippedValue: boolean,
98+
slowValue: boolean,
9799
onlyTestsValue: boolean,
98100
projectValue: string,
99101
projectSortValue: SortUIType,
@@ -106,6 +108,7 @@ export function useSearch(
106108
treeFilter.value.failed = failedValue
107109
treeFilter.value.success = successValue
108110
treeFilter.value.skipped = skippedValue
111+
treeFilter.value.slow = slowValue
109112
treeFilter.value.onlyTests = onlyTestsValue
110113
treeFilter.value.project = projectValue
111114
treeFilter.value.projectSort = projectSortValue === 'default' ? undefined : projectSortValue
@@ -117,16 +120,18 @@ export function useSearch(
117120
filter.failed,
118121
filter.success,
119122
filter.skipped,
123+
filter.slow,
120124
filter.onlyTests,
121125
currentProject.value,
122126
projectSort.value,
123127
] as const,
124-
([search, failed, success, skipped, onlyTests, project, projectSort]) => {
128+
([search, failed, success, skipped, slow, onlyTests, project, projectSort]) => {
125129
updateFilterStorage(
126130
search,
127131
failed,
128132
success,
129133
skipped,
134+
slow,
130135
onlyTests,
131136
project,
132137
projectSort,

packages/ui/client/composables/explorer/state.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const treeFilter = useLocalStorage<TreeFilterState>(
2424
failed: false,
2525
success: false,
2626
skipped: false,
27+
slow: false,
2728
onlyTests: false,
2829
search: '',
2930
project: ALL_PROJECTS,
@@ -92,6 +93,7 @@ export const filter = reactive<Filter>({
9293
failed: treeFilter.value.failed,
9394
success: treeFilter.value.success,
9495
skipped: treeFilter.value.skipped,
96+
slow: treeFilter.value.slow,
9597
onlyTests: treeFilter.value.onlyTests,
9698
})
9799
export const isFilteredByStatus = computed(() => {
@@ -107,6 +109,10 @@ export const isFilteredByStatus = computed(() => {
107109
return true
108110
}
109111

112+
if (filter.slow) {
113+
return true
114+
}
115+
110116
return false
111117
})
112118
export const filteredFiles = shallowRef<File[]>([])

packages/ui/client/composables/explorer/tree.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export class ExplorerTree {
7373
failed: filter.failed,
7474
success: filter.success,
7575
skipped: filter.skipped,
76+
slow: filter.slow,
7677
onlyTests: filter.onlyTests,
7778
},
7879
)
@@ -127,6 +128,7 @@ export class ExplorerTree {
127128
failed: filter.failed,
128129
success: filter.success,
129130
skipped: filter.skipped,
131+
slow: filter.slow,
130132
onlyTests: filter.onlyTests,
131133
},
132134
end ? this.executionTime : performance.now() - this.startTime,
@@ -143,6 +145,7 @@ export class ExplorerTree {
143145
failed: filter.failed,
144146
success: filter.success,
145147
skipped: filter.skipped,
148+
slow: filter.slow,
146149
onlyTests: filter.onlyTests,
147150
},
148151
end ? this.executionTime : performance.now() - this.startTime,
@@ -160,6 +163,7 @@ export class ExplorerTree {
160163
failed: filter.failed,
161164
success: filter.success,
162165
skipped: filter.skipped,
166+
slow: filter.slow,
163167
onlyTests: filter.onlyTests,
164168
})
165169
}
@@ -176,6 +180,7 @@ export class ExplorerTree {
176180
failed: filter.failed,
177181
success: filter.success,
178182
skipped: filter.skipped,
183+
slow: filter.slow,
179184
onlyTests: filter.onlyTests,
180185
})
181186
})
@@ -193,6 +198,7 @@ export class ExplorerTree {
193198
failed: filter.failed,
194199
success: filter.success,
195200
skipped: filter.skipped,
201+
slow: filter.slow,
196202
onlyTests: filter.onlyTests,
197203
})
198204
})
@@ -204,6 +210,7 @@ export class ExplorerTree {
204210
failed: filter.failed,
205211
success: filter.success,
206212
skipped: filter.skipped,
213+
slow: filter.slow,
207214
onlyTests: filter.onlyTests,
208215
})
209216
})

packages/ui/client/composables/explorer/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export interface UITaskTreeNode extends TaskTreeNode {
4040
indent: number
4141
state?: TaskState
4242
duration?: number
43+
slow?: boolean
4344
}
4445

4546
export interface TestTreeNode extends UITaskTreeNode {
@@ -73,6 +74,7 @@ export interface Filter {
7374
failed: boolean
7475
success: boolean
7576
skipped: boolean
77+
slow: boolean
7678
onlyTests: boolean
7779
}
7880

packages/ui/client/composables/explorer/utils.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
UITaskTreeNode,
99
} from '~/composables/explorer/types'
1010
import { isTestCase } from '@vitest/runner/utils'
11-
import { client } from '~/composables/client'
11+
import { client, config } from '~/composables/client'
1212
import { explorerTree } from '~/composables/explorer/index'
1313
import { openedTreeItemsSet } from '~/composables/explorer/state'
1414
import { getBadgeNameColor, isSuite as isTaskSuite } from '~/utils/task'
@@ -33,6 +33,20 @@ export function isParentNode(node: UITaskTreeNode): node is FileTreeNode | Suite
3333
return node.type === 'file' || node.type === 'suite'
3434
}
3535

36+
export function isSlowTestTask(task: Task) {
37+
if (task.type !== 'test') {
38+
return false
39+
}
40+
41+
const duration = task.result?.duration
42+
if (typeof duration !== 'number') {
43+
return false
44+
}
45+
46+
const treshold = config.value.slowTestThreshold
47+
return typeof treshold === 'number' && duration > treshold
48+
}
49+
3650
export function getSortedRootTasks(sort: SortUIType, tasks = explorerTree.root.tasks) {
3751
const sorted = [...tasks]
3852

@@ -73,6 +87,7 @@ export function createOrUpdateFileNode(
7387
fileNode.state = file.result?.state
7488
fileNode.mode = file.mode
7589
fileNode.duration = typeof file.result?.duration === 'number' ? Math.round(file.result.duration) : undefined
90+
fileNode.slow = false
7691
fileNode.collectDuration = file.collectDuration
7792
fileNode.setupDuration = file.setupDuration
7893
fileNode.environmentLoad = file.environmentLoad
@@ -93,6 +108,7 @@ export function createOrUpdateFileNode(
93108
typecheck: !!file.meta && 'typecheck' in file.meta,
94109
indent: 0,
95110
duration: typeof file.result?.duration === 'number' ? Math.round(file.result.duration) : undefined,
111+
slow: false,
96112
filepath: file.filepath,
97113
projectName: file.projectName || '',
98114
projectNameColor: explorerTree.colors.get(file.projectName || '') || getBadgeNameColor(file.projectName),
@@ -169,6 +185,7 @@ export function createOrUpdateNode(
169185
taskNode.name = task.name
170186
taskNode.mode = task.mode
171187
taskNode.duration = duration
188+
taskNode.slow = isSlowTestTask(task)
172189
taskNode.state = task.result?.state
173190
}
174191
else {
@@ -184,6 +201,7 @@ export function createOrUpdateNode(
184201
expanded: false,
185202
indent: node.indent + 1,
186203
duration,
204+
slow: isSlowTestTask(task),
187205
state: task.result?.state,
188206
} as TestTreeNode
189207
}
@@ -202,6 +220,7 @@ export function createOrUpdateNode(
202220
tasks: [],
203221
indent: node.indent + 1,
204222
duration,
223+
slow: false,
205224
state: task.result?.state,
206225
} as SuiteTreeNode
207226
}

0 commit comments

Comments
 (0)