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

Story not waiting for MSW to be ready before running #89

Open
FezVrasta opened this issue Nov 22, 2022 · 11 comments
Open

Story not waiting for MSW to be ready before running #89

FezVrasta opened this issue Nov 22, 2022 · 11 comments

Comments

@FezVrasta
Copy link

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

@yannbf
Copy link
Collaborator

yannbf commented Nov 22, 2022

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:
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?

@FezVrasta
Copy link
Author

Thanks. We use Storyshots and async loaders are not supported with it ☹️

@yannbf
Copy link
Collaborator

yannbf commented Nov 27, 2022

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.

@hiuny
Copy link

hiuny commented Dec 29, 2022

@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 #72 This pr doesn't seem to have progressed since February 27th.

@fdescamps
Copy link

Hello @yannbf,

I am really interested about this work !
Could you please merge it and generate a new version ?

Thanks a lot

Regards,

@yannbf
Copy link
Collaborator

yannbf commented Jan 11, 2023

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!

@fdescamps
Copy link

Thanks for your work !

@junkisai
Copy link

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:
storybookjs/test-runner#417

@cbovis
Copy link

cbovis commented Jan 30, 2024

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: 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.

@jalovatt
Copy link

jalovatt commented Feb 9, 2024

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.

  • This is only reproducible on our S3 Storybook deploy, not locally with the dev server.
  • The issue goes away if I disable browser caching, which would explain why Incognito mode fixed it for the users above.

Problem

I see two cases in mswLoader where the loader could incorrectly assume things are good to go.

  1. 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 ()
  1. 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.

Fix

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);
    });
  }
};

@0xR
Copy link

0xR commented Apr 12, 2024

The solution from @jalovatt didn't work for me

this worked for me:

-  loaders: [mswLoader],
+  loaders: [mswLoader, () => getWorker().start()],

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants