Gitlab CI breaking on unit tests - javascript

Two of my tests are passing locally but not on GitlabCI, and for the life of me I cannot figure out why.
I have read here that macOS is not case sensitive, and the ci might be, so I double checked all the spelling and everything came looks just fine.
Here's the filesystem layout:
src/
App.jsx
App.test.jsx
components/
A/
index.js
B/
C/
CContainer.jsx
CContainer.test.jsx
Below is the error code:
FAIL src/components/A/B/C/CContainer.test.jsx
● Test suite failed to run
Cannot find module './CContainer' from 'CContainer.test.jsx'
at Resolver.resolveModule (../../../tmp/node_modules/jest-resolve/build/index.js:179:17)
at Object.<anonymous> (src/components/A/B/C/CContainer.test.jsx:3:29)
My code from CContainer.test.jsx:
import React from 'react';
import { shallow } from 'enzyme';
import { CContainer } from './CContainer';
const props = {
saveTempUser: () => '',
history: {},
user: {},
};
it('renders without crashing', () => {
shallow(<CContainer {...props} />);
});
The two files are siblings, and CContainer is simply:
...
export class CContainer extends Component {
...
}
...
export default connect(mapStateToProps, mapDispatchToProps)(CContainer)
What's weird is in the top level App.test.js the same file causes a problem:
FAIL src/App.test.js
● Test suite failed to run
Cannot find module './B/C/CContainer' from 'index.js'
at Resolver.resolveModule (../../../tmp/node_modules/jest-resolve/build/index.js:179:17)
at Object.<anonymous> (src/components/A/index.js:4:29)
And index.js is just:
import ConnectedCContainer, { CContainer} from './B/C/CContainer'
...
export {
...
ConnectedCContainer,
CContainer
}.
Any ideas? I'm currently configuring a local runner to try and debug and will come back with more info when I have some.

Turns out it was the case of the file that was different than what was in git. Removing the file from git and adding a new file with the desired case fixed the problem.

Related

"Uncaught TypeError: WEBPACK_IMPORTED_MODULE is undefined" error after upgrading to create-react-app 5 and Webpack 5

In the midst of upgrading a project from CRA 4 to CRA 5, which bumps Webpack from 4 to 5 in the process. Specifically, the upgrade was from:
react-scripts: 4.0.3 to 5.0.0
webpack: 4.33.0 to 5.68.0
Everything's working except one issue that I'm having no luck tracking down the cause of.
One product within the app uses a set of nested index.ts files to manage exports, so they can be imported in one shot instead of a bunch of individual imports. The sequence looks like this:
//reports-async/containers/AggregatedReportDetail/AggregatedReportDetail.js
export const AggregatedReportDetail = () => {
...snip...
}
export default AggregatedReportDetail
-------------------------------
//reports-async/containers/index.ts
...snip...
export { default as AggregatedReportDetail } from './AggregatedReportDetail/AggregatedReportDetail'
export { default as TrendReportDetail } from './TrendReportDetail/TrendReportDetail'
...snip...
-------------------------------
//reports-async/index.ts
export * from './containers'
export * from './components'
export * from './models'
export * from './store'
export * from './types'
export * from './utils'
-------------------------------
//appMenu.js
import {
AggregatedReportDetail,
ReportPrint,
...snip...
} from './reports-async/containers'
This setup worked with CRA4/Webpack4, but with the update to CRA5/Webpack5, it compiles successfully but throws this error in the browser console:
Uncaught TypeError: _AggregatedReportDetail_AggregatedReportDetail__WEBPACK_IMPORTED_MODULE_4__ is undefined
I found that by changing the import in appMenu.js to directly import from the AggregatedReportDetail.js file instead of the module-level index.ts file, the error goes away (and it throws a similar error for the next imported component). IE, this works:
import AggregatedReportDetail from './reports-async/containers/AggregatedReportDetail/AggregatedReportDetail'
I'm assuming this is likely to be some kind of circular dependency issue or similar, but I can't seem to find any good info on how to resolve this without going back and manually changing 500+ imports to no longer use the index.ts files.
Am I missing something really obvious?

react helper import fails in production but works in development

I've got a file called "globalHelper.js" like this:
exports.myMethod = (data) => {
// method here
}
exports.myOtherMethod = () => { ... }
and so on...
I import my Helper in other files like this:
import helper from "../Helper/globalHelper";
Now there is the problem:
In the past, everything worked with that when running my build-script:
"build": "GENERATE_SOURCEMAP=false react-scripts build"
but for some reason, when I run "npm run build", I get the error:
Attempted import error: '../Helper/globalHelper' does not contain a default export (imported as 'helper')
However, when I simply start my development server (npm start), everything works just fine.
I already tried to import like import { helper } from "../Helper/globalHelper";, but that did not work either.
Does someone know how to solve that?
try exporting like this with ES6 syntax
export const myOtherMethod = () => { ... }
then
import { myOtherMethod } from '...your file'
The method you are using exports.functionName is CJS. you need to use require to get the methods.
The CommonJS (CJS) format is used in Node.js and uses require and
module.exports to define dependencies and modules. The npm ecosystem
is built upon this format.
If you want to use module method you can do this.
export { myOtherMethod1, myOtherMethod2 }
then you import like this.
import * as Module from '....file name'
Since you've not export default you should import the function with {} like :
import {helper} from "../Helper/globalHelper";

How to get Jest to find modules imported in components?

When I try to run npm jest, the test suite fails to run and gives this error message:
Cannot find module 'app/utils' from 'MoreModal.js'
1 | import React from 'react';
2 | import { Link } from 'react-router';
> 3 | import { fn, api } from 'app/utils';
My jest config is
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/js/__tests__/setup/setupEnzyme.js",
"testPathIgnorePatterns": ["<rootDir>/js/__tests__/setup/"],
},
I've tried fiddling with moduleDirectories, but that does nothing.
My enzyme setup is
Enzyme.configure({ adapter: new Adapter() });
The test:
describe('<MoreModal />', () => {
describe('render()', () => {
test('renders the component', () => {
const wrapper = shallow(<MoreModal />);
const component = wrapper.dive();
expect(toJson(component)).toMatchSnapShot();
})
})
})
How do I get jest to correctly find the modules in the component?
Your bundler probably has something set up for resolution to make app/utils work like that.
If you're using Webpack, https://www.npmjs.com/package/jest-webpack-resolver will help you wire up Jest to use the same resolution rules.
You can try the following:
"jest":{
"modulePaths": [
"<rootDir>/app"
],
"moduleDirectories": [
"app"
]
}
According to Jest documentation:
An alternative API to setting the NODE_PATH env variable, modulePaths
is an array of absolute paths to additional locations to search when
resolving modules. Use the string token to include the path
to your project's root directory. Example: ["/app/"].
It is also possible that your bundler resolves your modules relative to a different directory as very well mentioned by #AKX. Is "app/utils" in the root folder of your project or inside a different one? If that's the case your jest configuration should match your bundler's.

Enzyme render fails when importing image with webpack while testing with Jest

I want to test if a simple component renders (as I'm still figuring out Jest). The application itself loads an image with webpack to display the logo.
When I try to mount/render/shallow the stateless component, Jest throws an error.
FAIL src/components/blog/blogList.spec.jsx
● Test suite failed to run
/home/requinard/Projects/manus-frontend/src/img/manus_logo.png: Unexpected character '�' (1:0)
> 1 | �PNG
| ^
2 |
3 |
4 | IHDR��G} pHYs.#.#x�?vtEXtSoftwareAdobe ImageReadyq�e<K�IDATx��] \�����=)DY
It seems it's trying to load the image and failing at that. How can I stop Jest from loading the image for any component, or make it load the image so that it'll still render.
Stateless component:
import React from 'react';
PropTypes from 'prop-types';
import { BlogPostHeader } from './blogPostHeader';
import './blog.scss';
export const BlogList = props => (
<div className="blog-list">
{props.posts.map((post, key) => <BlogPostHeader post={post} key={key} className="blog-list-item" />)}
</div>
);
BlogList.propTypes = {
posts: PropTypes.array,
};
The test for the stateless component
import React from 'react';
import { render } from 'enzyme';
import { BlogList } from './BlogList';
describe('<BlogList >', () => {
it('should render in enzyme', () => {
const wrapper = render(<BlogList />);
expect(wrapper).toBeTruthy();
});
});
The component rendering the image (simplified):
import logo from '../img/logo.png';'
<div className="logo-container"><img src={logo} className="logo-child" /> </div>
For mocking images and other static assets, they actually have an item in the wiki.
https://facebook.github.io/jest/docs/webpack.html
I did not note that <rootDir> gets replaced by Jest itself, and you HAVE to include it yourself.
So with a file structure of
config \
jest \
fileMock.js
styleMock.js
src |
package.json
I have to include the following lines in package.json
"moduleNameMapper": {
"\\.(css|scss|less)$": "<rootDir>/config/jest/styleMock.js",
"\\.(png|jpg|gif|ttf|eot|svg)$": "<rootDir>/config/jest/fileMock.js"
}
Adding some more details to what #Samyn mentioned.
You need to have this entry in your package.json
"jest": {
"modulePaths": [
"__mocks__"
],
"moduleNameMapper": {
"\\.(css|sass|scss)$": "styleMock.js",
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "fileMock.js"
},
"testPathIgnorePatterns": [
"/node_modules/",
"/build/"
]
}
you need to have the mocks folder mentioned in the config, in the same folder as this package.json.
Create the styleMock.js and fileMock.js files inside them.
/* styleMock.js contents */
module.exports = {}
/* fileMock.js contents */
module.exports = 'test-file-stub';
If the image is required, you can mock it, like:
import mockery from "mockery";
mockery.enable();
mockery.registerMock('../img/logo.png', 0)
Find more info here https://www.npmjs.com/package/mockery
In case you are using create-react-app and changed scripts section in package.json to include enzyme (that's how started to get the same error and I got to this question), you don't need to do that, create-react-app already includes jest and enzyme, see https://github.com/facebook/create-react-app/issues/2564

jquery doesn't work with jsdom/enzyme

I have a minimum test react app with following component:
import React from 'react';
import $ from 'jquery';
export default class App extends React.Component {
componentDidMount() {
console.log('componentDidMount', $('#helloDiv').length);
}
render() {
return <div id='helloDiv'>
Hello React!
</div>;
}
}
this works fine when loading it in browser (Chrome). The console.log() in componentDidMount() prints out 1 helloDiv element found
However, if I run the test using mocha + enzyme + jsdom, the same console.log() in App component prints out 0:
import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import App from '../src/client/app/first'
describe('first test', () => {
it('should pass', () => {
const app = mount(<App />);
expect(app.find('#helloDiv').length).to.eq(1);
});
});
Note: I don't have problem with this unit test, it's passing. The real problem is when < App /> is mounted using enzyme, componentDidMount() is called but the console.log() statement inside it prints out 0, instead of 1
Here is how I run mocha:
mocha --require enzyme/withDom --compilers js:babel-core/register test/index.test.js
Any idea why jquery selector doesn't find anything in the test? It should not be mocha issue because the same issue happens if I change to jest
Finally found the issue:
Enzyme mount(<SomeComponent />) by default will do full DOM rendering but not insert the rendered component into current document (JSDom). That's why jQuery cannot find any element in current document
To do full DOM rendering AND attach to current document:
mount(<SomeComponent />, { attachTo: document.getElementById('app') });
Where app is empty div available when jsdom is setup:
global.document = jsdom('<html><head></head><body><div id="app" /></body></html>');
There needs to be some setup done before you could jsdom with jquery in node-env.
Try this if it helps.
Create a test helper file like this -
test_helper.js
import _$ from 'jquery';
import jsdom from 'jsdom';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.defaultView;
global.navigator = global.window.navigator;
const $ = _$(window);
chaiJquery(chai, chai.util, $);
export {expect};
While running -
mocha --require enzyme/withDom --compilers js:babel-core/register --require test/test_helper.js test/index.test.js
or another way use jsdom-global without test_helper.js file.
npm install --save-dev jsdom-global
Then :
import 'jsdom-global/register';
//at the top of file , even , before importing react
I couldn't get Phuong Nguyen's answer to work. I did find the relevant page in the enzyme docs. I ended up with something like, based on the final example on that page:
const div = global.document.createElement('div');
global.document.body.appendChild(graphDiv);
const wrapper = mount(<SomeComponent />, { attachTo: div }); // same as the other answer
// clean up after ourselves
wrapper.detach();
global.document.body.removeChild(div);

Categories

Resources