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

Testing components that use CSS modules is failing #910

Open
Nojusle opened this issue Aug 8, 2024 · 5 comments
Open

Testing components that use CSS modules is failing #910

Nojusle opened this issue Aug 8, 2024 · 5 comments
Labels

Comments

@Nojusle
Copy link

Nojusle commented Aug 8, 2024

Environment

  • Operating System: Darwin
  • Node Version: v21.6.1
  • Nuxt Version: 3.12.1
  • CLI Version: 3.12.0
  • Nitro Version: 2.9.6
  • Package Manager: [email protected]
  • Builder: -
  • User Config: compatibilityDate, devtools, modules
  • Runtime Modules: @nuxt/test-utils/module@^3.14.0
  • Build Modules: -

Reproduction

https://codesandbox.io/p/devbox/nuxt-test-with-css-modules-sz2wlg?file=%2Fsandbox.config.json%3A4%2C25-4%2C37

Describe the bug

When running tests on components that uses CSS Modules, the $style attribute is consistently set to undefined, causing the tests to fail.

Additional context

There are no examples in the Nuxt test examples folder that include CSS modules. I've read the documentation for @nuxt/test-utils, and it doesn't mention how to use it with CSS modules. I tried googling it, but I couldn't find any information on how to test Vue components with CSS modules. I only found an unanswered question regarding the same issue, e.g., #804. Since CSS modules work with Nuxt 'out of the box,' I believe testing components that use CSS modules should also work seamlessly.

Logs

❯ test.nuxt.test.ts (1) 5010ms
   × my test 5009ms

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  test.nuxt.test.ts > my test
Error: Test timed out in 5000ms.
If this is a long-running test, pass a timeout value as the last argument or configure it globally with "testTimeout".
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
TypeError: Cannot read properties of undefined (reading 'someCssModuleClassName')
 ❯ Proxy._sfc_render app.vue:3:20
      1| <template>
      2|   <div :class="$style.someCssModuleClassName">
      3|     <NuxtWelcome />
       |                    ^
      4|   </div>
      5| </template>
 ❯ Proxy.clonedComponent.render node_modules/.pnpm/@[email protected]_@[email protected][email protected][email protected][email protected]_pla_3tccdhrwarphrx6od5vsqeppdq/node_modules/@nuxt/test-utils/dist/runtime-utils/index.mjs:142:37
 ❯ renderComponentRoot node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:6267:16
 ❯ ReactiveEffect.componentUpdateFn [as fn] node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4942:46
 ❯ ReactiveEffect.run node_modules/.pnpm/@[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:181:19
 ❯ instance.update node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5073:16
 ❯ setupRenderEffect node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5083:5
 ❯ mountComponent node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4851:7
 ❯ processComponent node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4805:9
 ❯ patch node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4334:11

This error originated in "test.nuxt.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
The latest test that might've caused the error is "my test". It might mean one of the following:
- The error was thrown, while Vitest was running this test.
- If the error occurred after the test had been completed, this was the last documented test before it was thrown.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 Test Files  1 failed (1)
      Tests  1 failed (1)
     Errors  1 error
   Start at  18:00:20
   Duration  5.24s
@dosubot dosubot bot added bug Something isn't working vitest-environment labels Aug 8, 2024
Copy link

dosubot bot commented Aug 8, 2024

Hey @Nojusle! I'm a bot that can help you solve bugs, answer questions, and become a contributor. I'm here to help you while waiting for a human maintainer.

To resolve the issue of the $style attribute being undefined when testing components that use CSS Modules in your Nuxt 3 project, you need to ensure that the @nuxt/test-utils/module is properly loaded and configured in your nuxt.config file.

Here are the key steps:

  1. Ensure that @nuxt/test-utils/module is added to the modules array in your nuxt.config file:
// nuxt.config.js
export default {
  modules: [
    '@nuxt/test-utils/module',
    // other modules
  ],
  // other configurations
}
  1. Use the startNuxtAndGetViteConfig function to start Nuxt and get the Vite configuration, which includes the necessary setup for handling CSS Modules:
import { startNuxtAndGetViteConfig } from './path/to/config';

async function setup() {
  const { nuxt, viteConfig } = await startNuxtAndGetViteConfig();
  // additional setup if needed
}

setup();

By ensuring that the @nuxt/test-utils/module is properly loaded and configured, the $style attribute should be correctly defined when testing components that use CSS Modules [1].

Additionally, you can mock the $style attribute in your tests to avoid it being undefined. Here is an example of how to mock a component with CSS Modules:

import { mockComponent } from '@nuxt/test-utils/runtime'

// Mock the component and its styles
mockComponent('MyComponent', {
  props: {
    value: String
  },
  setup(props) {
    return {
      $style: {
        myClass: 'mocked-class'
      }
    }
  }
})

// Now you can write your tests
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { it, expect } from 'vitest'
import MyComponent from '~/components/MyComponent.vue'

it('renders with mocked styles', async () => {
  const component = await mountSuspended(MyComponent)
  expect(component.vm.$style.myClass).toBe('mocked-class')
})

In this example, the $style attribute is mocked to return a predefined class name, ensuring that it is not undefined during the tests [2].

To continue talking to Dosu, mention @dosu.

@robm-masabi
Copy link

Well, I'm not convinced it's the proper fix solution, but you can get the correct values passed through by modifying this line from...

setup: setup ? (props: Record<string, unknown>) => wrappedSetup(props, setupContext) : undefined,

....to...

setup: setup ? (props: Record<string, unknown>) => wrappedSetup({ ...props, ...component.__cssModules }, setupContext) : undefined,

I say that I don't think its the correct fix, as this then only solve the issue for components with a setup script, but I couldn't expose this via the render context.

@robm-masabi
Copy link

robm-masabi commented Sep 21, 2024

OK, so not a fix, but a temporary workaround for anyone else stumbling on this, a small function which wraps mountSuspended and should work as a drop-in replacement to make for easy removal if/when the core bug is fixed.

import { mountSuspended } from '@nuxt/test-utils/runtime'

export const mountWithNuxt: typeof mountSuspended = async (component, options) => {
  return mountSuspended(component, {
    ...options,
    // @ts-expect-error Vue's TS doesn't like us specifying $style as an external prop
    props: {
      ...options?.props,
      $style: {},
    },
    attrs: {
      ...options?.attrs,
      $style: undefined,
    },
  })
}

@Nojusle
Copy link
Author

Nojusle commented Sep 23, 2024

Great, the workaround works for me. That's all I needed, thank you!

@kier-mc
Copy link

kier-mc commented Oct 30, 2024

Thanks @robm-masabi for the workaround.

I've been playing around with this locally for the last hour or so and I've noticed that this only seems to occur if you use $style to pass the module class in the template. If you declare or pass the style via <script setup> in the component being tested, the tests should run as expected:

<script setup lang="ts">
  // works with no changes in the template
    const $style = useCssModules();

  // ...or use :class="elementStyle" in the template
    const styles = useCssModules();
    const elementStyle = styles.elementClass;
</script>

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

No branches or pull requests

3 participants