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

Vite + Storybook 6.3 + Jest: "global is not defined" #15391

Open
adiun opened this issue Jun 27, 2021 · 17 comments
Open

Vite + Storybook 6.3 + Jest: "global is not defined" #15391

adiun opened this issue Jun 27, 2021 · 17 comments

Comments

@adiun
Copy link

adiun commented Jun 27, 2021

Describe the bug
I am trying to get Vite + Storybook 6.3 working with some existing stories. These stories use jest.fn() as mocks. I previously added this in preview.js to get jest.fn() working within storybook:

import jest from "jest-mock";
window.jest = jest;

But when using the Vite integration I am now getting an error when running storybook:

Uncaught ReferenceError: global is not defined
    at ../node_modules/jest-mock/build/index.js (index.js:948)
    at __require2 (chunk-IHTDASF6.js?v=3b66b68d:17)
    at dep:jest-mock:1

To Reproduce
I am trying vite with storybook 6.3 and npm 7.19 workspaces. My repro repo is here: https://github.com/adiun/vite-monorepo:

  1. Ensure you have npm 7.19 installed for the workspaces functionality
  2. In the root, run npm i
  3. In the app folder, run npm run storybook.
  4. In the browser console for storybook you will see:
Uncaught ReferenceError: global is not defined
    at ../node_modules/jest-mock/build/index.js (index.js:948)
    at __require2 (chunk-IHTDASF6.js?v=3b66b68d:17)
    at dep:jest-mock:1

System

Environment Info:

  System:
    OS: macOS 11.3
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
  Binaries:
    Node: 14.16.0 - /usr/local/bin/node
    npm: 7.19.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 91.0.4472.114
    Edge: 91.0.864.48
    Firefox: 88.0.1
    Safari: 14.1
  npmPackages:
    @storybook/addon-docs: 6.3.0 => 6.3.0 
@adiun
Copy link
Author

adiun commented Jun 27, 2021

I was able to get a little further by mapping global to window in my main.js:

  async viteFinal(config) {
    return {
      ...config,
      define: {
        ...config.define,
        global: "window",
      },
    };
  }

Storybook loads now, but now I get this error:
Cannot read property 'fn' of undefined (the jest object which should be coming from jest-mock is undefined here)

image

Note I also tried adding jest-mock to optimizeDeps and that didn't have an effect either.

@adiun
Copy link
Author

adiun commented Jun 27, 2021

Alright, I finally fixed this... jest-mock doesn't explicitly provide a default export so in storybook's preview.js this is what I have:

import * as jest from "jest-mock";
window.jest = jest;

This combined with assigning global to window in main.js fixed this issue.

preview.js

import * as jest from "jest-mock";
window.jest = jest;

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

main.js

module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
  core: {
    builder: "storybook-builder-vite",
  },
  async viteFinal(config) {
    return {
      ...config,
      define: {
        ...config.define,
        global: "window",
      },
      esbuild: {
        ...config.esbuild,
        jsxInject: `import React from 'react'`,
      },
    };
  },
};

Updated my repo too

@adiun adiun closed this as completed Jun 27, 2021
@shilman
Copy link
Member

shilman commented Jun 28, 2021

cc @eirslett

@adiun
Copy link
Author

adiun commented Jul 3, 2021

Following up on this... it's actually a bad idea to redefine global like this in main.js:

define: {
  ...config.define,
  global: "window",
},

The Vite docs even mention this.

The problem is that when running a build-storybook, any references to global in libraries like @storybook/client-logger will be rewritten to window and it will break since it's running in node.

So I removed this code in main.js to get build-storybook working and added this hack to .storybook/preview-head.html to get start-storybook working:

<script>
  window.global = window;
</script>

main.js:

module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
  core: {
    builder: "storybook-builder-vite",
  },
  async viteFinal(config) {
    return {
      ...config,
      esbuild: {
        ...config.esbuild,
        jsxInject: `import React from 'react'`,
      },
      rollupOptions: {
        ...config.rollupOptions,
        // Externalize deps that shouldn't be bundled
        external: ["react", "react-dom"],
        output: {
          // Global vars to use in UMD build for externalized deps
          globals: {
            react: "React",
            "react-dom": "ReactDOM",
          },
        },
      },
    };
  },
};

preview.js:

import * as jest from "jest-mock";
window.jest = jest;

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

preview-head.html:

<script>
  window.global = window;
</script>

@shilman
Copy link
Member

shilman commented Jul 4, 2021

cc @eirslett

@eirslett
Copy link
Contributor

eirslett commented Jul 4, 2021

Maybe we should document this in the README - a section about troubleshooting and potential workarounds? We could inject window.global = window into the iframe, generated by the builder itself, but I suspect that we may encounter edge cases where that's not desirable?
I guess the problem here is that Jest is written to be run in Node.js, where global is a variable. I suppose the "correct" solution would be to fix Jest, so it uses globalThis?

@IanVS
Copy link
Member

IanVS commented Mar 10, 2022

FWIW, jest does now use globalThis, but only in the alpha 28 versions so far.

@tobiasdiez
Copy link
Contributor

Jest v28 is now released.

I've encountered a similar issue in the context of nuxt3, where the global imports in https://github.com/storybookjs/storybook/blob/next/app/vue3/src/client/preview/globals.ts make problems to hoist the preview, since depending on its configuration nuxt will render the page either on the serverside (node) or in the browser.

To support these use cases, it would be nice if globalThis is used everywhere. Can this issue maybe be reopened?

@IanVS
Copy link
Member

IanVS commented May 1, 2022

@tobiasdiez I'm not familiar with nuxt, but as I understand, using the global npm package should work because it tests whether it is running in an environment with global or window. https://github.com/Raynos/global/blob/master/window.js

Do you have a minimal reproduction we could take a look at? Is your issue related to jest, or the import you linked in vue3?

@tobiasdiez
Copy link
Contributor

tobiasdiez commented May 1, 2022

Nuxt tries to cover a few distribution and render channels (e.g. service workers) and for this reason adds its own polyfills, but global in a cjs file is not yet supported nuxt/nuxt#12830. One can workaround this issue by an adding custom alias that essentially redirects it to globalThis, see the instructions at nuxt-modules/storybook#385. But it would be nice if this workaround wouldn't be needed at all and storybook would rely directly on the more modern globalThis.

Yes, my use case is completely independent of jest.

@IanVS
Copy link
Member

IanVS commented May 1, 2022

I think maybe it is worth opening a new issue proposing a move to globalThis. But, it doesn't work in internet explorer, so it might be a non starter for now. Or, maybe a polyfill can be used there. Good to discuss in an issue, I think.

@Juice10
Copy link

Juice10 commented May 8, 2022

For anyone using yarn, overriding the jest-mock version using resolutions in your package.json might work for you to get the latest version of jest-mock which doesn't have the global issue.

"resolutions": {
    "jest-mock": "^28.1.0"
  },

@larsenwork
Copy link

larsenwork commented May 17, 2022

For anyone using yarn, overriding the jest-mock version using resolutions

Or with overrides in npm, confirmed working.

@csantos1113
Copy link

For anyone using yarn, overriding the jest-mock version using resolutions in your package.json might work for you to get the latest version of jest-mock which doesn't have the global issue.

"resolutions": {
    "jest-mock": "^28.1.0"
  },

I'm confused, if this is the fix, couldn't we open a PR here in storybook to upgrade the dependency

├─ @storybook/addon-interactions@npm:7.0.0-alpha.41
│  └─ jest-mock@npm:27.4.0 (via npm:^27.0.6)

Or what else are we missing?

@IanVS
Copy link
Member

IanVS commented Oct 24, 2022

Yes, I've been working on upgrading jest in the storybook packages, but it's a bit complex because of the way that jest types are global and the interactions with jest-dom. I'm waiting for some feedback on testing-library/jest-dom#483.

@bryanjtc
Copy link
Member

Any update on this?

@IanVS
Copy link
Member

IanVS commented Mar 23, 2023

Unfortunately jest-dom seems to be essentially unmaintained, and the Storybook team has not been prioritizing testing package changes like storybookjs/expect#12, so I'm still a bit stuck, unfortunately.

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

No branches or pull requests

9 participants