diff --git a/package.json b/package.json index 3b7016b..85b2ea1 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "test:watch": "vscode-test --watch-files src/**/*.ts --watch-files test/**/*.test.ts", "test-e2e": "vitest --root test/e2e", "test-e2e:legacy": "TEST_LEGACY=true vitest --root test/e2e", - "ecosystem-ci:build": "pnpm build", - "ecosystem-ci:test": "tsx ./scripts/ecosystem-ci.mts", + "ecosystem-ci:build": "SKIP_LEGACY=true pnpm build", + "ecosystem-ci:test": "SKIP_LEGACY=true tsx ./scripts/ecosystem-ci.mts", "typecheck": "tsc -b ./ --noEmit", "fmt": "oxfmt --check", "fmt:fix": "oxfmt" @@ -414,6 +414,6 @@ "engines": { "vscode": "^1.88.0" }, - "packageManager": "pnpm@10.11.1", + "packageManager": "pnpm@10.34.5", "pricing": "Free" } diff --git a/packages/extension/package.json b/packages/extension/package.json index f079b84..b1f4936 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -5,7 +5,6 @@ "dependencies": { "@types/picomatch": "catalog:latest", "@vitest/browser": "catalog:v3", - "@vitest/runner": "catalog:v3", "picomatch": "catalog:latest", "vitest": "catalog:v3", "vitest-vscode-shared": "workspace:*" diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 93ee93c..c38b076 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -1,4 +1,4 @@ -import type { ParsedStack, RunnerTaskResult, TestError } from 'vitest' +import type { ParsedStack, RunnerTask, RunnerTaskResult, TestError } from 'vitest' import type { ExtensionTestSpecification } from 'vitest-vscode-shared' import type { RunHandle, VitestProcessAPI } from './apiProcess' import type { ExtensionDiagnostic } from './diagnostic' @@ -7,7 +7,6 @@ import type { TestTree } from './testTree' import crypto from 'node:crypto' import path from 'node:path' import { stripVTControlCharacters } from 'node:util' -import { getTasks } from '@vitest/runner/utils' import { basename, normalize, relative } from 'pathe' import { normalizeDriveLetter } from 'vitest-vscode-shared' import * as vscode from 'vscode' @@ -17,6 +16,16 @@ import { log } from './log' import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' import { getErrorMessage, showVitestError } from './utils' +// Local copy of `getTasks` from `@vitest/runner/utils`. The extension host +// must not bundle `@vitest/runner` because the extension supports multiple +// Vitest majors and the bundled copy would not match the user's version. +function getTasks(task: RunnerTask): RunnerTask[] { + if (task.type === 'test') { + return [task] + } + return [task, ...task.tasks.flatMap(getTasks)] +} + export class TestRunner extends vscode.Disposable { protected testRun: vscode.TestRun | undefined // The request tied to the testRun diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c0423f..261fb98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,9 +280,6 @@ importers: '@vitest/browser': specifier: catalog:v3 version: 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) - '@vitest/runner': - specifier: catalog:v3 - version: 3.2.4 picomatch: specifier: catalog:latest version: 4.0.3 @@ -1365,48 +1362,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.37.0': resolution: {integrity: sha512-EZj3TurW1iLbq+7tBr++wsxwFyD+pvjMrTNRuSynDrs8J7w46cu/ZIzU/lFw7OG1/tDRDZ9nrKXxwbvIKXo2zA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.37.0': resolution: {integrity: sha512-ELXrDe1xRj+f7VpzJO2j54izMbi+Hov+kdqusXO3T1BwVEbA5sWgZrVMqkwEsj4k6Lw/obJK1SLUeNulR1D//g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.37.0': resolution: {integrity: sha512-79gMZgLD62dGmo5Xl4gaMc6NHRFj3GuxPrchHBlW54tcRSXTtb3gLh/J6Bl8nbbzSFRQGR7dkNQ8yYadXt6txQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.37.0': resolution: {integrity: sha512-QFdi9OhyWxnh975jeG490atcINXZwZb7epyNASPaT4wcodOTuDitrDgSPT8CFl8BcGOFTGZ6c3P/s8Afeg1Ngg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.37.0': resolution: {integrity: sha512-qweAj7+pLFQXfe3UU7EZiOmo+/2SWjzVZjyyTDcrZAT0E92zEKJBvYpHinUAOqipfo2Xlp8GIfq0FSb5Tmqd8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.37.0': resolution: {integrity: sha512-Lqc/0vS20qzZLw1ThpWn1hQgRqj4rM+E7PuBzrqp+wLH5lYFqieAiontGpl2pMPvJ0QrmQYav9mslHlAB5kOSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.37.0': resolution: {integrity: sha512-TnJm22+1cEcpYXzbcXS5Z9+9c+R0ronFdx5bG4OTdOL/wSpQQKzc2izgAXJ03QkP3tq7aAPhlhhxasvH3xgoUA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxfmt/binding-openharmony-arm64@0.37.0': resolution: {integrity: sha512-YLq27qMur3hPUponvV3Zr0oHxowox71j3+nc+/oCc1O+M0zFafhd6AoAoCiRrSYRW+asWhz3/UMPh0bYpimcMw==} @@ -1486,24 +1491,28 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.3': resolution: {integrity: sha512-Z03/wrqau9Bicfgb3Dbs6SYTHliELk2PM2LpG2nFd+cGupTMF5kanLEcj2vuuJLLhptNyS61rtk7SOZ+lPsTUA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.3': resolution: {integrity: sha512-iSXXZsQp08CSilff/DCTFZHSVEpEwdicV3W8idHyrByrcsRDVh9sGC3sev6d8BygSGj3vt8GvUKBPCoyMA4tgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.3': resolution: {integrity: sha512-qaj+MFudtdCv9xZo9znFvkgoajLdc+vwf0Kz5N44g+LU5XMe+IsACgn3UG7uTRlCCvhMAGXm1XlpEA5bZBrOcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.3': resolution: {integrity: sha512-U662UnMETyjT65gFmG9ma+XziENrs7BBnENi/27swZPYagubfHRirXHG2oMl+pEax2WvO7Kb9gHZmMakpYqBHQ==} @@ -1572,56 +1581,67 @@ packages: resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.53.3': resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.53.3': resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.53.3': resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.53.3': resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.53.3': resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.53.3': resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.53.3': resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.53.3': resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.53.3': resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.53.3': resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.53.3': resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} diff --git a/scripts/ecosystem-ci.mts b/scripts/ecosystem-ci.mts index 6c83bea..902908a 100644 --- a/scripts/ecosystem-ci.mts +++ b/scripts/ecosystem-ci.mts @@ -9,12 +9,16 @@ async function main() { await $`pnpm -C samples/browser i` await $`pnpm -C samples/imba i` - // setup pakcage overrides for samples used by test-e2e + // the ecosystem CI overrides every `@vitest/*` package with the latest + // version, so the legacy worker (Vitest 3) cannot be built or tested there + const unitTestArgs = + process.env.SKIP_LEGACY === 'true' ? ['--ignore', 'test/unit/collect.test.ts'] : [] + if (process.env.CI === 'true' && process.platform === 'linux') { - await $`xvfb-run pnpm test` + await $`xvfb-run pnpm test ${unitTestArgs}` await $`xvfb-run pnpm test-e2e --retry 2` } else { - await $`pnpm test` + await $`pnpm test ${unitTestArgs}` await $`pnpm test-e2e` } } diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 9308695..642e0da 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -1,4 +1,6 @@ import { readFileSync, rmSync } from 'node:fs' +import { createRequire } from 'node:module' +import { resolve } from 'node:path' import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest' import { expect } from '@playwright/test' import { test } from './utils/helper' @@ -193,8 +195,19 @@ test('watcher updates the file if there are several config files', async ({ laun }) }) +// Vitest 5 formats the values in `test.each` titles with pretty-format +// instead of loupe, so strings are no longer wrapped in quotes +function getVitestMajor(sample: string): number { + const { version } = createRequire(resolve(sample, 'package.json'))('vitest/package.json') + return Number(version.split('.')[0]) +} + test('ast collector keeps the pattern on rerun', async ({ launch }) => { const sample = 'samples/ast-collector' + const tableTestName = + getVitestMajor(sample) >= 5 + ? 'table1: returns ab when a is added b' + : "table1: returns 'ab' when 'a' is added 'b'" const { tester } = await launch({ workspacePath: sample, @@ -235,7 +248,7 @@ test('ast collector keeps the pattern on rerun', async ({ launch }) => { // table1: returns $expected when $a is added $b 'pattern|6': 'waiting', 'table1: returns 2 when 1 is added 1': 'passed', - "table1: returns 'ab' when 'a' is added 'b'": 'passed', + [tableTestName]: 'passed', }, // testing %s 'pattern|9': 'waiting', diff --git a/test/e2e/utils/assertions.ts b/test/e2e/utils/assertions.ts index 028bb94..81e5bd5 100644 --- a/test/e2e/utils/assertions.ts +++ b/test/e2e/utils/assertions.ts @@ -47,6 +47,12 @@ expect.extend({ }, async toHaveTests(item: TesterTestItem, tests: TestsTree) { const page = item.page + // the "Resolving Vitest..." item stays in the tree until every config is + // resolved, but roots are added as soon as their own config is resolved. + // Wait for it to go away, otherwise every row index shifts by one later. + await expect(page.locator('[aria-label*="Resolving Vitest..."]')).not.toBeAttached({ + timeout: 10_000, + }) const depth = Number(await item.locator.getAttribute('aria-level')) const currentIndex = Number(await item.locator.getAttribute('data-index')) diff --git a/tsdown.config.mjs b/tsdown.config.mjs index aa011f3..46ab597 100644 --- a/tsdown.config.mjs +++ b/tsdown.config.mjs @@ -2,12 +2,17 @@ import { defineConfig } from 'tsdown' +// The legacy worker bundles Vitest 3 packages. Set SKIP_LEGACY=true to leave it +// out of the build, e.g. in the Vitest ecosystem CI where every `@vitest/*` +// package is overridden with the latest version. +const skipLegacy = process.env.SKIP_LEGACY === 'true' + export default defineConfig([ { entry: { extension: './packages/extension/src/extension.ts', worker: './packages/extension/src/worker/index.ts', - workerLegacy: './packages/worker-legacy/src/index.ts', + ...(skipLegacy ? {} : { workerLegacy: './packages/worker-legacy/src/index.ts' }), workerNew: './packages/worker/src/index.ts', }, external: ['vscode'],