Skip to content

Commit ae5ec03

Browse files
authored
fix: properly re-evaluate actual modules of mocked external (#9898)
1 parent a2bfc0c commit ae5ec03

File tree

6 files changed

+166
-1
lines changed

6 files changed

+166
-1
lines changed

packages/vitest/src/runtime/moduleRunner/startVitestModuleRunner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export function startVitestModuleRunner(options: ContextModuleRunnerOptions): Vi
135135

136136
// if module is invalidated, the worker will be recreated,
137137
// so cached is always true in a single worker
138-
if (options?.cached) {
138+
if (!isImportActual && options?.cached) {
139139
return { cache: true }
140140
}
141141

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/cli/deps/dep-simple/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default 'test-dep-simple'
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "test-dep-simple",
3+
"type": "module",
4+
"private": true,
5+
"exports": "./index.js"
6+
}

test/cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"obug": "^2.1.1",
3131
"playwright": "catalog:",
3232
"test-dep-invalid": "link:./deps/dep-invalid",
33+
"test-dep-simple": "file:./deps/dep-simple",
3334
"tinyspy": "catalog:",
3435
"typescript": "catalog:",
3536
"unplugin-swc": "^1.5.9",

test/cli/test/mocking.test.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,152 @@ test('mock works without loading original', () => {
391391
}
392392
`)
393393
})
394+
395+
test.for([
396+
'node',
397+
'playwright',
398+
'webdriverio',
399+
])('repeating mock, importActual, and resetModules (%s)', async (mode) => {
400+
const { stderr, errorTree } = await runInlineTests({
401+
// external
402+
'./external.test.ts': `
403+
import { expect, test, vi } from "vitest"
404+
405+
test("external", async () => {
406+
vi.doMock(import("test-dep-simple"), async (importActual) => {
407+
const lib = await importActual();
408+
return lib;
409+
})
410+
const lib1: any = await import("test-dep-simple")
411+
expect(lib1.default).toBe("test-dep-simple")
412+
413+
vi.resetModules();
414+
vi.doMock(import("test-dep-simple"), async (importActual) => {
415+
const lib = await importActual();
416+
return lib;
417+
})
418+
const lib2: any = await import("test-dep-simple")
419+
expect(lib2.default).toBe("test-dep-simple")
420+
expect.soft(lib1 !== lib2).toBe(true)
421+
422+
vi.resetModules();
423+
vi.doMock(import("test-dep-simple"), async () => ({ mocked: true }));
424+
const lib3 = await import("test-dep-simple");
425+
expect(lib3).toMatchObject({ mocked: true })
426+
427+
const lib4 = await vi.importActual("test-dep-simple");
428+
expect(lib4.default).toBe("test-dep-simple")
429+
const lib5 = await vi.importActual("test-dep-simple");
430+
expect(lib4).toBe(lib5)
431+
});
432+
`,
433+
// builtin module
434+
'./builtin.test.ts': `
435+
import { expect, test, vi } from "vitest"
436+
437+
test("builtin", async () => {
438+
vi.doMock(import("node:path"), async (importActual) => {
439+
const lib = await importActual();
440+
return lib;
441+
})
442+
const lib1: any = await import("node:path")
443+
expect(lib1).toHaveProperty('join')
444+
445+
vi.resetModules();
446+
vi.doMock(import("node:path"), async (importActual) => {
447+
const lib = await importActual();
448+
return lib;
449+
})
450+
const lib2: any = await import("node:path")
451+
expect(lib2).toHaveProperty('join')
452+
expect.soft(lib1 !== lib2).toBe(true)
453+
454+
vi.resetModules();
455+
vi.doMock(import("node:path"), async () => ({ mocked: true }));
456+
const lib3 = await import("node:path");
457+
expect(lib3).toMatchObject({ mocked: true })
458+
459+
const lib4 = await vi.importActual("node:path");
460+
expect(lib4).toHaveProperty('join')
461+
const lib5 = await vi.importActual("node:path");
462+
expect(lib4).toBe(lib5)
463+
});
464+
`,
465+
// local module
466+
'./local.test.ts': `
467+
import { expect, test, vi } from "vitest"
468+
469+
test("local", async () => {
470+
vi.doMock(import("./local.js"), async (importActual) => {
471+
const lib = await importActual();
472+
return lib;
473+
})
474+
const lib1: any = await import("./local.js")
475+
expect(lib1).toHaveProperty('local')
476+
477+
vi.resetModules();
478+
vi.doMock(import("./local.js"), async (importActual) => {
479+
const lib = await importActual();
480+
return lib;
481+
})
482+
const lib2: any = await import("./local.js")
483+
expect(lib2).toHaveProperty('local')
484+
expect.soft(lib1 !== lib2).toBe(true)
485+
486+
vi.resetModules();
487+
vi.doMock(import("./local.js"), async () => ({ mocked: true }));
488+
const lib3 = await import("./local.js");
489+
expect(lib3).toMatchObject({ mocked: true })
490+
491+
const lib4 = await vi.importActual("./local.js");
492+
expect(lib4).toHaveProperty('local')
493+
const lib5 = await vi.importActual("./local.js");
494+
expect(lib4).toBe(lib5)
495+
});
496+
`,
497+
'./local.js': `export const local = 'local'`,
498+
}, modeToConfig(mode))
499+
500+
if (mode === 'webdriverio' || mode === 'playwright') {
501+
// browser mode doesn't support resetModules nor node builtin
502+
expect(errorTree()).toMatchInlineSnapshot(`
503+
{
504+
"builtin.test.ts": {
505+
"builtin": [
506+
"Cannot convert a Symbol value to a string",
507+
],
508+
},
509+
"external.test.ts": {
510+
"external": [
511+
"expected false to be true // Object.is equality",
512+
"expected { default: 'test-dep-simple', …(1) } to match object { mocked: true }
513+
(1 matching property omitted from actual)",
514+
],
515+
},
516+
"local.test.ts": {
517+
"local": [
518+
"expected false to be true // Object.is equality",
519+
"expected { local: 'local', …(1) } to match object { mocked: true }
520+
(1 matching property omitted from actual)",
521+
],
522+
},
523+
}
524+
`)
525+
return
526+
}
527+
528+
expect(stderr).toMatchInlineSnapshot(`""`)
529+
expect(errorTree()).toMatchInlineSnapshot(`
530+
{
531+
"builtin.test.ts": {
532+
"builtin": "passed",
533+
},
534+
"external.test.ts": {
535+
"external": "passed",
536+
},
537+
"local.test.ts": {
538+
"local": "passed",
539+
},
540+
}
541+
`)
542+
})

0 commit comments

Comments
 (0)