2025-05-30 18:56:14 +02:00

203 lines
5.2 KiB
TypeScript

import type { File, TaskResultPack, TestAnnotation } from '@vitest/runner'
import type { RunnerTaskEventPack } from 'vitest'
import type {
CollectorInfo,
FilteredTests,
RootTreeNode,
UITaskTreeNode,
} from '~/composables/explorer/types'
import { runCollapseAllTask, runCollapseNode } from '~/composables/explorer/collapse'
import { annotateTest, collectTestsTotalData, preparePendingTasks, runCollect, runLoadFiles } from '~/composables/explorer/collector'
import { runExpandAll, runExpandNode } from '~/composables/explorer/expand'
import { runFilter } from '~/composables/explorer/filter'
import {
filter,
search,
} from '~/composables/explorer/state'
export class ExplorerTree {
private rafCollector: ReturnType<typeof useRafFn>
private resumeEndRunId: ReturnType<typeof setTimeout> | undefined
constructor(
public projects: string[] = [],
public colors = new Map<string, string | undefined>(),
private onTaskUpdateCalled: boolean = false,
private resumeEndTimeout = 500,
public root = <RootTreeNode>{
id: 'vitest-root-node',
expandable: true,
expanded: true,
tasks: [],
},
public pendingTasks = new Map<string, Set<string>>(),
public nodes = new Map<string, UITaskTreeNode>(),
public summary = reactive<CollectorInfo>({
files: 0,
time: '',
filesFailed: 0,
filesSuccess: 0,
filesIgnore: 0,
filesRunning: 0,
filesSkipped: 0,
filesSnapshotFailed: 0,
filesTodo: 0,
testsFailed: 0,
testsSuccess: 0,
testsIgnore: 0,
testsSkipped: 0,
testsTodo: 0,
totalTests: 0,
failedSnapshot: false,
failedSnapshotEnabled: false,
}),
) {
// will run runCollect every ~100ms: 1000/10 = 100ms
// (beware increasing fpsLimit, it can be too much for the browser)
this.rafCollector = useRafFn(this.runCollect.bind(this), { fpsLimit: 10, immediate: false })
}
loadFiles(remoteFiles: File[], projects: { name: string; color?: string }[]) {
this.projects.splice(0, this.projects.length, ...projects.map(p => p.name))
this.colors = new Map(projects.map(p => [p.name, p.color]))
runLoadFiles(
remoteFiles,
true,
search.value.trim(),
{
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
},
)
}
startRun() {
this.resumeEndRunId = setTimeout(() => this.endRun(), this.resumeEndTimeout)
this.collect(true, false)
}
annotateTest(testId: string, annotation: TestAnnotation) {
annotateTest(testId, annotation)
if (!this.onTaskUpdateCalled) {
clearTimeout(this.resumeEndRunId)
this.onTaskUpdateCalled = true
this.collect(true, false, false)
this.rafCollector.resume()
}
}
resumeRun(packs: TaskResultPack[], _events: RunnerTaskEventPack[]) {
preparePendingTasks(packs)
if (!this.onTaskUpdateCalled) {
clearTimeout(this.resumeEndRunId)
this.onTaskUpdateCalled = true
this.collect(true, false, false)
this.rafCollector.resume()
}
}
endRun() {
this.rafCollector.pause()
this.onTaskUpdateCalled = false
this.collect(false, true)
}
private runCollect() {
this.collect(false, false)
}
private collect(start: boolean, end: boolean, task = true) {
if (task) {
queueMicrotask(() => {
runCollect(
start,
end,
this.summary,
search.value.trim(),
{
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
},
)
})
}
else {
runCollect(
start,
end,
this.summary,
search.value.trim(),
{
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
},
)
}
}
collectTestsTotal(
filtered: boolean,
onlyTests: boolean,
tests: File[],
filesSummary: FilteredTests,
) {
return collectTestsTotalData(filtered, onlyTests, tests, filesSummary, search.value.trim(), {
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
})
}
collapseNode(id: string) {
queueMicrotask(() => {
runCollapseNode(id)
})
}
expandNode(id: string) {
queueMicrotask(() => {
runExpandNode(id, search.value.trim(), {
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
})
})
}
collapseAllNodes() {
queueMicrotask(() => {
runCollapseAllTask()
})
}
expandAllNodes() {
queueMicrotask(() => {
runExpandAll(search.value.trim(), {
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
})
})
}
filterNodes() {
queueMicrotask(() => {
runFilter(search.value.trim(), {
failed: filter.failed,
success: filter.success,
skipped: filter.skipped,
onlyTests: filter.onlyTests,
})
})
}
}