Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/browser/src/client/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ export interface IframeMockingDoneEvent {

export interface IframeMockFactoryRequestEvent {
type: 'mock-factory:request'
eventId: string
id: string
}

export interface IframeMockFactoryResponseEvent {
type: 'mock-factory:response'
eventId: string
exports: string[]
}

export interface IframeMockFactoryErrorEvent {
type: 'mock-factory:error'
eventId: string
error: any
}

Expand Down
8 changes: 5 additions & 3 deletions packages/browser/src/client/tester/mocker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IframeChannelOutgoingEvent } from '@vitest/browser/client'
import type { IframeChannelOutgoingEvent, IframeMockFactoryErrorEvent, IframeMockFactoryResponseEvent } from '@vitest/browser/client'
import { channel } from '@vitest/browser/client'
import { ModuleMocker } from '@vitest/mocker/browser'
import { getBrowserState } from '../utils'
Expand All @@ -14,18 +14,20 @@ export class VitestBrowserClientMocker extends ModuleMocker {
const exports = Object.keys(module)
channel.postMessage({
type: 'mock-factory:response',
eventId: e.data.eventId,
exports,
})
} satisfies IframeMockFactoryResponseEvent)
}
catch (err: any) {
channel.postMessage({
type: 'mock-factory:error',
eventId: e.data.eventId,
error: {
name: err.name,
message: err.message,
stack: err.stack,
},
})
} satisfies IframeMockFactoryErrorEvent)
}
}
},
Expand Down
10 changes: 7 additions & 3 deletions packages/browser/src/client/tester/msw.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { channel } from '@vitest/browser/client'
import type {
IframeChannelEvent,
IframeMockFactoryRequestEvent,
IframeMockingDoneEvent,
} from '@vitest/browser/client'
import type { MockedModuleSerialized } from '@vitest/mocker'
import { ManualMockedModule } from '@vitest/mocker'
import { ModuleMockerMSWInterceptor } from '@vitest/mocker/browser'
import { nanoid } from '@vitest/utils'

export class VitestBrowserModuleMockerInterceptor extends ModuleMockerMSWInterceptor {
override async register(event: MockedModuleSerialized): Promise<void> {
Expand Down Expand Up @@ -42,19 +44,21 @@ export function createModuleMockerInterceptor() {
}

function getFactoryExports(id: string) {
const eventId = nanoid()
channel.postMessage({
type: 'mock-factory:request',
eventId,
id,
})
} satisfies IframeMockFactoryRequestEvent)
return new Promise<string[]>((resolve, reject) => {
channel.addEventListener(
'message',
function onMessage(e: MessageEvent<IframeChannelEvent>) {
if (e.data.type === 'mock-factory:response') {
if (e.data.type === 'mock-factory:response' && e.data.eventId === eventId) {
resolve(e.data.exports)
channel.removeEventListener('message', onMessage)
}
if (e.data.type === 'mock-factory:error') {
if (e.data.type === 'mock-factory:error' && e.data.eventId === eventId) {
reject(e.data.error)
channel.removeEventListener('message', onMessage)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ export type {
SerializedError,
TestError,
} from './types'

export { nanoid } from './nanoid'
12 changes: 12 additions & 0 deletions packages/utils/src/nanoid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// port from nanoid
// https://github.com/ai/nanoid
const urlAlphabet
= 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
export function nanoid(size = 21): string {
let id = ''
let i = size
while (i--) {
id += urlAlphabet[(Math.random() * 64) | 0]
}
return id
}
15 changes: 1 addition & 14 deletions packages/vitest/src/utils/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Arrayable, Nullable } from '../types/general'

export { notNullish, getCallLastIndex } from '@vitest/utils'
export { notNullish, getCallLastIndex, nanoid } from '@vitest/utils'

export interface GlobalConstructors {
Object: ObjectConstructor
Expand Down Expand Up @@ -203,16 +203,3 @@ export function wildcardPatternToRegExp(pattern: string): RegExp {
'i',
)
}

// port from nanoid
// https://github.com/ai/nanoid
const urlAlphabet
= 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'
export function nanoid(size = 21) {
let id = ''
let i = size
while (i--) {
id += urlAlphabet[(Math.random() * 64) | 0]
}
return id
}
13 changes: 13 additions & 0 deletions test/browser/fixtures/mocking/mocked-factory.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, test, vi } from 'vitest'
import { calculator, mocked } from './src/mocks_factory'
import factoryMany from './src/mocks_factory_many'

vi.mock(import('./src/mocks_factory'), () => {
return {
Expand All @@ -8,7 +9,19 @@ vi.mock(import('./src/mocks_factory'), () => {
}
})

vi.mock(import('./src/mocks_factory_many_dep1'), () => ({
dep1: "dep1-mocked"
}))
vi.mock(import('./src/mocks_factory_many_dep2'), () => ({
dep2: "dep2-mocked"
}))

test('adds', () => {
expect(mocked).toBe(true)
expect(calculator('plus', 1, 2)).toBe(1166)

expect(factoryMany).toEqual({
"dep1": "dep1-mocked",
"dep2": "dep2-mocked",
})
})
4 changes: 4 additions & 0 deletions test/browser/fixtures/mocking/src/mocks_factory_many.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { dep1 } from "./mocks_factory_many_dep1";
import { dep2 } from "./mocks_factory_many_dep2";

export default { dep1, dep2 }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dep1: string = "dep1"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const dep2: string = "dep2"
Loading