Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): allow run individual tests/suites from the UI #6641

Merged
merged 10 commits into from
Nov 18, 2024
17 changes: 14 additions & 3 deletions packages/ui/client/components/explorer/ExplorerItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Task, TaskState } from '@vitest/runner'
import { nextTick } from 'vue'
import { hasFailedSnapshot } from '@vitest/ws-client'
import { Tooltip as VueTooltip } from 'floating-vue'
import { client, isReport, runFiles } from '~/composables/client'
import { client, isReport, runFiles, runTestOrSuite } from '~/composables/client'
import { coverageEnabled } from '~/composables/navigation'
import type { TaskTreeNodeType } from '~/composables/explorer/types'
import { explorerTree } from '~/composables/explorer'
Expand All @@ -24,6 +24,7 @@ const {
disableTaskLocation,
onItemClick,
projectNameColor,
state,
} = defineProps<{
taskId: string
name: string
Expand Down Expand Up @@ -68,12 +69,22 @@ function toggleOpen() {
}

async function onRun(task: Task) {
if (!state || state === 'todo') {
return
}

onItemClick?.(task)
if (coverageEnabled.value) {
disableCoverage.value = true
await nextTick()
}
await runFiles([task.file])

if (type === 'file') {
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved
await runFiles([task.file])
}
else {
await runTestOrSuite(task)
}
}

function updateSnapshot(task: Task) {
Expand Down Expand Up @@ -224,7 +235,7 @@ const projectNameTextColor = computed(() => {
title="Run current test"
icon="i-carbon:play-filled-alt"
text-green5
:disabled="type !== 'file'"
:disabled="!state || state === 'todo'"
@click.prevent.stop="onRun(task)"
/>
</div>
Expand Down
55 changes: 51 additions & 4 deletions packages/ui/client/composables/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { createClient, getTasks } from '@vitest/ws-client'
import type { WebSocketStatus } from '@vueuse/core'
import type { File, SerializedConfig, TaskResultPack } from 'vitest'
import type { File, SerializedConfig, Task, TaskResultPack } from 'vitest'
import { reactive as reactiveVue } from 'vue'
import { createFileTask } from '@vitest/runner/utils'
import type { BrowserRunnerState } from '../../../types'
import { ENTRY_URL, isReport } from '../../constants'
import { parseError } from '../error'
import { activeFileId } from '../params'
import { ui } from '../../composables/api'
import { createStaticClient } from './static'
import { testRunState, unhandledErrors } from './state'
import { ui } from '~/composables/api'
import { ENTRY_URL, isReport } from '~/constants'
import { explorerTree } from '~/composables/explorer'
import { isFileNode } from '~/composables/explorer/utils'
import { isSuite as isTaskSuite } from '~/utils/task'

export { ENTRY_URL, PORT, HOST, isReport } from '../../constants'
export { ENTRY_URL, PORT, HOST, isReport } from '~/constants'

export const client = (function createVitestClient() {
if (isReport) {
Expand Down Expand Up @@ -68,6 +69,21 @@ export function runAll() {
return runFiles(client.state.getFiles()/* , true */)
}

function clearTaskResult(patterns: string[], task: Task) {
userquin marked this conversation as resolved.
Show resolved Hide resolved
patterns.push(task.id)
delete task.result
const node = explorerTree.nodes.get(task.id)
if (node) {
node.state = undefined
node.duration = undefined
if (isTaskSuite(task)) {
for (const t of task.tasks) {
clearTaskResult(patterns, t)
}
}
}
}

function clearResults(useFiles: File[]) {
const map = explorerTree.nodes
useFiles.forEach((f) => {
Expand Down Expand Up @@ -101,6 +117,37 @@ export function runFiles(useFiles: File[]) {
return client.rpc.rerun(useFiles.map(i => i.filepath))
}

export function runTestOrSuite(task: Task) {
const ids: string[] = []
clearTaskResult(ids, task)

// we also need to send the parent ids: the state doesn't have the parent ids
let parent = explorerTree.nodes.get(task.id)?.parentId
while (parent) {
const node = explorerTree.nodes.get(parent)
if (node) {
const parentTask = client.state.idMap.get(node.id)
if (parentTask) {
ids.unshift(parentTask.id)
delete parentTask.result
node.state = undefined
node.duration = undefined
}
else {
break
}
parent = explorerTree.nodes.get(node.id)?.parentId
}
else {
break
}
}

explorerTree.startRun()

return client.rpc.rerunTestOrSuite(task.file.filepath, ids)
}

export function runCurrent() {
if (current.value) {
return runFiles([current.value])
Expand Down
3 changes: 3 additions & 0 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export function setup(ctx: Vitest, _server?: ViteDevServer) {
async rerun(files) {
await ctx.rerunFiles(files)
},
async rerunTestOrSuite(filename, ids) {
await ctx.rerunTestOrSuite(filename, ids)
},
getConfig() {
return ctx.getCoreWorkspaceProject().getSerializableConfig()
},
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface WebSocketHandlers {
readTestFile: (id: string) => Promise<string | null>
saveTestFile: (id: string, content: string) => Promise<void>
rerun: (files: string[]) => Promise<void>
rerunTestOrSuite: (filename: string, ids: string[]) => Promise<void>
updateSnapshot: (file?: File) => Promise<void>
getUnhandledErrors: () => unknown[]
}
Expand Down
16 changes: 16 additions & 0 deletions packages/vitest/src/node/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ export class Vitest {
}

async rerunFiles(files: string[] = this.state.getFilepaths(), trigger?: string) {
this.configOverride.testNamePattern = undefined
if (this.filenamePattern) {
const filteredFiles = await this.globTestFiles([this.filenamePattern])
files = files.filter(file => filteredFiles.some(f => f[1] === file))
Expand All @@ -688,6 +689,21 @@ export class Vitest {
await this.report('onWatcherStart', this.state.getFiles(files))
}

async rerunTestOrSuite(filename: string, ids: string[]) {
const patterns: string[] = []
for (const id of ids) {
const task = this.state.idMap.get(id)
if (task) {
patterns.push(task.name)
}
}
await this.changeNamePattern(
patterns.length ? patterns.join('|') : '',
[filename],
patterns.length ? 'rerun test or suite' : 'rerun test file',
)
}

async changeProjectName(pattern: string) {
if (pattern === '') {
delete this.configOverride.project
Expand Down
Loading