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

fix(vitest): invalidate setupFiles with extension-less path #6749

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/vitest/src/runtime/runners/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ export class NodeBenchmarkRunner implements VitestRunner {
return await import('tinybench')
}

importFile(filepath: string, source: VitestRunnerImportSource): unknown {
async importFile(filepath: string, source: VitestRunnerImportSource): Promise<unknown> {
if (source === 'setup') {
getWorkerState().moduleCache.delete(filepath)
const resolved = await this.__vitest_executor.resolveUrl(filepath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be resolved here even without the extension?

resolved.setupFiles = toArray(resolved.setupFiles || []).map(file =>

This worked before without problems

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine it's working for most of the case, but the issue was when it's extension-less relative path. For example, setupFiles: ['./setup.ts'] works but setupFiles: ['./setup'] fails, which was OP's repro.

Alternatively, we can use Vite server resolver during resolveConfig or sometime later if we restructure a bit, but I thought there might be a reason not to bring that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am saying that it shouldn't fail even without the extension

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should fix it for sure, but I don't think this was a regression and extension-less relative path has never been handled with proper invalidation. I'll think about the solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any way, I don't think the current fix in this PR is the correct one. In the very least it seems weird to me that it processes the same setup file in every test (which means it processes the same value multiple times). The setup file ID should be resolved already at the config stage. The resolvePath in resolveConfig is supposed to be able to resolve any JS paths even without an extension as far as I understand - why doesn't it? 🤔

getWorkerState().moduleCache.delete(resolved[1])
}
return this.__vitest_executor.executeId(filepath)
}
Expand Down
5 changes: 3 additions & 2 deletions packages/vitest/src/runtime/runners/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ export class VitestTestRunner implements VitestRunner {

constructor(public config: SerializedConfig) {}

importFile(filepath: string, source: VitestRunnerImportSource): unknown {
async importFile(filepath: string, source: VitestRunnerImportSource): Promise<unknown> {
if (source === 'setup') {
this.workerState.moduleCache.delete(filepath)
const resolved = await this.__vitest_executor.resolveUrl(filepath)
this.workerState.moduleCache.delete(resolved[1])
}
return this.__vitest_executor.executeId(filepath)
}
Expand Down
9 changes: 9 additions & 0 deletions test/config/fixtures/setup-file-no-isolate/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { beforeEach } from 'vitest';

export type MyContext = {
testOk: boolean
}

beforeEach<MyContext>((ctx) => {
ctx.testOk = true;
});
AriPerkkio marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions test/config/fixtures/setup-file-no-isolate/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
setupFiles: ['./setup'],
isolate: false,
fileParallelism: false,
},
});
6 changes: 6 additions & 0 deletions test/config/fixtures/setup-file-no-isolate/x.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { expect, test } from "vitest"
import type { MyContext } from "./setup"

test<MyContext>("x", (ctx) => {
expect(ctx.testOk).toBe(true)
})
6 changes: 6 additions & 0 deletions test/config/fixtures/setup-file-no-isolate/y.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { expect, test } from "vitest"
import type { MyContext } from "./setup"

test<MyContext>("y", (ctx) => {
expect(ctx.testOk).toBe(true)
})
11 changes: 11 additions & 0 deletions test/config/test/setup-file-no-isolate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import path from 'node:path'
import { expect, test } from 'vitest'
import { runVitest } from '../../test-utils'

test('re-import setupFiles with no isolate', async () => {
const root = path.resolve(import.meta.dirname, '../fixtures/setup-file-no-isolate')

const { stderr, exitCode } = await runVitest({ root })
expect(stderr).toBe('')
expect(exitCode).toBe(0)
})