Allows for classes bound to CSS modules to be used in all the same ways global CSS classes can be used in the context of vue-test-utils. Currently, components that utilize CSS modules cannot be tested in the same way as components using global classes.
Example: consider a scenario where the value of some props conditionally apply classes to an element
// ...
<div :class="[$style.selectionDisplay, inputBorder ? $style.inputBorder : '',
$style[`${inputScale}`], $style[`${inputDisplayColor}`]]"/>
// ...
The first problem is that I cannot use $style.selectionDisplay as a selector. In this scenario, I would be forced to either sacrifice the readability/flexibility of the test by using a combination of selectors based on the present structure of the html, or I need to add an ID to the div purely for the sake of testing (which feels a bit yucky).
The second problem is that once this div is "selected", none of the CSS module classes will be returned by wrapper.classes(). It is impossible to test the classes.
I think there is more than one acceptable approach to solving this problem. Two possible approaches (perhaps even in combination):
(1) A mounting config option for specifying use of CSS modules. If this option is enabled, perhaps CSS module classes could then be referenced like traditional classes in tests ('.selectionDisplay' vs $style.selectionDisplay).
(2) Allow $style.className to be leveraged in the same fashion '.className' is.
Perhaps someone else has an even better idea.
What version of the test utils are you using @gloomylumi ? You should just be able to use .selectionDisplay as a selector (at least in the latest release) the utils just map the module classes to classes. Is your CSS in your vue component?
The point about conditional classes is one I'm having as well - .classes() gives all the classes attached even if they're value is false (and so not applied), I'm currently stringifying the component and checking for the string to be there 馃槩
The CSS is in the component SFC. I just upgraded to 1.0.0-beta.29 and tried again. I'm still not having success. I admit to being rather new to Vue testing, so perhaps I am doing something wrong. I was unable to find any specific documentation around CSS modules (not for lack of trying). I posed questions about selecting CSS module classes in both the official Vue forums and discord; no one could provide answers. Here is a specific example of the behavior I'm seeing:
template
<div
v-if="showOptions"
id="optionsList"
:class="$style.optionsList">
<div
v-for="(option, index) in options"
:class="$style.option"
:key="index"
@click="selectOption(option)">
<p v-if="option.displayValue">
{{ option.displayValue }}
</p>
<p v-else>
{{ option }}
</p>
</div>
</div>
test
test('renders option string when passed array of strings for options', () => {
const providedProps = {
options: ['foo'],
}
const wrapper = shallowMount(DropdownInput, {
propsData: providedProps,
})
wrapper.setData({ showOptions: true })
expect(wrapper.find('.option').text()).toContain(providedProps.options[0])
})
test failure log
[vue-test-utils]: find did not return .option, cannot call text() on empty Wrapper
59 | wrapper.setData({ showOptions: true })
60 |
> 61 | expect(wrapper.find('.option').text()).toContain(providedProps.options[0])
| ^
62 | })
If I add a global CSS class to the option div and select using that class, the test passes. The issue appears to be specific to the use of CSS modules.
Ah ok so, I'm assuming you're using Jest to run these tests?
If you add the vue-jest transformer these should start working for you. It parses the vue components into a Javascript file that Jest can deal with (in this case that means turning all of the $style.className into .className so that you can use those classnames in your test.
Turns out vue-jest's experimentalCSSCompile was disabled in the jest config file. Whoops. I'm gonna go ahead and close this and pretend this never happened...
@hewIngram For my own edification, where could I have found documentation on the expected behavior of CSS module classes (i.e. selecting $style.className with ".className")?
We're using vue-jest transformer and I've tried setting experimentalCSSCompile to true. However, that didn't fix the problem for us. Has anyone else faced this issue? If yes, do you have any recommendations?
Hey @SachaZvetelman,
To clarify if you switch to using non-moduler css it works fine? What versions of vue and vue-jest are you using?
Hey @hewIngram, thanks for your quick response!
Basically, if I use CSS modules, I cannot see the classes populated in my tests. They just come back empty.
If I switch to scoped CSS and change the usage of styles from for example :class="$styles["my-class"] to class="my-class", they are populated in our tests.
We're using:
vue: 2.6.11
vue-jest: 3.0.6
Ah ok cool - so the feature never made it into recent builds (from a quick look it looks like theres been a bunch of restructuring). We're using 4.0.0-beta.2 in product as it has this feature in but... that's not great.
@eddyerburgh or @JessicaSachs (or anybody else!) can I get this into an upcoming release? Happy to make a new PR for it (given it looks like a bunch has changed in the repo structure and stuff)
@hewIngram, thanks for the help. If I use 4.0.0-beta.2 though, it fails when trying to use imported SCSS variables. If I run the application rather than the tests, it works. With the older version (3.0.6) the imports were not throwing any problems.
I'm not finding any documentation specific to the 4.0.0-beta.2 version. Do you know what could be happening, or do you have any public repo I could check to see how you have configured it? I'm leaving my configs below just in case. Thanks again for your help.
Component.vue:
<style lang="scss" module>
$form-input-colour: $grey--dark; // <----- fails here with 'Error: Undefined variable: "$grey--dark".'
$form-input-bg: $white;
...
</style>
jest.config.js:
```:
module.exports = {
moduleFileExtensions: [
'js',
'json',
'vue'
],
transform: {
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub'
},
transformIgnorePatterns: [
'node_modules/(?!(lodash-es)/)'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^~include-media/(.*)$': '<rootDir>../../node_modules/include-media/$1',
'^~@company/(.*)$': '<rootDir>../../node_modules/@company/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
globals: {
'vue-jest': {
hideStyleWarn: true,
experimentalCSSCompile: true
}
},
testURL: 'http://localhost/'
};
`vue.config.js`:
const magicImporter = require('node-sass-magic-importer');
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('scss-importer')
.test(/.scss$/)
.use('importer')
.loader('sass-loader')
.options({
importer: magicImporter(),
data: @import "@/assets/scss/common.scss";
});
}
};
```
@hewIngram Any news on when this may be scheduled for release? If we can help on this at all, just let us know (and thanks for your help!)
SCSS modules not working here either
Finally got around to looking at this @SachaZvetelman - if you have a look here: https://github.com/hewIngram/vue-jest/tree/test-scss-variable-support
I've tweaked the ScssModule test to use a imported SCSS variable and it works fine. If you use the 3.0.5 version of vue-jest do you still see this error? How are you importing your $grey--dark variable?
@beamsies and @ashleynolan if it's SCSS that's the issue for you too could you add some extra details?
From looking through the 3.0.5 code it supports modules fine (although I can't see any tests specifically testing variables in css modules) as well as SCSS and SCSS as a module.
The only thing I could spot missing (and the thing I was after when I made the PR that resulted in that weird hanging 4.0.0-beta.2 branch I mentioned) was support for external css files - I'll open a PR now to add that in.
Although actually looking at the vue-jest repo theres v4 and v5 branches kicking about (that have external file support) with no related releases on npm so... not really sure what the plan is for releasing any of those though 馃憖
I had the same problem with Vue 3. I solved it mocking like that:
import { config } from '@vue/test-utils'
config.global.mocks = config.global.mocks || {}
config.global.mocks.$style = new Proxy({}, {
get(_, name) {
if (name !== '_isMockFunction') {
return name
}
}
})
The proxy will return any value based on the key passed and I needed to skip _isMockFunction to avoid an error.
Most helpful comment
I had the same problem with Vue 3. I solved it mocking like that:
The proxy will return any value based on the key passed and I needed to skip
_isMockFunctionto avoid an error.