Skip to content

Commit 7758e8f

Browse files
authored
Fix: Make sure MSW loader is resolved only when mocking is enabled (#157)
* upgrade storybook and vitest dependencies * make sure MSW loader only resolves when mocking is enabled * add more passthrough urls * update msw file * cleanup
1 parent 1ed70e3 commit 7758e8f

File tree

13 files changed

+1909
-2329
lines changed

13 files changed

+1909
-2329
lines changed

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,11 @@ initialize({}, [
257257

258258
#### Using the addon in Node.js with Portable Stories
259259

260-
If you're using [portable stories](https://storybook.js.org/docs/writing-tests/stories-in-unit-tests), you need to make sure the MSW loaders are applied correctly.
260+
If you're using [portable stories](https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest), you need to make sure the MSW loaders are applied correctly.
261261

262-
### Storybook 8
262+
### Storybook 8.2 or higher
263263

264-
You do so by calling the `load` function of your story before rendering it:
264+
If you [set up the project annotations](https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations) correctly, by calling the `play` function of your story, the MSW loaders will be applied automatically:
265265

266266
```ts
267267
import { composeStories } from '@storybook/react'
@@ -270,13 +270,12 @@ import * as stories from './MyComponent.stories'
270270
const { Success } = composeStories(stories)
271271

272272
test('<Success />', async() => {
273-
// 👇 Crucial step, so that the MSW loaders are applied
274-
await Success.load()
275-
render(<Success />)
273+
// The MSW loaders are applied automatically via the play function
274+
await Success.play()
276275
})
277276
```
278277

279-
### Storybook 7
278+
### Storybook < 8.2
280279

281280
You do so by calling the `applyRequestHandlers` helper before rendering your story:
282281

packages/docs/.storybook/preview.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { initialize, mswLoader } from 'msw-storybook-addon';
33

44
import '../src/styles.css';
55

6-
initialize();
7-
86
const preview: Preview = {
7+
// beforeAll is available in Storybook 8.2. Else the call would happen outside of the preview object
8+
beforeAll: async() => {
9+
initialize();
10+
},
11+
loaders: mswLoader,
912
parameters: {
1013
actions: { argTypesRegex: '^on[A-Z].*' },
1114
},
12-
loaders: [mswLoader],
1315
};
1416

1517
export default preview;

packages/docs/package.json

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,34 @@
4040
},
4141
"devDependencies": {
4242
"@rollup/plugin-replace": "^5.0.5",
43-
"@storybook/addon-a11y": "^8.0.0",
44-
"@storybook/addon-actions": "^8.0.0",
45-
"@storybook/addon-essentials": "^8.0.0",
46-
"@storybook/addon-links": "^8.0.0",
47-
"@storybook/addon-mdx-gfm": "^8.0.0",
48-
"@storybook/node-logger": "^8.0.0",
49-
"@storybook/preset-create-react-app": "^8.0.0",
50-
"@storybook/react": "^8.0.0",
51-
"@storybook/react-vite": "^8.0.0",
52-
"@testing-library/jest-dom": "^6.1.4",
53-
"@testing-library/react": "^14.1.2",
54-
"@testing-library/user-event": "^14.5.1",
43+
"@storybook/addon-a11y": "^8.2.1",
44+
"@storybook/addon-actions": "^8.2.1",
45+
"@storybook/addon-essentials": "^8.2.1",
46+
"@storybook/addon-links": "^8.2.1",
47+
"@storybook/addon-mdx-gfm": "^8.2.1",
48+
"@storybook/node-logger": "^8.2.1",
49+
"@storybook/preset-create-react-app": "^8.2.1",
50+
"@storybook/react": "^8.2.1",
51+
"@storybook/react-vite": "^8.2.1",
52+
"@testing-library/dom": "^10.3.1",
53+
"@testing-library/jest-dom": "^6.4.6",
54+
"@testing-library/react": "^16.0.0",
55+
"@testing-library/user-event": "^14.5.2",
5556
"@types/react": "^18.0.27",
5657
"@types/react-dom": "^18.0.10",
5758
"@types/react-router-dom": "^5.3.3",
5859
"@vitejs/plugin-react": "^4.2.1",
5960
"chromatic": "^11.0.3",
6061
"jsdom": "^23.0.0",
61-
"msw": "^2.0.9",
62-
"storybook": "^8.0.0",
62+
"msw": "^2.3.1",
63+
"storybook": "^8.2.1",
6364
"typescript": "^5.3.2",
6465
"vite": "^5.2.11",
65-
"vitest": "^1.6.0"
66+
"vitest": "^2.0.2"
6667
},
6768
"msw": {
68-
"workerDirectory": "public"
69+
"workerDirectory": [
70+
"public"
71+
]
6972
}
7073
}

packages/docs/public/mockServiceWorker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* - Please do NOT serve this file on production.
99
*/
1010

11-
const PACKAGE_VERSION = '2.2.14'
11+
const PACKAGE_VERSION = '2.3.1'
1212
const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
1313
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
1414
const activeClientIds = new Set()

packages/docs/src/demos/fetch/AddonOnNode.test.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
/**
2-
* @jest-environment jsdom
2+
* @vitest-environment jsdom
33
*/
44
import { render, screen } from '@testing-library/react'
55
import { composeStories, setProjectAnnotations } from '@storybook/react'
6-
import { describe, afterAll, it, expect } from 'vitest'
6+
import { describe, afterAll, it, expect, beforeAll } from 'vitest'
77

88
import { getWorker, applyRequestHandlers } from 'msw-storybook-addon'
99
import * as stories from './App.stories'
1010
import projectAnnotations from '../../../.storybook/preview'
1111

12-
setProjectAnnotations(projectAnnotations)
12+
const annotations = setProjectAnnotations(projectAnnotations)
1313

1414
const { MockedSuccess, MockedError } = composeStories(stories)
1515

1616
// Useful in scenarios where the addon runs on node, such as with portable stories
1717
describe('Running msw-addon on node', () => {
18+
beforeAll(annotations.beforeAll!)
1819
afterAll(() => {
1920
// @ts-expect-error TS(2339): Property 'close' does not exist on type 'SetupWork... Remove this comment to see the full error message
2021
getWorker().close()

packages/msw-addon/src/applyRequestHandlers.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import type { RequestHandler } from 'msw'
22
import { api } from '@build-time/initialize'
33
import type { Context } from './decorator.js'
4-
import { deprecate } from './util.js';
4+
import { deprecate } from './util.js'
55

6-
const deprecateMessage = deprecate(`
6+
const deprecateMessage = deprecate(`
77
[msw-storybook-addon] You are using parameters.msw as an Array instead of an Object with a property "handlers". This usage is deprecated and will be removed in the next release. Please use the Object syntax instead.
88
99
More info: https://github.com/mswjs/msw-storybook-addon/blob/main/MIGRATION.md#parametersmsw-array-notation-deprecated-in-favor-of-object-notation
1010
`)
1111

12-
// P.S. this is used by Storybook 7 users as a way to help them migrate.
12+
// P.S. this is publicly exported as it is used by Storybook 7 users as a way to help them migrate.
1313
// This should be removed from the package exports in a future release.
1414
export function applyRequestHandlers(
1515
handlersListOrObject: Context['parameters']['msw']
1616
): void {
17-
api?.resetHandlers();
18-
17+
api?.resetHandlers()
18+
1919
if (handlersListOrObject == null) {
2020
return
2121
}
Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
1-
import { InitializeOptions } from "./initialize.js";
1+
import { InitializeOptions } from './initialize.js'
22

3-
const fileExtensionPattern = /\.(js|jsx|ts|tsx|mjs|woff|woff2|ttf|otf|eot)$/;
3+
const fileExtensionPattern = /\.(js|jsx|ts|tsx|mjs|woff|woff2|ttf|otf|eot)$/
44
const filteredURLSubstrings = [
5-
"sb-common-assets",
6-
"node_modules",
7-
"node-modules",
8-
"hot-update.json",
9-
"__webpack_hmr",
10-
"sb-vite",
11-
];
5+
'sb-common-assets',
6+
'node_modules',
7+
'node-modules',
8+
'hot-update.json',
9+
'__webpack_hmr',
10+
'iframe.html',
11+
'sb-vite',
12+
'@vite',
13+
'@react-refresh',
14+
'/virtual:',
15+
'.stories.',
16+
'.mdx',
17+
]
1218

1319
const shouldFilterUrl = (url: string) => {
1420
// files which are mostly noise from webpack/vite builders + font files
1521
if (fileExtensionPattern.test(url)) {
16-
return true;
22+
return true
1723
}
1824

1925
const isStorybookRequest = filteredURLSubstrings.some((substring) =>
2026
url.includes(substring)
21-
);
27+
)
2228

2329
if (isStorybookRequest) {
24-
return true;
30+
return true
2531
}
2632

27-
return false;
28-
};
33+
return false
34+
}
2935

3036
export const augmentInitializeOptions = (options: InitializeOptions) => {
31-
if (typeof options?.onUnhandledRequest === "string") {
32-
return options;
37+
if (typeof options?.onUnhandledRequest === 'string') {
38+
return options
3339
}
3440

3541
return {
3642
...options,
3743
// Filter requests that we know are not relevant to the user e.g. HMR, builder requests, statics assets, etc.
3844
onUnhandledRequest: (...args) => {
39-
const [{ url }, print] = args;
45+
const [{ url }, print] = args
4046
if (shouldFilterUrl(url)) {
41-
return;
47+
return
4248
}
4349

4450
if (!options?.onUnhandledRequest) {
45-
print.warning();
46-
return;
51+
print.warning()
52+
return
4753
}
4854

49-
if (typeof options?.onUnhandledRequest === "function") {
50-
options.onUnhandledRequest(...args);
55+
if (typeof options?.onUnhandledRequest === 'function') {
56+
options.onUnhandledRequest(...args)
5157
}
5258
},
53-
} as InitializeOptions;
54-
};
59+
} as InitializeOptions
60+
}

packages/msw-addon/src/initialize.browser.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,29 @@ type SetupWorker = ReturnType<typeof setupWorker>
66

77
export let api: SetupWorker
88

9+
type ContextfulWorker = SetupWorker & {
10+
context: { isMockingEnabled: boolean; activationPromise?: any }
11+
}
12+
913
export type InitializeOptions = Parameters<SetupWorker['start']>[0]
1014

1115
export function initialize(
1216
options?: InitializeOptions,
1317
initialHandlers: RequestHandler[] = []
1418
): SetupWorker {
15-
const worker = setupWorker(...initialHandlers)
16-
worker.start(augmentInitializeOptions(options))
19+
const worker = setupWorker(...initialHandlers) as ContextfulWorker
20+
worker.context.activationPromise = worker.start(
21+
augmentInitializeOptions(options)
22+
)
1723
api = worker
1824
return worker
1925
}
2026

27+
export async function waitForMswReady() {
28+
const msw = getWorker() as ContextfulWorker
29+
await msw.context.activationPromise
30+
}
31+
2132
export function getWorker(): SetupWorker {
2233
if (api === undefined) {
2334
throw new Error(

packages/msw-addon/src/initialize.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ export declare function initialize(
1313
initialHandlers?: RequestHandler[]
1414
): SetupApi<LifeCycleEventsMap>
1515

16-
export declare function getWorker(): typeof api
16+
export declare function waitForMswReady(): Promise<void>
17+
18+
export declare function getWorker(): typeof api

packages/msw-addon/src/initialize.node.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ export function initialize(
1818
return server
1919
}
2020

21+
export async function waitForMswReady() {
22+
// in Node MSW is activated instantly upon registration. Still we need to check the presence of the worker
23+
getWorker()
24+
}
25+
2126
export function getWorker(): SetupServer {
2227
if (api === undefined) {
2328
throw new Error(

0 commit comments

Comments
 (0)