diff --git a/docs/api/index.md b/docs/api/index.md
index 42d956d45..d7b43605f 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -1585,6 +1585,10 @@ Vitest provides utility functions to help you out through it's **vi** helper. Yo
Removes all timers that are scheduled to run. These timers will never run in the future.
+### vi.dynamicImportSettled
+
+ Wait for all imports to load. Useful, if you have a synchronous call that starts importing a module, that you cannot wait otherwise.
+
### vi.fn
- **Type:** `(fn: Function) => CallableMockInstance`
diff --git a/examples/vue/components/AsAsync.vue b/examples/vue/components/AsAsync.vue
new file mode 100644
index 000000000..9ff17552e
--- /dev/null
+++ b/examples/vue/components/AsAsync.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
Async component
+
+
+
diff --git a/examples/vue/test/as-async.test.ts b/examples/vue/test/as-async.test.ts
new file mode 100644
index 000000000..276d6e16a
--- /dev/null
+++ b/examples/vue/test/as-async.test.ts
@@ -0,0 +1,15 @@
+import { flushPromises, mount } from '@vue/test-utils'
+import AsAsync from '../components/AsAsync.vue'
+
+test('mount component', async () => {
+ expect(AsAsync).toBeTruthy()
+
+ const wrapper = mount(AsAsync)
+
+ await wrapper.find('button').trigger('click')
+
+ await flushPromises() // start loading, so vitest started loading
+ await vi.dynamicImportSettled()
+
+ expect(wrapper.html()).toContain('1 x 2 = 2')
+})
diff --git a/examples/vue/tsconfig.json b/examples/vue/tsconfig.json
index 9b9d84243..10b0483b0 100644
--- a/examples/vue/tsconfig.json
+++ b/examples/vue/tsconfig.json
@@ -2,6 +2,8 @@
"compilerOptions": {
"target": "esnext",
"module": "esnext",
- "moduleResolution": "node"
+ "moduleResolution": "node",
+ "jsx": "preserve",
+ "types": ["vitest/globals"]
}
}
diff --git a/packages/vitest/src/integrations/vi.ts b/packages/vitest/src/integrations/vi.ts
index 1e6299765..9585c00f4 100644
--- a/packages/vitest/src/integrations/vi.ts
+++ b/packages/vitest/src/integrations/vi.ts
@@ -1,7 +1,7 @@
import type { FakeTimerInstallOpts } from '@sinonjs/fake-timers'
import { parseStacktrace } from '../utils/source-map'
import type { VitestMocker } from '../runtime/mocker'
-import { getWorkerState, resetModules } from '../utils'
+import { getWorkerState, resetModules, setTimeout } from '../utils'
import { FakeTimers } from './mock/timers'
import type { EnhancedSpy, MaybeMocked, MaybeMockedDeep } from './spy'
import { fn, isMockFunction, spies, spyOn } from './spy'
@@ -234,6 +234,24 @@ class VitestUtils {
resetModules()
return this
}
+
+ /**
+ * Wait for all imports to load.
+ * Useful, if you have a synchronous call that starts
+ * importing a module, that you cannot wait otherwise.
+ */
+ public async dynamicImportSettled() {
+ const state = getWorkerState()
+ const promises: Promise[] = []
+ for (const mod of state.moduleCache.values()) {
+ if (!mod.exports && mod.promise)
+ promises.push(mod.promise)
+ }
+ await Promise.all(promises)
+ // wait until the end of the loop, so `.then` on modules called,
+ // like in import('./example').then(...)
+ await new Promise(resolve => setTimeout(resolve))
+ }
}
export const vitest = new VitestUtils()
diff --git a/test/core/test/vi.spec.ts b/test/core/test/vi.spec.ts
index 2bb4f7465..7cde61f1b 100644
--- a/test/core/test/vi.spec.ts
+++ b/test/core/test/vi.spec.ts
@@ -28,4 +28,15 @@ describe('testing vi utils', () => {
const v2 = await import('vitest')
expect(v1).toBe(v2)
})
+
+ test('loads unloaded module', async () => {
+ let mod: any
+ import('../src/timeout').then(m => mod = m)
+
+ expect(mod).toBeUndefined()
+
+ await vi.dynamicImportSettled()
+
+ expect(mod.timeout).toBe(100)
+ })
})