mirror of
https://github.com/hanzoai/vitest.git
synced 2026-09-24 17:04:25 +00:00
236 lines
6.9 KiB
TypeScript
236 lines
6.9 KiB
TypeScript
import type { RunnerTestFile as File, RunnerTask as Task } from 'vitest'
|
|
import type {
|
|
FileTreeNode,
|
|
ParentTreeNode,
|
|
SortUIType,
|
|
SuiteTreeNode,
|
|
TestTreeNode,
|
|
UITaskTreeNode,
|
|
} from '~/composables/explorer/types'
|
|
import { client, config } from '~/composables/client'
|
|
import { explorerTree } from '~/composables/explorer/index'
|
|
import { openedTreeItemsSet } from '~/composables/explorer/state'
|
|
import { getBadgeNameColor, isSuite as isTaskSuite } from '~/utils/task'
|
|
|
|
export function isTestNode(node: UITaskTreeNode): node is TestTreeNode {
|
|
return node.type === 'test'
|
|
}
|
|
|
|
export function isRunningTestNode(node: UITaskTreeNode): node is TestTreeNode {
|
|
return node.mode === 'run' && (node.type === 'test')
|
|
}
|
|
|
|
export function isFileNode(node: UITaskTreeNode): node is FileTreeNode {
|
|
return node.type === 'file'
|
|
}
|
|
|
|
export function isParentNode(node: UITaskTreeNode): node is FileTreeNode | SuiteTreeNode {
|
|
return node.type === 'file' || node.type === 'suite'
|
|
}
|
|
|
|
export function isSlowTestTask(task: Task) {
|
|
if (task.type !== 'test') {
|
|
return false
|
|
}
|
|
|
|
const duration = task.result?.duration
|
|
if (typeof duration !== 'number') {
|
|
return false
|
|
}
|
|
|
|
const threshold = config.value.slowTestThreshold
|
|
return typeof threshold === 'number' && duration > threshold
|
|
}
|
|
|
|
export function getSortedRootTasks(sort: SortUIType, tasks = explorerTree.root.tasks) {
|
|
const sorted = [...tasks]
|
|
|
|
sorted.sort((a, b) => {
|
|
if (sort === 'duration-desc' || sort === 'duration-asc') {
|
|
const durationA = a.duration ?? 0
|
|
const durationB = b.duration ?? 0
|
|
if (durationA !== durationB) {
|
|
return sort === 'duration-desc'
|
|
? durationB - durationA
|
|
: durationA - durationB
|
|
}
|
|
}
|
|
else if (sort === 'asc' || sort === 'desc') {
|
|
const projectA = a.projectName || ''
|
|
const projectB = b.projectName || ''
|
|
if (projectA !== projectB) {
|
|
return sort === 'asc'
|
|
? projectA.localeCompare(projectB)
|
|
: projectB.localeCompare(projectA)
|
|
}
|
|
}
|
|
// Default sort (by filepath, then project) is the fallback
|
|
return `${a.filepath}:${a.projectName}`.localeCompare(`${b.filepath}:${b.projectName}`)
|
|
})
|
|
|
|
return sorted
|
|
}
|
|
|
|
export function createOrUpdateFileNode(
|
|
file: File,
|
|
collect = false,
|
|
) {
|
|
let fileNode = explorerTree.nodes.get(file.id) as FileTreeNode | undefined
|
|
|
|
if (fileNode) {
|
|
fileNode.typecheck = !!file.meta && 'typecheck' in file.meta
|
|
fileNode.label = file.meta?.__vitest_label__
|
|
fileNode.state = file.result?.state
|
|
fileNode.mode = file.mode
|
|
fileNode.duration = typeof file.result?.duration === 'number' ? Math.round(file.result.duration) : undefined
|
|
fileNode.slow = false
|
|
fileNode.collectDuration = file.collectDuration
|
|
fileNode.setupDuration = file.setupDuration
|
|
fileNode.environmentLoad = file.environmentLoad
|
|
fileNode.prepareDuration = file.prepareDuration
|
|
}
|
|
else {
|
|
fileNode = {
|
|
id: file.id,
|
|
parentId: 'root',
|
|
name: file.name,
|
|
mode: file.mode,
|
|
expandable: true,
|
|
// When the current run finish, we will expand all nodes when required, here we expand only the opened nodes
|
|
expanded: openedTreeItemsSet.value.size > 0 && openedTreeItemsSet.value.has(file.id),
|
|
type: 'file',
|
|
children: new Set(),
|
|
tasks: [],
|
|
typecheck: !!file.meta && 'typecheck' in file.meta,
|
|
label: file.meta?.__vitest_label__,
|
|
indent: 0,
|
|
duration: typeof file.result?.duration === 'number' ? Math.round(file.result.duration) : undefined,
|
|
slow: false,
|
|
filepath: file.filepath,
|
|
projectName: file.projectName || '',
|
|
projectNameColor: explorerTree.colors.get(file.projectName || '') || getBadgeNameColor(file.projectName),
|
|
collectDuration: file.collectDuration,
|
|
setupDuration: file.setupDuration,
|
|
environmentLoad: file.environmentLoad,
|
|
prepareDuration: file.prepareDuration,
|
|
state: file.result?.state,
|
|
}
|
|
explorerTree.nodes.set(file.id, fileNode)
|
|
explorerTree.root.tasks.push(fileNode)
|
|
}
|
|
if (collect) {
|
|
for (let i = 0; i < file.tasks.length; i++) {
|
|
createOrUpdateNode(file.id, file.tasks[i], true)
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createOrUpdateSuiteTask(
|
|
id: string,
|
|
all: boolean,
|
|
) {
|
|
const node = explorerTree.nodes.get(id)
|
|
if (!node || !isParentNode(node)) {
|
|
return
|
|
}
|
|
|
|
const task = client.state.idMap.get(id)
|
|
// if no children just return
|
|
if (!task || !isTaskSuite(task)) {
|
|
return
|
|
}
|
|
|
|
// update the node
|
|
createOrUpdateNode(node.parentId, task, all && task.tasks.length > 0)
|
|
|
|
return [node, task] as const
|
|
}
|
|
|
|
export function createOrUpdateNodeTask(id: string) {
|
|
const node = explorerTree.nodes.get(id)
|
|
if (!node) {
|
|
return
|
|
}
|
|
|
|
const task = client.state.idMap.get(id)
|
|
// if it is not a test just return
|
|
if (!task || task.type !== 'test') {
|
|
return
|
|
}
|
|
|
|
createOrUpdateNode(node.parentId, task, false)
|
|
}
|
|
|
|
export function createOrUpdateNode(
|
|
parentId: string,
|
|
task: Task,
|
|
createAll: boolean,
|
|
) {
|
|
const node = explorerTree.nodes.get(parentId) as ParentTreeNode | undefined
|
|
let taskNode: UITaskTreeNode | undefined
|
|
const duration = typeof task.result?.duration === 'number'
|
|
? Math.round(task.result.duration)
|
|
: undefined
|
|
if (node) {
|
|
taskNode = explorerTree.nodes.get(task.id)
|
|
if (taskNode) {
|
|
if (!node.children.has(task.id)) {
|
|
node.tasks.push(taskNode)
|
|
node.children.add(task.id)
|
|
}
|
|
|
|
taskNode.name = task.name
|
|
taskNode.mode = task.mode
|
|
taskNode.duration = duration
|
|
taskNode.slow = isSlowTestTask(task)
|
|
taskNode.state = task.result?.state
|
|
}
|
|
else {
|
|
if (task.type === 'test') {
|
|
taskNode = {
|
|
id: task.id,
|
|
fileId: task.file.id,
|
|
parentId,
|
|
name: task.name,
|
|
mode: task.mode,
|
|
type: task.type,
|
|
expandable: false,
|
|
expanded: false,
|
|
indent: node.indent + 1,
|
|
duration,
|
|
slow: isSlowTestTask(task),
|
|
state: task.result?.state,
|
|
} as TestTreeNode
|
|
}
|
|
else {
|
|
taskNode = {
|
|
id: task.id,
|
|
fileId: task.file.id,
|
|
parentId,
|
|
name: task.name,
|
|
mode: task.mode,
|
|
type: 'suite',
|
|
expandable: true,
|
|
// When the current run finish, we will expand all nodes when required, here we expand only the opened nodes
|
|
expanded: openedTreeItemsSet.value.size > 0 && openedTreeItemsSet.value.has(task.id),
|
|
children: new Set(),
|
|
tasks: [],
|
|
indent: node.indent + 1,
|
|
duration,
|
|
slow: false,
|
|
state: task.result?.state,
|
|
} as SuiteTreeNode
|
|
}
|
|
explorerTree.nodes.set(task.id, taskNode)
|
|
node.tasks.push(taskNode)
|
|
node.children.add(task.id)
|
|
}
|
|
|
|
if (taskNode && createAll && isTaskSuite(task)) {
|
|
for (let i = 0; i < task.tasks.length; i++) {
|
|
createOrUpdateNode(taskNode.id, task.tasks[i], createAll)
|
|
}
|
|
}
|
|
}
|
|
}
|