Closed FezVrasta closed 4 months ago
Hey @FezVrasta thanks for opening this issue! I wonder if you could experiment with making local changes to your dependency and include the following code changes: https://github.com/mswjs/msw-storybook-addon/compare/main...lyleunderwood:feat/add-experimental-loaders
Then, instead of doing this in your preview.js
file:
import { initialize, mswDecorator } from 'msw-storybook-addon'
initialize()
export const decorators = [mswDecorator]
do this:
import { initialize, mswLoader } from 'msw-storybook-addon'
initialize()
export const loaders = [mswLoader]
And let me know if that fixes your problem?
Thanks. We use Storyshots and async loaders are not supported with it ☹️
Did you try using the Storybook test runner? https://github.com/storybookjs/test-runner
Storyshots will be discontinued at some point in favor of the test runner, which supports every feature of Storybook (including loaders), snapshots etc.
@yannbf I had the same problem and came across this issue while searching for a solution. The method using loaders works very well. It would be nice if it was released soon https://github.com/mswjs/msw-storybook-addon/pull/72 This pr doesn't seem to have progressed since February 27th.
Hello @yannbf,
I am really interested about this work ! Could you please merge it and generate a new version ?
Thanks a lot
Regards,
Hey peeps, PR is merged, and a scheduled release will soon happen. Thanks for providing feedback! There are quite a few changes I'm planning to make in this addon, including adding support to Storybook 7.0, which might take some time but we'll get there!
Thanks for your work !
I changed to use mswLoader
with version 1.10.0 of msw-storybook-addon
, but I am experiencing a similar issue as the title of this issue.
It seemed like the following issue also reported a similar phenomenon: https://github.com/storybookjs/test-runner/issues/417
I changed to use
mswLoader
with version 1.10.0 ofmsw-storybook-addon
, but I am experiencing a similar issue as the title of this issue.It seemed like the following issue also reported a similar phenomenon: storybookjs/test-runner#417
I was banging my head against a wall with this one. MSW would say it initialised fine in my local environment but no requests were being intercepted. The set up had been working forever but at some point it mysteriously broke. Even stranger, if I modified something related to the story then requests would be mocked when the story was auto-reloaded. It would also run perfectly fine via our Chromatic visual regression testing.
In desperation I figured I'd try another browser and discovered that the broken behaviour only occurs for me in Chrome. In Firefox, Safari, Edge, Opera, Arc everything is fine. Switched Chrome to incognito mode and it worked fine there which tipped me to extensions. Started removing extensions until eventually all is well in the world again.
Unfortunately I wasn't systematic enough about it to know which extension was causing the problem but I assume it was something that works at the network level. Perhaps this approach is useful to yourself or others in debugging.
I'm seeing this behaviour in 2.0.0-beta.1
. Not sure how different 2.x is from 1.x, or how MSW's implementation has changed on that side, but a quick skim suggested that the same failure states are possible.
In my case, some extra console logging showed that my API requests were being fired before MSW was finished loading.
I see two cases in mswLoader where the loader could incorrectly assume things are good to go.
If the service worker hasn't loaded enough for if (... && navigator.serviceWorker.controller)
to pass, the loader returns too early.
I was able to directly identify this as the source of failure in my case:
loaders: [
async (context) => {
console.log('calling mswLoader');
await mswLoader(context);
console.log('mswLoader finished');
if (!navigator.serviceWorker.controller) {
console.log('worker not found');
}
},
],
// Console output:
calling mswLoader
mswLoader finished
worker not found
API request firing
API request finished
[MSW] Mocking enabled.
Failed to load resource: the server responded with a status of 405 ()
serviceWorker.ready
resolves when the ServiceWorkerRegistration
becomes active
(MDN), but that happens when the inner ServiceWorker
has a state
of either activating
and activated
. (MDN)
MSW doesn't actually enable itself until the worker is activated
(MSW source), so again mswLoader
is able to return too early.
I haven't spotted a case where this was causing my queries to fire too early, but it is at least possible.
I added a function to explicitly wait for both of the conditions above, preventing my stories from rendering until MSW is actually in place. With it, I've been unable to reproduce this issue at all.
loaders: [
async (context) => {
await mswLoader(context);
await waitForActivatedServiceWorker();
},
],
const waitForActivatedServiceWorker = async () => {
// Wait for the worker to be loaded
const serviceWorker = navigator.serviceWorker.controller
|| await new Promise((resolve, reject) => {
let triesLeft = 10;
const fn = () => {
if (navigator.serviceWorker.controller) {
resolve(navigator.serviceWorker.controller);
} else {
triesLeft -= 1;
if (triesLeft === 0) {
reject(new Error('Timed out waiting for service worker'));
} else {
setTimeout(fn, 100);
}
}
};
setTimeout(fn, 100);
});
if (!serviceWorker) {
throw new Error('No service worker found');
}
// Make sure the worker is actually ready to go
if (serviceWorker.state !== 'activated') {
await new Promise<void>((resolve) => {
const fn = (e: Event) => {
if (e.target && 'state' in e.target && e.target.state === 'activated') {
serviceWorker.removeEventListener('statechange', fn);
resolve();
}
};
serviceWorker.addEventListener('statechange', fn);
});
}
};
The solution from @jalovatt didn't work for me
this worked for me:
- loaders: [mswLoader],
+ loaders: [mswLoader, () => getWorker().start()],
The solution from @jalovatt didn't work for me
this worked for me:
- loaders: [mswLoader], + loaders: [mswLoader, () => getWorker().start()],
As a heads up @yannbf, upgrading to the latest version of this library I found unreliable results with 404's and various requests attempted before mocking enabled. @0xR message seems so far to be the most reliable working solution.
Took me a minute to find getWorker
function.
It's here: import { initialize, mswLoader, getWorker } from 'msw-storybook-addon';
I'm using msw-storybook-addon==2.0.2
with msw==2.3.1
and have this issue which mostly happens in Firefox.
I think the only reason why adding the second call to worker.start()
works is because it adds extra time needed for MSW to complete initialization, therefore, I don't think it is reliable. Besides, it floods the console with warnings about the redundant call to worker.start()
and removes options' augmentations added by the addon here
I also think that the problem is indeed in the fact that the addon only waits for navigator.serviceWorker.ready
to be resolved, but it may resolve before the mocking is enabled and enableMocking
is called here https://github.com/mswjs/msw/blob/bed402cdd9b79ef3084b3195cdcce0d83c7e2cfc/src/browser/setupWorker/start/createStartHandler.ts#L115
enableMocking
sends a message to the service worker to start mocking, and only after the worker receives it it can be used because that's when the client is added, and if there are no clients, all requests are bypassed
https://github.com/mswjs/msw/blob/bed402cdd9b79ef3084b3195cdcce0d83c7e2cfc/src/mockServiceWorker.js#L106-L111
The workaround I decided to use is a simple promise with is resolved upon receiving a message from the worker confirming activating and just use it instead of the loader provided by the addon:
const mockWatcher = new Promise<void>(resolve => {
navigator.serviceWorker.addEventListener('message', event => {
if (event.data.type === 'MOCKING_ENABLED') resolve();
});
});
@yannbf, is it something that can be used by the addon itself? I can create PR if it makes sense.
I was getting the error:
msw storybook Cannot read properties of undefined (reading 'url')
which was thrown by the initialize()
function.
So I had to move that method to a loader as well.
And while making changes in the preview.ts
, hotreloading was double-loading the serviceworker, causing the same error to be thrown. So I had to prevent re-execution while hotreloading.
Which resulted in this patch:
import {
Context,
initialize as originalInitialize,
mswLoader as originalMswLoader,
} from 'msw-storybook-addon';
import { StartOptions } from 'msw/browser';
let initializeOptions: StartOptions | undefined;
export const initialize = (options: StartOptions | undefined) => {
initializeOptions = options;
};
let loaded = false;
export const mswLoader = async (context: Context) => {
if (loaded) return;
originalInitialize(initializeOptions);
await originalMswLoader(context);
loaded = true;
};
I've started running into this again, even with the fix I described previously. The suggestion of waiting for MOCKING_ENABLED
also didn't help, and honestly at this point I'm not sure if it's msw-storybook-addon
or msw
itself at fault.
Adding some console logs shows:
loaders running
mswLoader resolved
got service worker, state = activated
loaders finished
[MSW] Mocking enabled.
TypeError: Cannot read properties of undefined (reading 'id')
^
An API request we depend on, received the Storybook HTML because the mock route isn't in place yet.
I've removed the previous fix in favor of just waiting for a successful test request to one of the mocked routes, just to be absolutely sure.
:rocket: Issue was released in v2.0.3
:rocket:
Hey everyone, this issue should be finally fully fixed by in v2.0.3, please try it out and report back if you have any issues. Thank you so much for all of your input and ideas!
Hello @yannbf ,
my team and I are still experiencing this issue, however it happens every so often. About every 20 refreshes a request will return a 404 as if it wasn't intercepted by msw
.
The console print of [MSW] Mocking enabled
happens first normally every time, and then it's followed by failed requests with a 404 status code every 20 or so refreshes. There is no reliable way to recreate this other than trying to refresh the Storybook page until it happens.
We are using the latest version of msw-storybook-plugin
, so 2.0.3, and the latest current version of msw
, which is 2.3.4. Our storybook version is 7.6.20.
I tested this in the latest version of Chrome and on OSes MacOS and Windows.
We even tried fixes from this discussion but nothing changes. It almost seems like there could be more timing issues at hand here that might be a bit harder to detect. I'm happy to provide whatever is necessary to see if this is our issue or a plugin issue.
In preview.tsx
we have the following stuff for msw:
initialize({
onUnhandledRequest: 'bypass',
});
const preview: ProjectAnnotations<Renderer> = {
...
loaders: [mswLoader],
};
Hello all,
seems like I have the same issue as @frle10 while on v2.0.3. Some requests get intercepted by msw and some don't, it seems to be really random.
This happens in the case where I have a global msw handler trying to intercept requests coming from a global storybook decorator.
If I use the same decorator and handler but on the story level this issue doesn't happen.
I'm still having the issue where it is not intercepting a post call.
We are experiencing an issue where the story is executed/rendered before MSW is ready. We can see in the console that some requests run before the "MSW is ready" console log.
Is there any solution to this problem? We are running the most recent version of the plugin and Storybook 6.5.10