[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix(browser): fix browser mock factory event race condition (#6530)

authored by

Hiroshi Ogawa and committed by
GitHub
(Sep 25, 2024, 10:33 AM +0200) f131f93b b553c7d6

+49 -20
+2
packages/utils/src/index.ts
··· 51 51 SerializedError, 52 52 TestError, 53 53 } from './types' 54 + 55 + export { nanoid } from './nanoid'
+12
packages/utils/src/nanoid.ts
··· 1 + // port from nanoid 2 + // https://github.com/ai/nanoid 3 + const urlAlphabet 4 + = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' 5 + export function nanoid(size = 21): string { 6 + let id = '' 7 + let i = size 8 + while (i--) { 9 + id += urlAlphabet[(Math.random() * 64) | 0] 10 + } 11 + return id 12 + }
+3
packages/browser/src/client/channel.ts
··· 39 39 40 40 export interface IframeMockFactoryRequestEvent { 41 41 type: 'mock-factory:request' 42 + eventId: string 42 43 id: string 43 44 } 44 45 45 46 export interface IframeMockFactoryResponseEvent { 46 47 type: 'mock-factory:response' 48 + eventId: string 47 49 exports: string[] 48 50 } 49 51 50 52 export interface IframeMockFactoryErrorEvent { 51 53 type: 'mock-factory:error' 54 + eventId: string 52 55 error: any 53 56 } 54 57
+1 -14
packages/vitest/src/utils/base.ts
··· 1 1 import type { Arrayable, Nullable } from '../types/general' 2 2 3 - export { notNullish, getCallLastIndex } from '@vitest/utils' 3 + export { notNullish, getCallLastIndex, nanoid } from '@vitest/utils' 4 4 5 5 export interface GlobalConstructors { 6 6 Object: ObjectConstructor ··· 202 202 `^${pattern.split('*').map(escapeRegExp).join('.*')}$`, 203 203 'i', 204 204 ) 205 - } 206 - 207 - // port from nanoid 208 - // https://github.com/ai/nanoid 209 - const urlAlphabet 210 - = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' 211 - export function nanoid(size = 21) { 212 - let id = '' 213 - let i = size 214 - while (i--) { 215 - id += urlAlphabet[(Math.random() * 64) | 0] 216 - } 217 - return id 218 205 }
+13
test/browser/fixtures/mocking/mocked-factory.test.ts
··· 1 1 import { expect, test, vi } from 'vitest' 2 2 import { calculator, mocked } from './src/mocks_factory' 3 + import factoryMany from './src/mocks_factory_many' 3 4 4 5 vi.mock(import('./src/mocks_factory'), () => { 5 6 return { ··· 8 9 } 9 10 }) 10 11 12 + vi.mock(import('./src/mocks_factory_many_dep1'), () => ({ 13 + dep1: "dep1-mocked" 14 + })) 15 + vi.mock(import('./src/mocks_factory_many_dep2'), () => ({ 16 + dep2: "dep2-mocked" 17 + })) 18 + 11 19 test('adds', () => { 12 20 expect(mocked).toBe(true) 13 21 expect(calculator('plus', 1, 2)).toBe(1166) 22 + 23 + expect(factoryMany).toEqual({ 24 + "dep1": "dep1-mocked", 25 + "dep2": "dep2-mocked", 26 + }) 14 27 })
+5 -3
packages/browser/src/client/tester/mocker.ts
··· 1 - import type { IframeChannelOutgoingEvent } from '@vitest/browser/client' 1 + import type { IframeChannelOutgoingEvent, IframeMockFactoryErrorEvent, IframeMockFactoryResponseEvent } from '@vitest/browser/client' 2 2 import { channel } from '@vitest/browser/client' 3 3 import { ModuleMocker } from '@vitest/mocker/browser' 4 4 import { getBrowserState } from '../utils' ··· 14 14 const exports = Object.keys(module) 15 15 channel.postMessage({ 16 16 type: 'mock-factory:response', 17 + eventId: e.data.eventId, 17 18 exports, 18 - }) 19 + } satisfies IframeMockFactoryResponseEvent) 19 20 } 20 21 catch (err: any) { 21 22 channel.postMessage({ 22 23 type: 'mock-factory:error', 24 + eventId: e.data.eventId, 23 25 error: { 24 26 name: err.name, 25 27 message: err.message, 26 28 stack: err.stack, 27 29 }, 28 - }) 30 + } satisfies IframeMockFactoryErrorEvent) 29 31 } 30 32 } 31 33 },
+7 -3
packages/browser/src/client/tester/msw.ts
··· 1 1 import { channel } from '@vitest/browser/client' 2 2 import type { 3 3 IframeChannelEvent, 4 + IframeMockFactoryRequestEvent, 4 5 IframeMockingDoneEvent, 5 6 } from '@vitest/browser/client' 6 7 import type { MockedModuleSerialized } from '@vitest/mocker' 7 8 import { ManualMockedModule } from '@vitest/mocker' 8 9 import { ModuleMockerMSWInterceptor } from '@vitest/mocker/browser' 10 + import { nanoid } from '@vitest/utils' 9 11 10 12 export class VitestBrowserModuleMockerInterceptor extends ModuleMockerMSWInterceptor { 11 13 override async register(event: MockedModuleSerialized): Promise<void> { ··· 42 44 } 43 45 44 46 function getFactoryExports(id: string) { 47 + const eventId = nanoid() 45 48 channel.postMessage({ 46 49 type: 'mock-factory:request', 50 + eventId, 47 51 id, 48 - }) 52 + } satisfies IframeMockFactoryRequestEvent) 49 53 return new Promise<string[]>((resolve, reject) => { 50 54 channel.addEventListener( 51 55 'message', 52 56 function onMessage(e: MessageEvent<IframeChannelEvent>) { 53 - if (e.data.type === 'mock-factory:response') { 57 + if (e.data.type === 'mock-factory:response' && e.data.eventId === eventId) { 54 58 resolve(e.data.exports) 55 59 channel.removeEventListener('message', onMessage) 56 60 } 57 - if (e.data.type === 'mock-factory:error') { 61 + if (e.data.type === 'mock-factory:error' && e.data.eventId === eventId) { 58 62 reject(e.data.error) 59 63 channel.removeEventListener('message', onMessage) 60 64 }
+4
test/browser/fixtures/mocking/src/mocks_factory_many.ts
··· 1 + import { dep1 } from "./mocks_factory_many_dep1"; 2 + import { dep2 } from "./mocks_factory_many_dep2"; 3 + 4 + export default { dep1, dep2 }
+1
test/browser/fixtures/mocking/src/mocks_factory_many_dep1.ts
··· 1 + export const dep1: string = "dep1"
+1
test/browser/fixtures/mocking/src/mocks_factory_many_dep2.ts
··· 1 + export const dep2: string = "dep2"