From 0f3350667254177a6dfe297bf74bfc5eb10d4dc9 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 5 Jun 2025 18:40:44 +0200 Subject: [PATCH 01/10] fix: rerun tests when project's setup file is changed (#8097) --- packages/vitest/src/node/watcher.ts | 25 +++++++++++++++++ test/watch/test/workspaces.test.ts | 43 ++++++++++++++++++++++++++++- test/workspaces/vitest.config.ts | 1 - 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/vitest/src/node/watcher.ts b/packages/vitest/src/node/watcher.ts index 8e5219a7cf7d..d80c99a1b752 100644 --- a/packages/vitest/src/node/watcher.ts +++ b/packages/vitest/src/node/watcher.ts @@ -140,6 +140,27 @@ export class VitestWatcher { } } + private handleSetupFile(filepath: string) { + let isSetupFile: boolean = false + + this.vitest.projects.forEach((project) => { + if (!project.config.setupFiles.includes(filepath)) { + return + } + + this.vitest.state.filesMap.forEach((files) => { + files.forEach((file) => { + if (file.projectName === project.name) { + isSetupFile = true + this.changedTests.add(file.filepath) + } + }) + }) + }) + + return isSetupFile + } + /** * @returns A value indicating whether rerun is needed (changedTests was mutated) */ @@ -153,6 +174,10 @@ export class VitestWatcher { return true } + if (this.handleSetupFile(filepath)) { + return true + } + const projects = this.vitest.projects.filter((project) => { const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph return moduleGraph.getModulesByFile(filepath)?.size diff --git a/test/watch/test/workspaces.test.ts b/test/watch/test/workspaces.test.ts index 6e202e4c6eb8..52c416cb5c9d 100644 --- a/test/watch/test/workspaces.test.ts +++ b/test/watch/test/workspaces.test.ts @@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url' import { dirname, resolve } from 'pathe' import { afterAll, afterEach, expect, it } from 'vitest' -import { runVitestCli } from '../../test-utils' +import { runInlineTests, runVitestCli } from '../../test-utils' const file = fileURLToPath(import.meta.url) const dir = dirname(file) @@ -125,3 +125,44 @@ it('adding a new test file matching project specific config triggers re-run', as expect(vitest.stdout).not.include('|node|') expect(vitest.stdout).not.include('|happy-dom|') }) + +it('editing a setup file inside the project reruns tests', async () => { + const { fs, vitest } = await runInlineTests({ + 'setupFile.js': '', + 'project-1/basic.test.js': `test("[p1] reruns")`, + 'project-2/basic.test.js': `test("[p2] doesn\'t rerun")`, + 'vitest.config.js': { + test: { + projects: [ + { + test: { + name: 'p1', + include: ['./project-1/basic.test.js'], + setupFiles: ['./setupFile.js'], + globals: true, + }, + }, + { + test: { + name: 'p2', + include: ['./project-2/basic.test.js'], + globals: true, + }, + }, + ], + }, + }, + }, { watch: true }) + + await vitest.waitForStdout('Waiting for file changes') + expect(vitest.stdout).toContain('[p1] reruns') + expect(vitest.stdout).toContain('[p2] doesn\'t rerun') + + fs.editFile('./setupFile.js', () => '// ---edit') + + vitest.resetOutput() + await vitest.waitForStdout('Test Files 1 passed') + + expect(vitest.stdout).toContain('[p1] reruns') + expect(vitest.stdout).not.toContain('[p2] doesn\'t rerun') +}) diff --git a/test/workspaces/vitest.config.ts b/test/workspaces/vitest.config.ts index 953ea057fa19..7a885957313e 100644 --- a/test/workspaces/vitest.config.ts +++ b/test/workspaces/vitest.config.ts @@ -23,7 +23,6 @@ export default defineConfig({ globalConfigValue: true, }, projects: [ - 'space_2', './space_*/vitest.config.ts', './space_1/*.config.ts', From c69be1fc1ab5d413661a7bead5bffd7147f4aa97 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 5 Jun 2025 19:26:25 +0200 Subject: [PATCH 02/10] feat(ui): show test annotations and metadata in the test report tab (#8093) --- packages/ui/client/auto-imports.d.ts | 11 + packages/ui/client/components.d.ts | 2 + .../components/AnnotationAttachmentImage.vue | 34 +++ packages/ui/client/components/FileDetails.vue | 41 +++- .../components/explorer/ExplorerItem.vue | 24 +- .../ui/client/components/views/ViewEditor.vue | 37 +--- .../ui/client/components/views/ViewReport.vue | 116 ++-------- .../components/views/ViewReportError.vue | 18 +- .../components/views/ViewTestReport.vue | 206 ++++++++++++++++++ packages/ui/client/composables/attachments.ts | 27 +++ packages/ui/client/composables/codemirror.ts | 35 ++- packages/ui/client/composables/error.ts | 60 ++++- .../ui/client/composables/explorer/utils.ts | 1 + packages/ui/client/composables/navigation.ts | 6 +- packages/ui/client/composables/params.ts | 3 + packages/ui/client/composables/screenshot.ts | 34 +++ packages/ui/client/utils/task.ts | 17 ++ test/core/test/basic.test.ts | 10 +- test/ui/fixtures/annotated.test.ts | 1 + test/ui/test/html-report.spec.ts | 74 ++++++- test/ui/test/ui.spec.ts | 76 ++++++- 21 files changed, 649 insertions(+), 184 deletions(-) create mode 100644 packages/ui/client/components/AnnotationAttachmentImage.vue create mode 100644 packages/ui/client/components/views/ViewTestReport.vue create mode 100644 packages/ui/client/composables/attachments.ts create mode 100644 packages/ui/client/composables/screenshot.ts diff --git a/packages/ui/client/auto-imports.d.ts b/packages/ui/client/auto-imports.d.ts index 021bd219e2d0..988fe2d8dda1 100644 --- a/packages/ui/client/auto-imports.d.ts +++ b/packages/ui/client/auto-imports.d.ts @@ -12,6 +12,7 @@ declare global { const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] const calcExternalLabels: typeof import('./composables/module-graph')['calcExternalLabels'] const codemirrorRef: typeof import('./composables/codemirror')['codemirrorRef'] + const columnNumber: typeof import('./composables/params')['columnNumber'] const computed: typeof import('vue')['computed'] const computedAsync: typeof import('@vueuse/core')['computedAsync'] const computedEager: typeof import('@vueuse/core')['computedEager'] @@ -49,6 +50,7 @@ declare global { const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] const effectScope: typeof import('vue')['effectScope'] const extendRef: typeof import('@vueuse/core')['extendRef'] + const getAttachmentUrl: typeof import('./composables/attachments')['getAttachmentUrl'] const getCurrentBrowserIframe: typeof import('./composables/api')['getCurrentBrowserIframe'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] @@ -61,13 +63,16 @@ declare global { const injectLocal: typeof import('@vueuse/core')['injectLocal'] const isDark: typeof import('./composables/dark')['isDark'] const isDefined: typeof import('@vueuse/core')['isDefined'] + const isExternalAttachment: typeof import('./composables/attachments')['isExternalAttachment'] const isProxy: typeof import('vue')['isProxy'] const isReactive: typeof import('vue')['isReactive'] const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] + const isTestFile: typeof import('./composables/error')['isTestFile'] const lineNumber: typeof import('./composables/params')['lineNumber'] const mainSizes: typeof import('./composables/navigation')['mainSizes'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] + const mapLeveledTaskStacks: typeof import('./composables/error')['mapLeveledTaskStacks'] const markRaw: typeof import('vue')['markRaw'] const navigateTo: typeof import('./composables/navigation')['navigateTo'] const nextTick: typeof import('vue')['nextTick'] @@ -94,6 +99,7 @@ declare global { const onUpdated: typeof import('vue')['onUpdated'] const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] const openInEditor: typeof import('./composables/error')['openInEditor'] + const openScreenshot: typeof import('./composables/screenshot')['openScreenshot'] const openedTreeItems: typeof import('./composables/navigation')['openedTreeItems'] const panels: typeof import('./composables/navigation')['panels'] const params: typeof import('./composables/params')['params'] @@ -120,19 +126,23 @@ declare global { const resolveComponent: typeof import('vue')['resolveComponent'] const resolveRef: typeof import('@vueuse/core')['resolveRef'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] + const sanitizeFilePath: typeof import('./composables/attachments')['sanitizeFilePath'] const selectedTest: typeof import('./composables/params')['selectedTest'] const setIframeViewport: typeof import('./composables/api')['setIframeViewport'] const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] const shouldOpenInEditor: typeof import('./composables/error')['shouldOpenInEditor'] + const showAnnotationSource: typeof import('./composables/codemirror')['showAnnotationSource'] const showCoverage: typeof import('./composables/navigation')['showCoverage'] const showDashboard: typeof import('./composables/navigation')['showDashboard'] const showLine: typeof import('./composables/codemirror')['showLine'] + const showLocationSource: typeof import('./composables/codemirror')['showLocationSource'] const showNavigationPanel: typeof import('./composables/navigation')['showNavigationPanel'] const showReport: typeof import('./composables/navigation')['showReport'] const showRightPanel: typeof import('./composables/navigation')['showRightPanel'] const showSource: typeof import('./composables/codemirror')['showSource'] + const showTaskSource: typeof import('./composables/codemirror')['showTaskSource'] const syncRef: typeof import('@vueuse/core')['syncRef'] const syncRefs: typeof import('@vueuse/core')['syncRefs'] const templateRef: typeof import('@vueuse/core')['templateRef'] @@ -279,6 +289,7 @@ declare global { const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] + const useScreenshot: typeof import('./composables/screenshot')['useScreenshot'] const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] const useScroll: typeof import('@vueuse/core')['useScroll'] const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] diff --git a/packages/ui/client/components.d.ts b/packages/ui/client/components.d.ts index 0a7fe8fa8abb..8bd55fe18a44 100644 --- a/packages/ui/client/components.d.ts +++ b/packages/ui/client/components.d.ts @@ -7,6 +7,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + AnnotationAttachmentImage: typeof import('./components/AnnotationAttachmentImage.vue')['default'] BrowserIframe: typeof import('./components/BrowserIframe.vue')['default'] CodeMirrorContainer: typeof import('./components/CodeMirrorContainer.vue')['default'] ConnectionOverlay: typeof import('./components/ConnectionOverlay.vue')['default'] @@ -38,5 +39,6 @@ declare module 'vue' { ViewModuleGraph: typeof import('./components/views/ViewModuleGraph.vue')['default'] ViewReport: typeof import('./components/views/ViewReport.vue')['default'] ViewReportError: typeof import('./components/views/ViewReportError.vue')['default'] + ViewTestReport: typeof import('./components/views/ViewTestReport.vue')['default'] } } diff --git a/packages/ui/client/components/AnnotationAttachmentImage.vue b/packages/ui/client/components/AnnotationAttachmentImage.vue new file mode 100644 index 000000000000..6196bf21c202 --- /dev/null +++ b/packages/ui/client/components/AnnotationAttachmentImage.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/ui/client/components/FileDetails.vue b/packages/ui/client/components/FileDetails.vue index db1f236ade49..74fb43d83fea 100644 --- a/packages/ui/client/components/FileDetails.vue +++ b/packages/ui/client/components/FileDetails.vue @@ -1,4 +1,5 @@ @@ -174,7 +190,7 @@ const projectNameTextColor = computed(() => { {{ current.file.projectName }}
- {{ current?.name }} + {{ testTitle }}
{ :file="current" data-testid="console" /> - + +
diff --git a/packages/ui/client/components/explorer/ExplorerItem.vue b/packages/ui/client/components/explorer/ExplorerItem.vue index d813df041973..06b61fd99c58 100644 --- a/packages/ui/client/components/explorer/ExplorerItem.vue +++ b/packages/ui/client/components/explorer/ExplorerItem.vue @@ -4,11 +4,12 @@ import type { TaskTreeNodeType } from '~/composables/explorer/types' import { Tooltip as VueTooltip } from 'floating-vue' import { nextTick } from 'vue' import { client, isReport, runFiles, runTask } from '~/composables/client' -import { showSource } from '~/composables/codemirror' +import { showTaskSource } from '~/composables/codemirror' import { explorerTree } from '~/composables/explorer' import { hasFailedSnapshot } from '~/composables/explorer/collector' import { escapeHtml, highlightRegex } from '~/composables/explorer/state' import { coverageEnabled } from '~/composables/navigation' +import { getProjectTextColor } from '~/utils/task' // TODO: better handling of "opened" - it means to forcefully open the tree item and set in TasksList right now const { @@ -149,26 +150,11 @@ function showDetails() { onItemClick?.(t) } else { - showSource(t) + showTaskSource(t) } } -const projectNameTextColor = computed(() => { - switch (projectNameColor) { - case 'blue': - case 'green': - case 'magenta': - case 'black': - case 'red': - return 'white' - - case 'yellow': - case 'cyan': - case 'white': - default: - return 'black' - } -}) +const projectNameTextColor = computed(() => getProjectTextColor(projectNameColor))