Giter Club home page Giter Club logo

msw-storybook-addon's Introduction

MSW Storybook Addon

Features

  • Mock Rest and GraphQL requests right inside your story.
  • Document how a component behaves in various scenarios.
  • Get a11y, snapshot and visual tests using other addons for free.

Full documentation and live demos

Installing and Setup

Install MSW and the addon

With npm:

npm i msw msw-storybook-addon -D

Or with yarn:

yarn add msw msw-storybook-addon -D

Generate service worker for MSW in your public folder.

If you already use MSW in your project, you have likely done this before so you can skip this step.

npx msw init public/

Refer to the MSW official guide for framework specific paths if you don't use public.

Configure the addon

Enable MSW in Storybook by initializing MSW and providing the MSW loader in ./storybook/preview.js:

import { initialize, mswLoader } from 'msw-storybook-addon'

// Initialize MSW
initialize()

const preview = {
  parameters: {
    // your other code...
  },
  // Provide the MSW addon loader globally
  loaders: [mswLoader],
}

export default preview

Start Storybook

When running Storybook, you have to serve the public folder as an asset to Storybook, so that MSW is included, otherwise it will not be available in the browser.

This means you should set the staticDirs field in the Storybook main config file. Refer to the docs if needed.

npm run storybook

Usage

You can pass request handlers (https://mswjs.io/docs/concepts/request-handler) into the handlers property of the msw parameter. This is commonly an array of handlers.

import { http, HttpResponse } from 'msw'

export const SuccessBehavior = {
  parameters: {
    msw: {
      handlers: [
        http.get('/user', () => {
          return HttpResponse.json({
            firstName: 'Neil',
            lastName: 'Maverick',
          })
        }),
      ],
    },
  },
}

Advanced Usage

Composing request handlers

The handlers property can also be an object where the keys are either arrays of handlers or a handler itself. This enables you to inherit (and optionally overwrite/disable) handlers from preview.js using parameter inheritance:

type MswParameter = {
  handlers: RequestHandler[] | Record<string, RequestHandler | RequestHandler[]>
}

Suppose you have an application where almost every component needs to mock requests to /login and /logout the same way. You can set global MSW handlers in preview.js for those requests and bundle them into a property called auth, for example:

//preview.ts
import { http, HttpResponse } from 'msw'

// These handlers will be applied in every story
export const parameters = {
  msw: {
    handlers: {
      auth: [
        http.get('/login', () => {
          return HttpResponse.json({
            success: true,
          })
        }),
        http.get('/logout', () => {
          return HttpResponse.json({
            success: true,
          })
        }),
      ],
    },
  },
}

Then, you can use other handlers in your individual story. Storybook will merge both global handlers and story handlers:

import { http, HttpResponse } from 'msw'

// This story will include the auth handlers from .storybook/preview.ts and profile handlers
export const SuccessBehavior = {
  parameters: {
    msw: {
      handlers: {
        profile: http.get('/profile', () => {
          return HttpResponse.json({
            firstName: 'Neil',
            lastName: 'Maverick',
          })
        }),
      },
    },
  },
}

Now suppose you want to ovewrite the global handlers for auth. All you have to do is set them again in your story and these values will take precedence:

import { http, HttpResponse } from 'msw'

// This story will overwrite the auth handlers from preview.ts
export const FailureBehavior = {
  parameters: {
    msw: {
      handlers: {
        auth: http.get('/login', () => {
          return HttpResponse.json(null, { status: 403 })
        }),
      },
    },
  },
}

What if you want to disable global handlers? All you have to do is set them as null and they will be ignored for your story:

import { http, HttpResponse } from 'msw'

// This story will disable the auth handlers from preview.ts
export const NoAuthBehavior = {
  parameters: {
    msw: {
      handlers: {
        auth: null,
        others: [
          http.get('/numbers', () => {
            return HttpResponse.json([1, 2, 3])
          }),
          http.get('/strings', () => {
            return HttpResponse.json(['a', 'b', 'c'])
          }),
        ],
      },
    },
  },
}

Configuring MSW

msw-storybook-addon starts MSW with default configuration. initialize takes two arguments:

A common example is to configure the onUnhandledRequest behavior, as MSW logs a warning in case there are requests which were not handled.

If you want MSW to bypass unhandled requests and not do anything:

// .storybook/preview.ts
import { initialize } from 'msw-storybook-addon'

initialize({
  onUnhandledRequest: 'bypass',
})

If you want to warn a helpful message in case stories make requests that should be handled but are not:

// .storybook/preview.ts
import { initialize } from 'msw-storybook-addon'

initialize({
  onUnhandledRequest: ({ url, method }) => {
    const pathname = new URL(url).pathname
    if (pathname.startsWith('/my-specific-api-path')) {
      console.error(`Unhandled ${method} request to ${url}.

        This exception has been only logged in the console, however, it's strongly recommended to resolve this error as you don't want unmocked data in Storybook stories.

        If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses
      `)
    }
  },
})

Although composing handlers is possible, that relies on Storybook's merging logic, which only works when the handlers in your story's parameters are objects and not arrays. To get around this limitation, you can pass initial request handlers directly the initialize function as a second argument.

// .storybook/preview.ts
import { http, HttpResponse } from 'msw'
import { initialize } from 'msw-storybook-addon'

initialize({}, [
  http.get('/numbers', () => {
    return HttpResponse.json([1, 2, 3])
  }),
  http.get('/strings', () => {
    return HttpResponse.json(['a', 'b', 'c'])
  }),
])

Using the addon in Node.js with Portable Stories

If you're using portable stories, you need to make sure the MSW loaders are applied correctly.

Storybook 8

You do so by calling the load function of your story before rendering it:

import { composeStories } from '@storybook/react'
import * as stories from './MyComponent.stories'

const { Success } = composeStories(stories)

test('<Success />', async() => {
  // ๐Ÿ‘‡ Crucial step, so that the MSW loaders are applied
  await Success.load()
  render(<Success />)
})

Storybook 7

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

import { applyRequestHandlers } from 'msw-storybook-addon'
import { composeStories } from '@storybook/react'
import * as stories from './MyComponent.stories'

const { Success } = composeStories(stories)

test('<Success />', async() => {
  // ๐Ÿ‘‡ Crucial step, so that the MSW loaders are applied
  await applyRequestHandlers(Success.parameters.msw)
  render(<Success />)
})

Note: The applyRequestHandlers utility should be an internal detail that is called automatically by the portable stories, however as it's not possible in Storybook 7, it's exported by the addon. It will be removed in upcoming releases, so it is recommended that you upgrade to Storybook 8 when possible.

Troubleshooting

MSW is interfering with HMR (Hot Module Replacement)

If you're experiencing issues like [MSW] Failed to mock a "GET" request to "http://localhost:6006/4cb31fa2eee22cf5b32f.hot-update.json" in the console, it's likely that MSW is interfering with HMR. This is not common and it seems to only happen in Webpack projects, but if it happens to you, you can follow the steps in this issue to fix it:

#36 (comment)

msw-storybook-addon's People

Contributors

adwd avatar coderkevin avatar itaditya avatar jarojasm95 avatar jesusthehun avatar jonniebigodes avatar kettanaito avatar oscard0m avatar pentzzsolt avatar rajtslegr avatar sethdavenport avatar sivli-embir avatar stevensacks avatar will-stone avatar yannbf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

msw-storybook-addon's Issues

Multiple stories

When I have something like:

const Template = () => ....

export const Story1 = Template.bind({})
export const Story2 = Template.bind({})

Story1.parameters = {
   msw: [ graphql.query('MyQuery', .....) ]      
}

Story2.parameters = {
   msw: [ graphql.query('MyQuery', .....) ]      
}

when I switch from story1 to story2, it uses the resolver for story1.

HTTPS setup

Hi guys, I followed this guide: https://storybook.js.org/addons/msw-storybook-addon. But, in my case I need to have my domain in the URL, so I remapped as such (using Fiddler on windows 10): localhost:6006 lib.domain.net

In my package.json:

"sb": "start-storybook -s public -p 6006"
"https": "start-storybook -s public -p 6006 --https --ssl-key=server/server.key --ssl-cert=server/server.cert"

Running npm run sb:

Running npm run https:

Failed to register a ServiceWorker for scope ('https://lib.domain.net/') with script ('https://lib.domain.net/mockServiceWorker.js'): An SSL certificate error occurred when fetching the script.

The error is pretty clear - the browser doesn't like my SSL setup and doesn't want to internally request the file. At least this is how I understand it.
Do I have to use something like this --unsafely-treat-insecure-origin-as-secure=http://lib.domain.net when running my browser? I wasn't able to do it, tho, didn't work out. Is there a simpler way?

Thanks!

Story-specific handler / request persisting after switching stories

Hi there! Thanks for the add-on!

I'm running into a problem with a story that demos a loading state:

  1. The Loading story applies a handler with ctx.delay('infinite'). This story works great!
    • Specifically, this is a graphql handler for a request from Apollo Client.
  2. But when I load that story and then switch to another story in the same file, that second story also appears as loading forever. The pending request from the first story remains in the network tab, and no new request appears from the second story.

Any help debugging / solving this would be appreciated! Please let me know if additional info would be helpful.

It's a bit hard to debug this / pinpoint what part of the stack may be responsible for not ending the pending request and initiating a new one when switching from the Loading story to the next one. Apologies if I'm barking up the wrong tree.

In the meanwhile, since refreshing the browser tab on the second (non-loading) story allows that story to load correctly, I've worked around this by using a decorator that reloads the iframe when leaving the Loading story. This is the only workaround I've found that helps.

// Yuck...
Loading.decorators = [
  (Story) => {
    useEffect(() => {
      return () => window.location.reload();
    }, []);

    return <Story />;
  },
];

Provide mock data from storybook gui

I just discovered this awesome add-on, great job!

I plan to use it in a project and haven't experimented with it yet, would it be possible to provide mock data from the storybook gui (like we do with the controls addon for example).

This would be interesting as it would let the users (or product owners/sales managers) test some data without touching code.

I'm asking because the data that I display in my apps is very complex (data viz), and usually the result of a calculation on the backend.

Inherit preview.js parameters handlers in stories

As per Storybook rules of parameter inheritance, using msw in stories should inherit/concat the handlers from the preview.js parameters.

Example:
The API I am using requires that I make a call for an oauth token before I can call any API call. In the current way this addon works, I have to duplicate this resolver in every single story that defines msw in its parameters because it overwrites the one from preview.js instead of merging with it.

preview.js

export const parameters = {
    actions: {argTypesRegex: '^on[A-Z].*'},
    msw: [
        rest.post('/oauth/token', (req, res, ctx) =>
            res(ctx.json({token_type: 'Bearer', access_token: 'test_token'}))
        ),
    ],
};

thing.stories.js

export default {
    component: Thing,
    parameters: {
        msw: [
            rest.get('/some/endpoint', (req, res, ctx) => 
                res(ctx.json([]))
            ),
        ],
    },
};

Both handlers should work in the story. Currently, the oauth/token handler is not included in the story.

Enable default handlers to be specified

I've recently started using MSW and I have some 'default' Mock API handlers defined under a 'mocks' directory which I use with MSW during development (when the API endpoints aren't in place, etc).

Unless I'm testing a particular request scenario (e.g. a loading state, error state), I'd ideally want these default handlers to be used by stories/tests. I plan to use msw-storybook-addon for defining scenario-specific network/response behaviour.

The addon is working great for the latter, but I can't see a way to specify the default handlers as 'setupWorker' is called within msw-storybook-addon without any parameters. Am I missing something? Is there a reason to avoid the idea of default handlers?

Cheers

How should this work with vue3

Hi,

I've a question regarding this addon and vue + graphql, on the storybook addon page Vue is under "works with".

So I'm trying to add this to my story, in the example I only see React stories, those work with story Parameters.

Vue stories will work with Args, but when I add msw :[ query here] to my args, I will get the following error in my console:

image

MSW is succesfully loaded becasue I see: [MSW] Mocking enabled. in my console.

Any insights on how to get this working with Vue(3) + graphql?

Allow `parameters.msw` to be a function that takes `args`

Hello! I'm stoked to see this library growing so much. At work we still have a custom setup for Storybook and MSW because we're using args to result in dynamic MSW calls.

I wonder if you could add something like

if (typeof context.parameters.msw === "function") {
  api.use(...context.parameters.msw(context.args))
}

Where the story meta would be:

export default {
  ...,
  args: { isLoading },
  parameters: {
    msw: (args) => [
      rest.get("...", (_, res, ctx) =>
        res(ctx.delay(args.isLoading ? "infinite" : 0), ctx.json(...))
      )
    ]
  }
}

Angular Support

Is there any reason this wouldn't work with Angular? It seems like it's not actually intercepting my requests.

It looks like it should've worked but it's returned the data from the actual endpoint and not what I've mocked:
image

According to the console MSW is enabled
image

Here is my story where I'm adding the params:

export const primary = () => ({
  moduleMetadata: {
    imports: [HttpClientModule],
  },
  component: MyComponent,
  parameters: {
    msw: [
      rest.get('/api/unauthed', (req, res, ctx) => {
        return res(
          ctx.json({
            config: 'myConfig'
          })
        );
      }),
    ],
  },
});

preview.js

import { addDecorator, addParameters } from '@storybook/angular';
import { withKnobs } from '@storybook/addon-knobs';
import { withA11y } from '@storybook/addon-a11y';
import { create } from '@storybook/theming';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import { initializeWorker, mswDecorator } from 'msw-storybook-addon';

initializeWorker();
addDecorator(mswDecorator);
addDecorator(withKnobs);
addDecorator(withA11y);

const theme = create({
  base: 'light',
  brandTitle: 'MyOrg',
  fontBase: '"Open Sans", sans-serif',
  brandImage: 'assets/logo.svg',
});

addParameters({
  options: {
    theme,
    showRoots: true,
  },
  viewport: {
    viewports: INITIAL_VIEWPORTS,
  },
});

angular.json

    "storybook": {
      "projectType": "application",
      "root": "apps/storybook",
      "sourceRoot": "apps/storybook/src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
              ...
              "assets": [
                "apps/storybook/src/mockServiceWorker.js"
             ]
           }
         }
       }
    }

Am I missing something?

Live demos not working?

Hi,

When accessing https://msw-sb.vercel.app/ none of the demos seem to work. The data displayed in the Mocked Success are the actual data, and no error is shown in the Mocked Error stories.

A screen capture:

image

You can see the API calls being done.

Am I missing something?

Thanks!

MSW is initialized after story renders on refresh or fresh load

I am using msw addon to mock graphql calls for apollo client. I have added it as per the installation docs, however I am seeing errors when I load a component that makes a query on mount. To work around this, we have to tell people to "refresh" the page after they first load. Note that it does not occur after you load, navigate to a new story, and then return, within the same SPA (not using browser controls).

image

Storybook may freeze when switching to a story that sends requests but does not define any routes.

We have a Storybook setup for a React app. Unfortunately the repository is private. I will try to create a repository that allows to recreate this issue that I'll describe below.

The packages and browsers we use:

Package name Version
@storybook/react 6.2.8
msw 0.28.1
msw-storybook-addon 1.1.0
Browser Version
Chromium 90.0.4430.72 (Official Build) Arch Linux (64-bit)
Firefox 87.0 (64-bit)

We add the mswDecorator in the preview.js of Storybook like so:

import { addDecorator } from '@storybook/react'
import { initializeWorker, mswDecorator } from 'msw-storybook-addon'

initializeWorker()
addDecorator(mswDecorator)

Some of our stories define routes in parameters.msw, some do not.
When Storybook is running (localhost:6006) it works fine initially and switching to other stories that do not send out any requests works also as expected.

However, we have one story that does sends numerous requests out to a local backend server.
When loading Storybook with that story selected (e.g.: http://localhost:6006/?path=/story/problematic--story), it works also as expected.
The problem arises when switching to the problematic story.
It will freeze the browser completely. I wasn't able to get anything out of the chromium profiler as it never finishes to process the profile.
Luckily I was able to get some hint from the Firefox profiler that it is stuck on a function called deferNetworkRequestsUntil.

The function the profiler refers to in source:

/**
 * Intercepts and defers any requests on the page
 * until the Service Worker instance is ready.
 * Must only be used in a browser.
 */
function deferNetworkRequestsUntil(predicatePromise) {
    // Defer any `XMLHttpRequest` requests until the Service Worker is ready.
    const originalXhrSend = window.XMLHttpRequest.prototype.send;
    window.XMLHttpRequest.prototype.send = function (...args) {
        // Keep this function synchronous to comply with `XMLHttpRequest.prototype.send`,
        // because that method is always synchronous.
        until$1(() => predicatePromise).then(() => {
            window.XMLHttpRequest.prototype.send = originalXhrSend;
            this.send(...args);
        });
    };
    // Defer any `fetch` requests until the Service Worker is ready.
    const originalFetch = window.fetch;
    window.fetch = (...args) => __awaiter(this, void 0, void 0, function* () {
        yield until$1(() => predicatePromise);
        window.fetch = originalFetch;
        return window.fetch(...args);
    });
}

I guess this is the one from msw:
https://github.com/mswjs/msw/blob/2943c080bbdaaf74ec9e23147592fe76a0e6a147/src/utils/deferNetworkRequestsUntil.ts#L3-L29

Removing msw-storybook-addon an its setup from preview.js reliably removes the problem.

I realize what I am writing here is probably not much to go on. But maybe someone else encountered this before.

Peer dependency error after upgrading to msw 1.0.0

Hi guys

After updating to I am receiving this peer dependency error after updating to msw: 1.0.0

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/msw
npm ERR!   dev msw@"^1.0.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer msw@">=0.[35](https://gitlab.com/nomanini/nomanini-dashboard-v4/-/jobs/3658311191#L35).0 <1.0.0" from [email protected]
npm ERR! node_modules/msw-storybook-addon
npm ERR!   dev msw-storybook-addon@"^1.7.0" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: [email protected]
npm ERR! node_modules/msw
npm ERR!   peer msw@">=0.35.0 <1.0.0" from [email protected]
npm ERR!   node_modules/msw-storybook-addon
npm ERR!     dev msw-storybook-addon@"^1.7.0" from the root project
npm ERR!

Not working with addon-storyshots-puppeteer

Hi, this addon seems to break the Storybook storyshots addon. Here's a repro (I simply ran yarn create react-app && npx sb init and then added storyshots and msw):

https://github.com/FezVrasta/msw-storybook-test

Just run yarn test storybook.test.js and you will notice the tests error due to a timeout.

 โ— Storyshots โ€บ Example/Page โ€บ Logged Out

    thrown: "Exceeded timeout of 15000 ms for a hook.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

      4 | import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
      5 |
    > 6 | initStoryshots({
        | ^
      7 |   test: imageSnapshot(),
      8 | });
      9 |

      at node_modules/@storybook/addon-storyshots/dist/ts3.9/api/index.js:29:37
          at Array.forEach (<anonymous>)
      at callTestMethodGlobals (node_modules/@storybook/addon-storyshots/dist/ts3.9/api/index.js:27:13)
      at testStorySnapshots (node_modules/@storybook/addon-storyshots/dist/ts3.9/api/index.js:73:9)
      at Object.<anonymous> (src/storybook.test.js:6:1)

A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks.

I noticed that replacing Puppeteer with Playwright, as explained here fixes the issue.

Could you please look into it?

Rest calls are being cancelled in localhost

Hi!

I'm setting up aws on a project with storybook using this msw-storybook-addon. I started first with just aws and a setup for jest and it worked just fine. When I cover all steps for installing the addon and reuse the handlers, I see that the serviceWorker gets installed and it is intercepting the calls. Still, sometimes returns a 200 with no data and some others the call shows as cancelled in the Network tab.

aws: 0.35.0
aws-storybook-addon: 1.7.0
storybook: 6.5.15

My handlers are looking like this:

[
  rest.get('/recruiter/api/recruiter_access/get_for_current_user', (_req, res, ctx) => {
    console.log({_req })
    return res(ctx.json({res: 'login'}));
  }),
  rest.get('/recruiter/api/account', (_req, res, ctx) => {
    console.log({_req })
    return res(ctx.json({res: 'logout'}));
  }),
];

Here is a screenshot of the Network Tab in Chrome

image

Here a screenshot of the serviceWorker view in Application Tab in Chrome (200 received)

image

When the requests show as cancelled in Network Tab, They don't show up in serviceWork Network filter

In console tab I get

image

image

using chrome 109.0.5414.119 in Mac (M1)

I understand this might be quite a specific case. Any pointer would be very much appreciated ๐Ÿ˜Š

Thanks a lot in advance!

[Feature] Provide mockServiceWorker automatically

Currently the installation instructions ask to use the msw cli that copies the service worker to the public folder, but that seems to pollute the folder and does not allow for future updates. Instead I think it should be possible to tap into storybook's build/server and provide the mock file automatically.

I haven't investigated the build process, but at least when using the storybook server a middleware can do the trick.

SyntaxError: Unexpected token '?' error during CircleCI build process

My project is Gatsby v3, with Storybook and the MSW Storybook plugin. I am only using MSW in Storybook and nowhere in the Gatsby site itself. Latest versions of all libraries.

In the CircleCI build, these errors appeared in the log during the build step. It didn't break the build (as far as I can tell), but I'm on a feature branch right now which doesn't attempt to deploy the entire Gatsby site. It just builds a single page Gatsby site which is enough for the Storybook to build and deploy to Chromatic.

At any rate, here are the errors. Not sure if they're anything to worry about considering the build finished successfully, but I felt like I should report them all the same.

> [email protected] postinstall /home/circleci/repo/node_modules/msw
> node -e "try{require('./config/scripts/postinstall')}catch(e){}"

/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/prompts/rawlist.js:126
      this.selected = this.selected ?? -1;
                                     ^

SyntaxError: Unexpected token '?'
    at wrapSafe (internal/modules/cjs/loader.js:1053:16)
    at Module._compile (internal/modules/cjs/loader.js:1101:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Function.promptModule.restoreDefaultPrompts (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:65:36)
    at Object.inquirer.createPromptModule (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:72:16)
    at Object.<anonymous> (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:84:28)
[MSW] Failed to automatically update the worker script:
Error: Command failed: node /home/circleci/repo/node_modules/msw/cli/index.js init /home/circleci/repo/.storybook/static
/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/prompts/rawlist.js:126
      this.selected = this.selected ?? -1;
                                     ^

SyntaxError: Unexpected token '?'
    at wrapSafe (internal/modules/cjs/loader.js:1053:16)
    at Module._compile (internal/modules/cjs/loader.js:1101:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Function.promptModule.restoreDefaultPrompts (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:65:36)
    at Object.inquirer.createPromptModule (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:72:16)
    at Object.<anonymous> (/home/circleci/repo/node_modules/msw/node_modules/inquirer/lib/inquirer.js:84:28)

getWorker() TS issue

This is odd, the following code generates a TS error even though on the face of it the types look OK:

import { getWorker } from "msw-storybook-addon";

afterAll(() => {
  // ts(2339) error
  getWorker().close();
});

It seems to think close is missing from SetupApi but that doesn't make sense.

I have created a sample repo here:

https://github.com/robcaldecott/msw-storybook-addon-type-errors

Current workarounds are either // @ts-ignore or this:

import { SetupServerApi } from "msw/lib/types/node";

afterAll(() => {
  (getWorker() as SetupServerApi).close();
});

Better ideas are welcome.

Should per story handlers work on the Docs tab when all stories are presented on the same page?

I'm not certain if this is actually related to #63, but when I have multiple stories displayed on the Docs page with different returns on the same handler endpoint/method, only the last one is applied. Works fine on the individual story canvases as they're treated independently.

In addition to the example provided in #63, if there is a story without any handlers placed last, it causes all of the others to fail with the error that no handlers have been defined.

Should this actually work on the Docs tab?

Currently using @latest for msw, storybook/react, and msw-storybook-addon.

Conflict with Chromatic and google fonts

I've discovered a conflict with the msw addon and Chromatic that needs to be addressed.

The msw addon captures all http requests, not just the ones you assign it. If there isn't a defined handler, it passes it through.

I am using google fonts and MSW tries to load the google fonts itself before passing the request through to google (I can see it in the console warning locally).

There's a delay in this pass-through that causes Chromatic snapshots to be taken before or after the google font loads in every story. The timing is inconsistent.

I am using a google font via the preview-head.html.

<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
    href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&family=Noto+Sans+JP:wght@300;400;500;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap"
    rel="stylesheet"
/>

Because of this, I have to resolve dozens of differences every time I commit because the fonts are rendering differently each time in every story. Sometimes the google font loads in time, sometimes it doesn't. When it doesn't, it's using the fallback font. It's inconsistent so different stories are different each time.

All other requests got into mockServiceWorker.

The service worker worked fine in localhost:6006 but not in Azure Static Web App.

I deployed a storybook with msw-addon to Azure Static Web App but all requests got into the service worker.

image

mockServiceWorker.js:265 [MSW] Failed to mock a "GET" request to "https://xxx.azurestaticapps.net/runtime~main.f1d0f47e5792ea375d40.bundle.js": TypeError: Cannot read property 'id' of undefined
    at handleRequest **(https://xxx.azurestaticapps.net/mockServiceWorker.js:117:34)**

Error after upgrading and replacing mswDecorator with mswLoader - Storybook v7

I'm getting an error after upgrading to 1.7.0 and replacing the mswDecorator with mswLoader. Any help would be appreciated. If there is any more info needed to debug, let me know. Thanks.

storybook 7.0.0-beta.31
msw-storybook-addon 1.7.0
msw 0.49.3
react 18.2.0

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
    at throwOnInvalidObjectType (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:80645:9)
    at reconcileChildFibers (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:81586:7)
    at reconcileChildren (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:84920:28)
    at mountIndeterminateComponent (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:85910:5)
    at beginWork (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:87340:16)
    at beginWork$1 (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:93179:14)
    at performUnitOfWork (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:92310:12)
    at workLoopSync (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:92219:5)
    at renderRootSync (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:92187:7)
    at recoverFromConcurrentError (http://0.0.0.0:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-fa810f.iframe.bundle.js:91603:20)

Using with Jest: ReferenceError: fetch is not defined

Hi team,

When I'm using the add-on, I have problem running unit test with Jest with error:
ReferenceError: fetch is not defined

I've tried solution in this issue: mswjs/msw#686
But after importing whatwg-fetch in setupFile.js for Jest the mocked response won't be returned for the story.

Is there way to use add-on with Jest?

Percy Visual Regression fails with timeouts when initializeWorker is called

We are using MSW to run visual regression using Percy and recent update of npm packages leads to timeouts in E2E tests.

Package name Version
@storybook/react 6.3.10
msw 0.35.0
msw-storybook-addon 1.4.0

Unfortunately the repo is private and I am not able to share it but I can create an empty one if you want, steps to reproduce:

  1. Create an empty folder and initialise it with npm/yarn
  2. Add latest react
  3. Call 'npx sb init' to install Storybook with default stories
  4. Install @percy/cli @percy/storybook (you will need a free Percy account to run tests)
  5. Run percy tests -- it will pass
  6. Install MSW Storybook Addon and run percy tests again -- it will fail with timeouts

In interactive mode I can see Chromium is doing multiple page reloads when MSW is attached, and removing initializeWorker() from preview.js clearly solves the problem. Any help to get directions where to continue digging is extremely appreciated

Race condition but only in chromatic?

So far no issues running storybook locally via start-storybook,

but seeing a race condition when building storybook for chromatic. Looks like The interceptor gets loaded after the story tried to access apis.

Here is my current setup

    "@storybook/vue3": "^6.4.20",
    "@storybook/builder-vite": "0.1.22",
    "msw": "^0.39.2",
    "msw-storybook-addon": "^1.6.1",
    "chromatic": "^6.5.3"

And in my preview.js

import { handlers } from '../mocks/handlers'

initialize()

export const decorators = [mswDecorator]

export const parameters = {  
  msw: {
    handlers,
  },
}

handlers is an array of RestHandler endpoints

When loading the built story:
image

Just to see i also tried loading msw into the parameters of the individual story, with no luck.

onUnhandledRequest drastically increasing execution time

Problem

Execution of visual tests on Chromatic goes from 3 minutes on average to around 18 minutes

Changing the following code:

 initializeWorker({
   onUnhandledRequest(req) {
     // Only some API requests should be mocked
     if (!req.url.href.startsWith(myapi(''))) {
       return;
     }
     console.error(
       'Found an unhandled %s request to %s',
       req.method,
       req.url.href,
     );

    throw new Error(
      'Unhandled request, please ensure you are mocking the correct endpoints',
    );
  },
});

to simply:

 initializeWorker();

fixed the issue.

I'm not sure if the problem is with this addon, with msw itself, or with how the integration is being done. If I want to workaround this and provide a fallback request handler to setupServer, I can't because there is no way to forward options to setupServer.

Version info:

msw-storybook-addon: 1.1.0
msw: 0.30.0:

How to prevent `msw` from catching some URLs: improve docs?

Hi,

I looked multiple times at the addon docs but also the main docs and it was not obvious at all from the initialize() function I would be able to let pass some specific URLs thanks to passthrough():

initialize({
  onUnhandledRequest: (request, print) => {
    if (request.url.pathname.startsWith('/node_modules/')) {
      request.passthrough();
    } else {
      // For all others, show a warning
      print.warning();
    }
  },
});

It did not come to mind since https://mswjs.io/docs/api/request/passthrough talks about request, but in the examples https://msw-sb.vercel.app/?path=/story/guides-getting-started--page#configuring-msw it only talks about method and url.

It's maybe not an issue, but I hope it can save some time to others :D

Thank you,

Passing msw returned resuts into specific storybook component args

I'm trying to pass the result of the MSW request added in parameters to a specific story component arg, the approach described in all examples shown in the doc is passing the endpoint URL as props into the component, then the component treats the result. In my case, I want to pass the result of the MSW request into the story component as props via args, which means that the data will be ready for the component directly when the fetching from MSW is finished. How can I pass the MSW returned data directly into args in the story component? I'm using Nuxt.js and Nuxt-storybook module.

mock service worker integration in storybook-build

First of all, thanks for creating this addon, It's being really useful overall. However, I have a specific use case where when building storybook with the default build-storybook command, which generates the storybook-static folder, I want to be able to get data from the mock service provider.
Right now, when the build finishes, the mock service provider doesn't work (can't fetch any data).

How could I make it work within the build?

I want this behaviour because I can just send to my clients a link to check how the frontend is looking right now, which is the storybook build, and having no mocks in it will provide them nothing useful.

Service Worker script does not exist at the given path

Hello, we have a storybook running in a subdirectories (for PRs and also one stable version). Msw was not working out of the box for this setup, but then I used #75 setup.

  serviceWorker: {
    url: `blabla/blabla-storybook/${STORYBOOK_SHA ?? "latest"}/mockServiceWorker.js`,
  }

Issues is, we are still getting an error that file does not exist in the path. But when I click on the link in the error message, I am taken to mockServiceWorker.js, so it obliviously exist in the location.

Uncaught (in promise) Error: [MSW] Failed to register a Service Worker for scope ('https://cdn.blabla.no/') with script ('https://cdn.blabla.no/blabla/blablabla-storybook/${commitSHA}/mockServiceWorker.js'): Service Worker script does not exist at the given path.

Did you forget to run "npx msw init <PUBLIC_DIR>"

We are using React with CRA and latest version of storybook.

Is anyone able to help with this, please? I have no clue at this point what might be the issue.

Demos Doesn't Work

Hi,
I want to mock hooks via react-query, and referred the demos in this package https://github.com/mswjs/msw-storybook-addon/tree/main/packages/docs/src/demos/react-query for implementation. However, I found the demos are not working either on my side, or on official page https://msw-sb.vercel.app/?path=/story/demos-react-router-rq--mocked-film-subsection.
We can see that for Default, success and error cases, they are actually all showing successful response from https://swapi.dev/api/films/, and the mocked data is not reflected at all.

Can anyone help me understand why this is happening and how can I properly mock react-query hooks ?

Add support for node environments

Storybook is awesome, and so is msw. I wrote @storybook/testing-react to allow users to completely reuse stories in tests, so that people don't need to create similar setups twice.
The library has incredible potential to work very efficiently with this addon, as long as it somehow supports non-browser environments. I have tweaked slightly with it and it has worked perfectly.

This is the change:

export function initializeWorker(options) {
  if (typeof global.process === 'undefined') {
    worker = setupWorker();
    worker.start(options);
  } else if (maybeSomeFlagHere) {
    worker = setupServer();
    worker.listen(options);
  }
}

After talking to @kettanaito, I am enthusiastic to bring a PR with the changes, as well as making more improvements to this addon. I'm wondering if the fallback to node support should be opt-in, opt-out or just default.

What are your thoughts on all of this? :D

Add Typescript declaration file

Storybook supports config files with TS if installed ts-node.
If exist a declaration file, TS users can use initializeWorker and mswDecorator when using preview.ts.

A livereload triggered by Webpack leaves the add-on in a bad state

The scenario:

  1. This is with @storybook/[email protected], [email protected] and [email protected]
  2. Force reload (or restart storybook): msw intercepts the backend call fine; everything works as expected
  3. Something in the code has been changed, which triggers storybook/webpack to livereload
  4. After livereload, the backend call is no longer intercepted by msw and causes a 404 (and a broken story)

CleanShot 2023-02-18 at 10 19 33@2x

The story:

import type { Entity } from '../data-service.js';
import type { StoryObj } from '@storybook/web-components';
import type { RouterLocation } from '@vaadin/router';
import { html } from 'lit';
import { rest } from 'msw';
import './register.js';

export default {
  title: 'Example/Details',
  args: {
    id: '0'
  },
  render: ({ id }: { id: string }) =>
    html`<mg-details .location=${{ params: { id: id } } as unknown as RouterLocation}></mg-details>`,
  parameters: {
    layout: 'fullscreen'
  }
};

const ENTITIES: Entity[] = [
  {
    id: '0',
    name: 'Foo bar',
    description: 'Non laboris aliqua esse consequat aliquip amet culpa do irure anim nulla veniam.',
    approved: true,
    createdAt: new Date().toISOString()
  }
];

export const Desktop: StoryObj = {
  parameters: {
    msw: {
      handlers: [rest.get('/api/entities/0', (_, res, ctx) => res(ctx.json(ENTITIES[0])))]
    }
  }
};

preview.ts:

initialize({
  onUnhandledRequest: ({ url: { href } }, print) => {
    if (href.includes('use.typekit.net') || href.endsWith('.js')) {
      return;
    }

    print.warning();
  }
});

export const decorators = [
  mswDecorator
];

bug: wrong base url

The default root path doesn't feel very good๏ผš
image

This can cause some problems, and if you don't deploy your site in the root path, the MSW will fail.

Readme discrepency

I have recently started to use the mws-storybook-addon but but found the following discrepency between the docs and the actual code and was wondering what the supposed correct variant is supposed to be.

Package Name Version
msw 0.35.0
mws-storybook-addon 1.4.1
storybook-react 6.3.12

On the official npm page the in-story configuration looks like this:
image

In this repo it looks like this:
image

When debugging, the actual code on my machine it looks like this:
image

So the variant which actually works is the first one. But the latter variant looks as if it was the intended improvement over the first design.

Require function is used in a way in which dependencies cannot be statically extracted

Hi,

Firstly thank-you for all the hard work on msw, it really makes mocking end-points really easy. I have found that on latest Storybook (6.4.9), there's a warning in the console:

./node_modules/msw-storybook-addon/dist/mswDecorator.js
25:45-52 Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

It is referring to this line. I have found simply commenting it out directly in my node_modules resolves the issue. Is there a reason for assigning require to __non_webpack_require__? Doesn't this overwrite the point of using __non_webpack_require__ in this instance? i.e.

  1. globalThis.__non_webpack_require__ = require
    is replaced by webpack to
    globalThis.__non_webpack_require__ = __webpack__require__
  2. Meaning the next line reads:
    const { setupServer } = __webpack_require__('msw/node')

I'm going to see if I can get this repo up and running in a fork and replicate the issue...

still seeing ModuleNotFoundError

I saw recent issue Webpack 5 Throws ModuleNotFoundError and related PR fix: add webpack5 support

I was led to this issue because following install and setup of msw-storybook-addon I see a similar error on storybook startup, ModuleNotFoundError: Module not found: Error: Can't resolve 'http' with the same long sequence of

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "http": require.resolve("stream-http") }'
	- install 'stream-http'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "http": false }

However I have msw-storybook-addon version 1.4.1, according to the PR that should be released. Perhaps the issue here is subtly different?

% grep msw-storybook-addon package.json
    "msw-storybook-addon": "^1.4.1",

and from yarn.lock,

msw-storybook-addon@^1.4.1:
  version "1.4.1"
  resolved "https://registry.yarnpkg.com/msw-storybook-addon/-/msw-storybook-addon-1.4.1.tgz#7a12cf0116a7af7f1ce1b652838ded161296e963"

Allow to initialize the worker with request handlers

I have an array of request handlers that covers all endpoints for the whole app. I use these as default responses in unit tests (so I don't have to constantly define handlers for endpoints like /notifications etc) and override them when necessary.
However, I wasn't able to achieve that with this plugin. The overriding & merging of handlers work differently here and it doesn't exactly fit my needs.

What I want is to have an array of default handlers and be able to override specific endpoints in certain stories. I managed to do that with this change:

// https://github.com/mswjs/msw-storybook-addon/blob/main/packages/msw-addon/src/mswDecorator.ts#L24

export function initialize(options?: InitializeOptions): SetupApi {
  if (IS_BROWSER) {
    const { setupWorker } = require('msw')
    const worker = setupWorker()

-> 

export function initialize(options?: InitializeOptions, requestHanders: RequestHandler[] = []): SetupApi {
  if (IS_BROWSER) {
    const { setupWorker } = require('msw')
    const worker = setupWorker(...requestHanders);

I think it's a minor change that doesn't break the API and might be useful for others as well. Shall I create a PR for that or not? What do you think?

Webpack 5 Throws ModuleNotFoundError

Hi, I'm using Webpack^5.46.0, @storybook/react^6.3.4, msw-storybook-addon^1.2.0, and msw^0.35.0. I have Storybook configured to use Webpack 5. Whenever the Storybook dev server is running and I modify the preview to initialize the worker, it works. But when I shutdown the server and restart it, it breaks with the following error:

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it

This error is thrown multiple times for multiple packages. Has this been reported before? Any possible fix?

Storybook + msw + cypress

Hello. I connected your addon to the storybook. Everything works great. Now the problem is how you can use it in cypress tests. It appears that cypress does not intercept msw requests configured via msw-storybook-addon

The last declared `handler` is applied to all stories in a file.

I'm trying to use the MSW add-on with React Query. Am experiencing an issue wherein the last declared handler is what's used for all stories in a file.

// LoginForm.stories.tsx
import { LoginForm } from ".";
import { Meta, Story } from "@storybook/react";
import { rest } from "msw";

export default {
  title: "components/molecules/LoginForm",
  component: LoginForm,
  decorators: [
    (Story) => (
      <div className="flex flex-col justify-center py-12 sm:px-6 lg:px-8 min-h-screen bg-gray-50">
        <Story />
      </div>
    ),
  ],
  parameters: {
    // opt out of decorators rendering in code preview
    docs: {
      source: {
        type: "dynamic",
        excludeDecorators: true,
      }
    },
  },
} as Meta;

const Template: Story = () => <LoginForm />;

export const MockedDefault = Template.bind({});
MockedDefault.parameters = {
  msw: [
    // Loading state
    rest.post("/auth/login", (req, res, ctx) => {
      return res(ctx.delay(1000 * 60 * 60 * 60), ctx.status(200));
    }),
  ],
};

export const MockedError = Template.bind({});
MockedError.parameters = {
  msw: [
    // Error state
    rest.post("/auth/login", (req, res, ctx) => {
      return res(
        ctx.status(400),
        ctx.json({ message: "Incorrect email and/or password. xxxxx" })
      );
    }),
  ]
};

The "Error state" handler is what's applied to all stories in the file. If I swap "Loading state" and "Error state", it's the "Loading state" that's applied to all stories in the file. As an additional check, I changed the endpoint of MockedDefault's handler to some/other/endpoint, I still receive the response from the "Error state" handler. My assumption is MockedDefault should throw an error, since the story has an unhandled request?

This is what my .storybook/preview.js looks like:

import "../src/index.css";
import { BrowserRouter as Router } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import { initialize, mswDecorator } from "msw-storybook-addon";

// Initialize MSW
initialize();

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  options: {
    storySort: {
      order: ["Introduction", ["Intro", "Installation"], "components"],
    },
  },
  layout: "fullscreen",
};

const queryClient = new QueryClient();

export const decorators = [
  mswDecorator,
  (Story) => (
    <QueryClientProvider client={queryClient}>
      <Router>
        <Story />
      </Router>
    </QueryClientProvider>
  ),
];

Packages:

Package Version
MSW 0.35.0
msw-storybook-addon 1.4.1
storybook/react 6.3.12
react-query 3.33.1

Use the new framework API?

Hey fellow maintainers ๐Ÿ‘‹ !
I'm one of the Storybook maintainers. I focus primarily on documentation and community outreach. I'm opening up this issue and letting you know that the Storybook API for building addons is currently being updated to factor in the changes introduced by the upcoming 7.0 release. If you're interested in making the required changes to help the community from benefiting from using your addon, we've prepared an abridged guide for the required changes here, and if you have any questions or issues, please reach out to us in the #prerelease channel in our Discord Server.

Looking forward to hearing from you and hopefully getting his addon updated as well for 7.0.

Hope everyone has a fantastic day.

Stay safe

Specify strict version range for the "msw" peer dependency

Current behavior

The msw package is specified as a peer dependency of the add-on using a wildcard as the expected version range:

"peerDependencies": {
"msw": "*"
},

Expected behavior

The latest version of msw is specified as the acceptable range:

{
  "peerDependencies": {
    "msw": "^0.29.0"
  }
}

Motivation

Using a wildcard version range leaks breaking changes from MSW to the add-on users who are using a version of the add-on that hasn't yet been optimized for the breaking change. Restricting and maintaining the peer dependency's version range will allow us to distribute breaking changes properly.

Story not waiting for MSW to be ready before running

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.