diff --git a/packages/browser/src/client/tester/msw.ts b/packages/browser/src/client/tester/msw.ts index 1cdc3b88ef20..0897ef998e05 100644 --- a/packages/browser/src/client/tester/msw.ts +++ b/packages/browser/src/client/tester/msw.ts @@ -27,6 +27,10 @@ export function createModuleMocker() { http.get(/.+/, async ({ request }) => { const path = cleanQuery(request.url.slice(location.origin.length)) if (!mocks.has(path)) { + if (path.includes('/deps/')) { + return fetch(bypass(request)) + } + return passthrough() } @@ -128,6 +132,20 @@ function passthrough() { }) } +function bypass(request: Request) { + const clonedRequest = request.clone() + clonedRequest.headers.set('x-msw-intention', 'bypass') + const cacheControl = clonedRequest.headers.get('cache-control') + if (cacheControl) { + clonedRequest.headers.set( + 'cache-control', + // allow reinvalidation of the cache so mocks can be updated + cacheControl.replace(', immutable', ''), + ) + } + return clonedRequest +} + const postfixRE = /[?#].*$/ function cleanUrl(url: string): string { return url.replace(postfixRE, '') diff --git a/packages/browser/src/node/plugin.ts b/packages/browser/src/node/plugin.ts index 8838f2a7f1c0..e4dc57e83cac 100644 --- a/packages/browser/src/node/plugin.ts +++ b/packages/browser/src/node/plugin.ts @@ -41,7 +41,7 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => { }) // eslint-disable-next-line prefer-arrow-callback server.middlewares.use(async function vitestBrowserMode(req, res, next) { - if (!req.url) { + if (!req.url || !browserServer.provider) { return next() } const url = new URL(req.url, 'http://localhost') diff --git a/test/browser/cjs-lib/lib.d.ts b/test/browser/cjs-lib/lib.d.ts new file mode 100644 index 000000000000..58379c6bce6b --- /dev/null +++ b/test/browser/cjs-lib/lib.d.ts @@ -0,0 +1 @@ +export function lib(): string diff --git a/test/browser/cjs-lib/lib.mjs b/test/browser/cjs-lib/lib.mjs new file mode 100644 index 000000000000..9f0f245d5627 --- /dev/null +++ b/test/browser/cjs-lib/lib.mjs @@ -0,0 +1,3 @@ +export function lib() { + return 'original' +} diff --git a/test/browser/cjs-lib/package.json b/test/browser/cjs-lib/package.json index 7ea91ce84286..c38344798898 100644 --- a/test/browser/cjs-lib/package.json +++ b/test/browser/cjs-lib/package.json @@ -2,6 +2,10 @@ "name": "@vitest/cjs-lib", "type": "commonjs", "exports": { - "default": "./index.js" + "./lib": { + "types": "./lib.d.ts", + "default": "./lib.mjs" + }, + ".": "./index.js" } } diff --git a/test/browser/fixtures/mocking-watch/1_mocked-on-watch-change.test.ts b/test/browser/fixtures/mocking-watch/1_mocked-on-watch-change.test.ts new file mode 100644 index 000000000000..843efa538cfc --- /dev/null +++ b/test/browser/fixtures/mocking-watch/1_mocked-on-watch-change.test.ts @@ -0,0 +1,12 @@ +import { lib } from '@vitest/cjs-lib/lib' +import { vi, test, expect } from 'vitest' + +vi.mock(import('@vitest/cjs-lib/lib'), () => { + return { + lib: vi.fn(() => 'mocked') + } +}) + +test('mocks correctly', () => { + expect(lib()).toBe('mocked') +}) diff --git a/test/browser/fixtures/mocking-watch/2_not-mocked-import.test.ts b/test/browser/fixtures/mocking-watch/2_not-mocked-import.test.ts new file mode 100644 index 000000000000..6e12fb874c76 --- /dev/null +++ b/test/browser/fixtures/mocking-watch/2_not-mocked-import.test.ts @@ -0,0 +1,6 @@ +import { lib } from '@vitest/cjs-lib/lib' +import { test, expect } from 'vitest' + +test('not mocked', () => { + expect(lib()).toBe('original') +}) diff --git a/test/browser/fixtures/mocking-watch/vitest.config.ts b/test/browser/fixtures/mocking-watch/vitest.config.ts new file mode 100644 index 000000000000..ebe6e47aaef6 --- /dev/null +++ b/test/browser/fixtures/mocking-watch/vitest.config.ts @@ -0,0 +1,22 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vitest/config' + +const provider = process.env.PROVIDER || 'playwright' +const name = + process.env.BROWSER || (provider === 'playwright' ? 'chromium' : 'chrome') + +export default defineConfig({ + optimizeDeps: { + include: ['@vitest/cjs-lib', '@vitest/cjs-lib/lib'], + }, + cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), + test: { + browser: { + fileParallelism: false, + enabled: true, + provider, + name, + headless: true, + }, + }, +}) diff --git a/test/browser/fixtures/mocking/vitest.config.ts b/test/browser/fixtures/mocking/vitest.config.ts index 9fbb1e0da80f..3620bdc48ef4 100644 --- a/test/browser/fixtures/mocking/vitest.config.ts +++ b/test/browser/fixtures/mocking/vitest.config.ts @@ -1,3 +1,4 @@ +import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' const provider = process.env.PROVIDER || 'playwright' @@ -9,6 +10,7 @@ export default defineConfig({ include: ['@vitest/cjs-lib'], needsInterop: ['@vitest/cjs-lib'], }, + cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), test: { browser: { enabled: true, diff --git a/test/browser/package.json b/test/browser/package.json index acfb69417c9c..db96674dfbe6 100644 --- a/test/browser/package.json +++ b/test/browser/package.json @@ -10,6 +10,7 @@ "test:safaridriver": "PROVIDER=webdriverio BROWSER=safari pnpm run test:unit", "test-fixtures": "vitest", "test-mocking": "vitest --root ./fixtures/mocking", + "test-mocking-watch": "vitest --root ./fixtures/mocking-watch", "test-snapshots": "vitest --root ./fixtures/update-snapshot", "coverage": "vitest --coverage.enabled --coverage.provider=istanbul --browser.headless=yes", "test:browser:preview": "PROVIDER=preview vitest", diff --git a/test/browser/specs/mocking.test.ts b/test/browser/specs/mocking.test.ts index fc5eb8c58d75..040a7e764066 100644 --- a/test/browser/specs/mocking.test.ts +++ b/test/browser/specs/mocking.test.ts @@ -1,5 +1,5 @@ -import { expect, onTestFailed, test } from 'vitest' -import { runVitest } from '../../test-utils' +import { expect, onTestFailed, onTestFinished, test } from 'vitest' +import { editFile, runVitest } from '../../test-utils' test.each([true, false])('mocking works correctly - isolated %s', async (isolate) => { const result = await runVitest({ @@ -26,3 +26,29 @@ test.each([true, false])('mocking works correctly - isolated %s', async (isolate expect(result.stdout).toContain('mocked-do-mock-factory.test.ts') expect(result.exitCode).toBe(0) }) + +test('mocking dependency correctly invalidates it on rerun', async () => { + const { vitest, ctx } = await runVitest({ + root: 'fixtures/mocking-watch', + watch: true, + }) + onTestFinished(async () => { + await ctx.close() + await ctx.closingPromise + }) + + await vitest.waitForStdout('Waiting for file changes...') + + expect(vitest.stderr).toBe('') + expect(vitest.stdout).toContain('1_mocked-on-watch-change.test.ts') + expect(vitest.stdout).toContain('2_not-mocked-import.test.ts') + + vitest.resetOutput() + editFile('./fixtures/mocking-watch/1_mocked-on-watch-change.test.ts', content => `${content}\n`) + + await vitest.waitForStdout('Waiting for file changes...') + + expect(vitest.stderr).toBe('') + expect(vitest.stdout).toContain('1_mocked-on-watch-change.test.ts') + expect(vitest.stdout).not.toContain('2_not-mocked-import.test.ts') +})