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 @@ + + + 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) + }) })