Imported styles object is empty in Jest - css

I have a component:
import React from 'react';
import * as styles from './RedComponent.css';
class RedComponent extends React.Component {
render () {
return <div className={ styles.red }></div>;
}
}
This is the test case:
describe('Test suite', () => {
test('RedComponent tests', () => {
const wrapper = shallow(<RedComponent />);
});
console.log(wrapper.debug());
gives
<div className={[undefined]}></div>
instead of
<div className="RedComponent__red"></div>
If I console the imported styles I get
console.log(styles); // {default: {}}
This is only in Jest test cases. Style is not undefined when the app renders in browser.
My jest config:
{
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules"
],
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
},
"setupFiles": [
"./test-setup.js"
],
"collectCoverageFrom": [
"src/**/*.{js}",
"!**/node_modules/**"
],
"testEnvironment": "node",
"transform": {
"^.+\\.js$": "babel-jest",
"\\.(md|ttf|txt|eot|ico|otf|svg|png|gif|woff2|woff|jpeg)$": "./file-transformer.js"
}
}
Using jest v21.2.1, identity-obj-proxy v3.0.0 and React v16.0.0.

You have to change this line
import * as styles from './RedComponent.css';
to this:
import styles from './RedComponent.css';
I assume that you are using css-loader or similar and this is just how the loader works.

Maybe worths to check the example:
https://github.com/keyanzhang/jest-css-modules-example/
I think your moduleNameMapper should be like this:
"^.+\\.(css|less)$": "identity-obj-proxy"

Create a jest/identity-obj-proxy-esm.js file with the following content:
// This works around the fact we use ES named exports for styles, e.g.:
// import * as styles from './styles.scss'.
// https://github.com/keyanzhang/identity-obj-proxy/issues/8
module.exports = new Proxy(
{},
{
get: function getter(target, key) {
if (key === '__esModule') {
// True instead of false to pretend we're an ES module.
return true;
}
return key;
},
},
);
Edit jest.config.js:
// jest.config.js
module.exports = {
...
moduleNameMapper: {
...
'\\.(css|scss)$': '<rootDir>/jest/identity-obj-proxy-esm.js',
}
};
🏆 João Vieira and https://github.com/keyz/identity-obj-proxy/issues/8#issuecomment-430241345

Related

how to setup antd less support with nextjs 12

im trying to setup nextjs 12 with ant design antd and in next.config.js when i try to setup withAntdLess it gives type error
Type '{}' is missing the following properties from type '{ esModule: boolean; sourceMap: boolean; modules: { mode: string; }; }': esModule, sourceMap, modules
although all props are optional according to next-plugin-antd-less docs
next.config.js file:
// #ts-check
// next.config.js
const withAntdLess = require('next-plugin-antd-less');
/**
* #type {import('next').NextConfig}
**/
module.exports =withAntdLess({
cssLoaderOptions: {},
// Other Config Here...
webpack(config) {
return config;
},
reactStrictMode: true,
});
I solved it using next-with-less https://github.com/elado/next-with-less
next.config.js
const withLess = require('next-with-less');
const lessToJS = require('less-vars-to-js');
const themeVariables = lessToJS(
fs.readFileSync(
path.resolve(__dirname, './public/styles/custom.less'),
'utf8'
)
);
module.exports = withLess({
...
lessLoaderOptions: {
lessOptions: {
javascriptEnabled: true,
modifyVars: themeVariables, // make your antd custom effective
localIdentName: '[path]___[local]___[hash:base64:5]',
},
},
...
})
Import your custom less file on top off the file _app.jsx
import 'public/styles/custom.less';
...
Import the default Antd less file on your custom less file: (in my case public/styles/custom.less)
#import "~antd/dist/antd.less";
....
Extra notes:
If you have an old implementation of Antd, you should remove the integration in your .babelrc
[
"import",
{
"libraryName": "antd",
"libraryDirectory": "lib",
"style": true
}
],
If you have an old implementation of Antd, you should remove the integration in your webpack zone in your next.config.js
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}

React-responsive-carousel styles not working

I am trying to add react-responsive-carousel to my NextJS project. When i run npm run dev everything seems to be fine, however my carousel is rendered with no styles.
import { Carousel } from 'react-responsive-carousel';
import 'react-responsive-carousel/lib/styles/carousel.min.css'; // requires loader
const MyCarousel = () => {
return (
<Carousel
autoPlay
interval={2500}
showArrows={true}
>
<div>content</div>
<div>content</div>
<div>content</div>
</Carousel>
)
}
Documentation says that styles need a loader so I configures next.config.js as follows
const withLess = require('#zeit/next-less');
const withCss = require('#zeit/next-css');
const withImage = require('next-images');
const theme = require('./app/styles/antdoverrides');
module.exports = withImage(
withCss({
cssModules: true,
optimizeFonts: false,
...withLess({
lessLoaderOptions: {
javascriptEnabled: true,
importLoaders: 0,
modifyVars: {
...theme,
},
},
cssLoaderOptions: {
importLoaders: 3,
localIdentName: '[local]___[hash:base64:5]',
},
webpack5: false,
webpack: (config, { isServer }) => {
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals),
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader',
});
}
return config;
},
}),
}),
);
still not getting desired result. Any hints appreciated
You need to import your styles in your _app.js file if you don't use the objects. Just in your _app make import of your styles, something like:
import "../styles/globals.css";
Also you need to npm run local or something like this, check your package.json file to run locally your project instead of build

vue mpa fails to compile when adding css to <style> tags

I have an MPA app, where vue.js is used as a part of the application. I have a very simple test set up, here:
relevant parts of my template
....
<div id='app-basket-checkout'>
<h1>Better Be Here</h1>
</div>
....
pageBasketCheckout.js (essentially my app.js)
import Vue from 'vue'
import AppBasketCheckout from './BasketCheckout.vue'
import './dummyScss.css'
Vue.config.productionTip = false
new Vue({
render: h => h(AppBasketCheckout)
}).$mount('#app-basket-checkout')
component
<template>
<div id="app-basket-checkout">
{{msg}}
</div>
</template>
<script>
export default {
name: 'AppBasketCheckout',
components: {
},
data() {
return {
msg: 'Hello'
}
}
}
</script>
<style scoped>
</style>
So the above code renders just fine in my front end. I end up with an extra div that has hello printed inside, well done.
However when I add css to the style tag:
<template>
<div id="app-basket-checkout">
{{msg}}
</div>
</template>
<script>
export default {
name: 'AppBasketCheckout',
components: {
},
data() {
return {
msg: 'Hello'
}
}
}
</script>
<style scoped>
body {
font-family: Arial, Helvetica, sans-serif;
line-height: 1.4;
}
</style>
This produces this error in chrome:
Uncaught Error: Cannot find module './BasketCheckout.vue?vue&type=style&index=0&id=2711cf65&scoped=true&lang=css&'
at webpackMissingModule (VM45512 BasketCheckout.vue:4)
at eval (VM45512 BasketCheckout.vue:4)
at Module../src/BasketCheckout.vue (pageBasketCheckout.bundle.js:40)
at __webpack_require__ (index.bundle.js:4312)
at eval (pageBasketCheckout.js:3)
at Module../src/pageBasketCheckout.js (pageBasketCheckout.bundle.js:29)
at __webpack_require__ (index.bundle.js:4312)
at checkDeferredModulesImpl (index.bundle.js:4453)
at webpackJsonpCallback (index.bundle.js:4435)
at pageBasketCheckout.bundle.js:9
Again this error only happens when adding css to the component. Here is my webpack.config.js:
const path = require('path');
const webpack = require('webpack')
const glob = require('glob')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
watch: true,
context: path.resolve(__dirname, 'uniquesite/uniquesite'),
mode: 'development',
entry: {
index: {
import: ['#babel/polyfill', './src/index.js'],
// dependOn: ['babel'],
},
pageProductDetails: {
import: ['#babel/polyfill', './src/pageProductDetails.js'],
dependOn: ['index'],
},
pageBasketCheckout: {
import: ['#babel/polyfill', './src/dummyScss.scss', './src/pageBasketCheckout.js'],
dependOn: ['index']
}
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'uniquesite/uniquesite/static/uniquesite/js/'),
},
plugins: [
new VueLoaderPlugin()
],
resolve: {
alias: {
jquery: "jquery/src/jquery",
'jquery-ui': "jquery-ui-dist/jquery-ui.js",
boostrap: "bootstrap/dist/js/bootstrap.bundle.js"
}
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
},{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
};
You'll note I've also tried importing a dummy .css file to ensure the style loader works, as I've seen one more SO question with a similar problem that solved it that way. That didn't work for me however.
Update 1
My current thinking is that the problem has to be happening in the VueLoaderPlugin. That plugin is reponsible for splitting the script into distinct parts for template, logic, and style. It looks like the style is not actually making it into the bundle. See below.
"use strict";
eval(
"__webpack_require__.r(__webpack_exports__);
/* harmony import */
var _BasketCheckout_vue_vue_type_template_id_2711cf65___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
/*! ./BasketCheckout.vue?vue&type=template&id=2711cf65& */
\"./src/BasketCheckout.vue?vue&type=template&id=2711cf65&\"
);
/* harmony import */
var _BasketCheckout_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
/*! ./BasketCheckout.vue?vue&type=script&lang=js& */
\"./src/BasketCheckout.vue?vue&type=script&lang=js&\"
);
Object(function webpackMissingModule() {
var e = new Error(
\"Cannot find module './BasketCheckout.vue?vue&type=style&index=0&lang=css&'\"
); e.code = 'MODULE_NOT_FOUND';
throw e;
}());
/* harmony import */
var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(
/*! !../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */
\"../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"
);
/* normalize component */
var component = (
0,_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_3__.default
)(
_BasketCheckout_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__.default,
_BasketCheckout_vue_vue_type_template_id_2711cf65___WEBPACK_IMPORTED_MODULE_0__.render,
_BasketCheckout_vue_vue_type_template_id_2711cf65___WEBPACK_IMPORTED_MODULE_0__.staticRenderFns,
false,
null,
null,
null)
/* hot reload */
if (false) { var api; }
component.options.__file = \"src/BasketCheckout.vue\"
/* harmony default export */
__webpack_exports__[\"default\"] = (component.exports);
//# sourceURL=webpack:///./src/BasketCheckout.vue?"
);
Scoped CSS rules only apply to the current component (and its child components' root nodes).
You are mounting your Vue instance at #app-basket-checkout, which is already inside a <body> element.
You can style <body>, but do it using a global stylesheet that is imported in your app.js, not a subcomponent.
Alternatively, you can apply a class-based style at a low level node within your Vue instance and likely deliver your desired styles.

Storybook custom webpack loading empty scss objects

I added a custom webpack.config.js file to my .storybook project so that I can import .scss files. This is what I added, straight from the storybook docs.
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = (storybookBaseConfig, configType) => {
// configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Make whatever fine-grained changes you need
storybookBaseConfig.module.rules.push({
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"],
include: path.resolve(__dirname, '../src')
});
// Return the altered config
return storybookBaseConfig;
};
Here's my story:
import React from 'react';
import { storiesOf } from '#storybook/react'; // eslint-disable-line import/no-extraneous-dependencies
import { action } from '#storybook/addon-actions'; // eslint-disable-line import/no-extraneous-dependencies
import { linkTo } from '#storybook/addon-links'; // eslint-disable-line import/no-extraneous-dependencies
import Button from './'
import ButtonStyles from './index.scss'
import ButtonCompareTrayStyles from './compare-tray.scss'
import ButtonCompareRemminderStyles from './compare-reminder.scss'
console.log({ButtonStyles, ButtonCompareTrayStyles, ButtonCompareRemminderStyles})
storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
.add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>)
.add('with default styles', () => <Button styles={ButtonStyles} onClick={action('clicked')}>Hello World</Button>)
.add('with CompareTray styles', () => <Button styles={ButtonCompareTrayStyles} onClick={action('clicked')}>Hello World</Button>)
.add('with CompareRemminder styles', () => <Button styles={ButtonCompareRemminderStyles} onClick={action('clicked')}>Hello World</Button>)
When I log some Button styles, it appears that each one of these objects is empty.
Why are these objects empty? How can I get scss working with storybook?
For everyone who has the same problems, I added the package #storybook/preset-scss and configured it the following way:
module.exports = {
"stories": [
"../src/**/*.stories.*",
"../src/**/*.story.*"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-actions",
"#storybook/addon-essentials",
"#storybook/addon-knobs",
{
name: '#storybook/preset-scss',
options: {
cssLoaderOptions: {
modules: true
}
}
},
]
}
That's it.

Integration testing of Relay containers with Jest against a working GraphQL backend not working

I'd like to implement the integration testing of my Relay containers against a running GraphQL backend server. I'm going to use Jest for this. I'd like to say that unit testing of React components works well as expected with my Jest setup.
Here's what I have in the package.json for the Jest:
"jest": {
"moduleFileExtensions": [
"js",
"jsx"
],
"moduleDirectories": [
"node_modules",
"src"
],
"moduleNameMapper": {
"^.+\\.(css|less)$": "<rootDir>/src/styleMock.js",
"^.+\\.(gif|ttf|eot|svg|png)$": "<rootDir>/src/fileMock.js"
},
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react/",
"<rootDir>/node_modules/react-dom/",
"<rootDir>/node_modules/react-addons-test-utils/",
"<rootDir>/node_modules/react-relay/"
]
}
Here's the .babelrc I'm using:
{
"presets": ["es2015", "react", "stage-0"],
"plugins": ["./babelRelayPlugin.js"]
}
Here's the test itself. It must make a request to `http://localhost:10000/q' GraphQL endpoint to fetch a simple piece that represents the info about the current user ('me').
jest.disableAutomock();
import React from 'react';
import Relay from 'react-relay';
import TestUtils from 'react-addons-test-utils';
import RelayNetworkDebug from 'react-relay/lib/RelayNetworkDebug';
RelayNetworkDebug.init();
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('http://localhost:10000/q')
);
describe('Me', () => {
it('can make request to /q anyway', () => {
class RootRoute extends Relay.Route {
static queries = {
root: (Component) => Relay.QL`
query {
root {
${Component.getFragment('root')}
}
}
`,
};
static routeName = 'RootRoute';
}
class AppRoot extends React.Component {
static propTypes = {
root: React.PropTypes.object,
};
render() {
expect(this.props.root).not.toBe(null);
expect(this.props.root.me).not.toBe(null);
expect(this.props.root.me.firstName).not.toBe(null);
expect(this.props.root.me.authorities[0]).not.toBe(null);
expect(this.props.root.me.authorities[0].authority).toEqual('ROLE_ANONYMOUS_AAA');
return (
<div>
{this.props.root.me.firstName}
</div>
);
}
}
const AppContainer = Relay.createContainer(AppRoot, {
fragments: {
root: () => Relay.QL`
fragment on Root {
me {
firstName
email
authorities {
authority
}
}
}
`,
},
});
const container = TestUtils.renderIntoDocument(
<div>
<Relay.RootContainer Component={AppContainer} route={new RootRoute()} />
</div>
);
expect(container).not.toBe(null);
});
});
The problem is that the test passes. But in my opinion it must fail at this line inside the render() expect(this.props.root.me.authorities[0].authority).toEqual('ROLE_ANONYMOUS_AAA');. It seems like the render() method is not executed at all.
I'm running Jest like this
./node_modules/.bin/jest
Does this all suppose to work at all?
Thank you.
This is possible, take a look on the code: https://github.com/sibelius/relay-integration-test
and on my blog post: https://medium.com/entria/relay-integration-test-with-jest-71236fb36d44#.ghhvvbbvl
The missing piece is that you need to polyfill XMLHttpRequest to make it work with React Native.
And you need to polyfill fetch for React web

Resources