I am new to Jest, I am trying to mock my context HOC function to test my component
Here is my folder structure
Folder Structure
Basically I am trying to mock the homeContext.js file which is being used by my HomeScreen component. As per the docs I have created a __mocks__ folder under the contexts folder and created a homeContext.js file
Here are the contents of the files:
/contexts/__mocks__/homeContext.js
const mock = jest.fn().mockImplementation(() => {
console.log('Mocking');
return {
withHomeContext: jest.fn(() => {}),
};
});
module.exports = mock;
contexts/homeContext.js
export const withHomeContext = ChildComponent => props => {
console.log('Not Hitting');
return (
<HomeContext.Consumer>
{context => <ChildComponent {...props} homeContext={context} />}
</HomeContext.Consumer>
);
};
Here is how I am trying to use it in my scenes/home/index.test.js
jest.mock('../../contexts/homeContext');
import React from 'react';
import {mount} from 'enzyme';
import HomeScreen from './index';
describe('<HomeScreen /> renders', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<HomeScreen />);
expect(wrapper).toBeDefined();
});
})
This is how the component which I am testing uses it
import * as React from 'react';
//Context
import {withHomeContext} from '../../contexts/homeContext';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
relatedItems: [],
};
}
render() {
//Some render logic
}
}
export default withHomeContext(HomeScreen);
Please help me why my __mocks__/homeContext.js never gets hit, it is hitting the actual file, which console logs ('Not Hitting')
Related
I am getting Error: Uncaught [Error: Child(...): Nothing was returned from render when running a test file for the Parent component.
These are the relevant files
/components/Page/Children/Child.js
import React from "react"
export default function Child() {
return <div>abc</div>
}
/components/Page/Children/index.js
export { default } from "./Child"
/components/Page/Parent.js
import React from "react"
import Child from "./Children"
export default class Parent extends React.Component {
constructor(props) {
super(props)
}
render() {
return <Child />
}
}
/components/Page/_tests_/Parent.spec.js
import Parent from "../Parent"
jest.mock("../Children")
describe("<Parent/>", () => {
let wrapper
let props = ...
describe("render", () => {
it("renders", () => {
wrapper = mount(<Parent props={props} />)
expect(wrapper).toMatchSnapshot()
})
})
changing the Child.js file to a react class component (as updated below) resolves the issue but I do not understand why that would be.
/components/Page/Children/Child.js
import React from "react"
export default class Child extends React.Component {
render() {
return <div>abc</div>
}
}
The reason why this error happened is you are mocking the ./Children module and you didn't provide a mocked Child component. So, if you want to mock the Child component, you need to provide a return statement.
E.g.
Parent.spec.js:
import React from 'react';
import Parent from './Parent';
jest.mock('./Children', () => {
return jest.fn(() => <div>mocked child</div>);
});
describe('<Parent/>', () => {
let wrapper;
let props = {};
describe('render', () => {
it('renders', () => {
wrapper = mount(<Parent props={props} />);
expect(wrapper).toMatchSnapshot();
});
});
});
unit test result:
PASS src/stackoverflow/63607465/Page/Parent.spec.js
<Parent/>
render
✓ renders (37ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 2.792s, estimated 3s
snapshot:
// Jest Snapshot v1,
exports[`<Parent/> render renders 1`] = `
<Parent
props={Object {}}
>
<mockConstructor>
<div>
mocked child
</div>
</mockConstructor>
</Parent>
`;
How do I create a component for Gatsby that will load on the client-side, not at build time?
I created this one and it renders with gatsby develop but not with the rendered server-side rendering
import React from 'react';
import axios from 'axios';
import adapter from 'axios-jsonp';
export default class Reputation extends React.Component<{}, { reputation?: number }> {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await axios({
url: 'https://api.stackexchange.com/2.2/users/23528?&site=stackoverflow',
adapter
});
if (response.status === 200) {
const userDetails = response.data.items[0];
const reputation = userDetails.reputation;
this.setState({
reputation
});
}
}
render() {
return <span>{ this.state.reputation?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") }</span>
}
}
If you don't want the component to be bundled in the main js file at build time, use loadable-components
Install loadable-components and use it as a wrapper for a component that wants to use a client-side only package. docs
import React, { Component } from "react";
import Loadable from "#loadable/component";
const LoadableReputation = Loadable(() =>
import("../components/Reputation")
);
const Parent = () => {
return (
<div>
<LoadableReputation />
</div>
);
};
export default Parent;
before render this component, make sure you have a window, to detect if there is a window object. I would write a hook for that:
function hasWindow() {
const [isWindow, setIsWindow] = React.useState(false);
React.useEffect(() => {
setIsWindow(true);
return ()=> setIsWindow(false);
}, []);
return isWindow;
}
In the parent component check if there is a window object:
function Parent(){
const isWindow = hasWindow();
if(isWindow){
return <Reputation />;
}
return null;
}
I want to start my React microapp with props I'm passing from Single SPA (customProps). The only way I've figured out is:
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
import App from './where/my/root/is.js';
function domElementGetter() {
return document.getElementById("mounting-node")
}
let EnhancedRootComponent = App; /* 1 */
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: EnhancedRootComponent, /* 1 */
domElementGetter,
})
export const bootstrap = [
(args) => {
/* 2 */ EnhancedRootComponent = () => <App myArgs={args.thePropsIWannaPass} />;
return Promise.resolve();
},
reactLifecycles.bootstrap,
];
export const mount = [reactLifecycles.mount];
export const unmount = [reactLifecycles.unmount];
This does work (I can see and use the passed props in my component) but I'm not completely OK with the fact that the root component changes in between calling singleSpaReact (1) and calling bootstrap(2). Would there be side effects to this that I'm not seeing now? Does anyone know a better approach for this?
You have this value inside the props variable without this reassign.
Check this out:
Root-config.js, file responsible for passing prop to microfrontend
import { registerApplication, start } from 'single-spa';
import * as isActive from './activity-functions';
registerApplication('#company/micro2', () => System.import('#company/micro2'), isActive.micro2);
registerApplication('#company/micro1', () => System.import('#company/micro1'), isActive.micro1, { "authToken": "test" });
start();
micro1 Root.tsx
import React from 'react';
export default class Root extends React.Component {
constructor(props: any){
super(props)
}
state = {
hasError: false,
};
componentDidCatch() {
this.setState({ hasError: true });
}
render() {
console.log(this.props)
return (
<div>test</div>
);
}
}
console.log output:
props:
authToken: "test" <---- props which you pass
name: "#company/micro1"
mountParcel: ƒ ()
singleSpa: {…}
__proto__: Object
for more advance usage
const lifecycles = singleSpaReact({
React,
ReactDOM,
loadRootComponent: (props) =>
new Promise((resolve, reject) => resolve(() =>
<Root {...props} test2={'test2'}/>)),
domElementGetter,
});
I’m trying to understand why my jest/enzyme tests are failing. I have a component that I use the compose function from redux in, structured as following:
class MyComponent extends Component {
//code here
}
export default compose(
connect(mapStateToProps),
)(MyComponent)
In my jest test, I do this:
Import { MyComponent } from ‘app/MyComponent’;
describe(‘<MyComponent />’, () => {
let component;
beforeEach(() => {
props = {
id: ‘23423’
}
component = shallow(<MyComponent {…props} />);
}
it(‘Loads correctly’, () => {
expect(component.state(‘isLoading’)).toEqual(true);
expect(component.find(‘InnerComponent’).length).toBe(1);
}
However, I get errors like "Cannot read property 'state' of undefined". I understand that using shallow rendering doesn't give me the exact structure that I need, but I'm not sure what else to try to get the right structure. I tried shallow-rendering the wrapped component and then finding the unwrapped component within it, like so, component = shallow(<MyComponent {...props} />).find('MyComponent').shallow();, with no luck. Any other suggestions would be appreciated.
`
Usually you want to test the component and not the integration of the component with redux:
class MyComponent extends Component {
//code here
}
export { MyComponent } // export the component
export default compose(
connect(mapStateToProps),
)(MyComponent)
Also on your test you would import the named export import { MyComponent } from '...' instead of importing the default: import MyComponent from '..'
import { MyComponent } from ‘app/MyComponent’;
describe(‘<MyComponent />’, () => {
let component;
beforeEach(() => {
props = {
id: ‘23423’
}
component = shallow(<MyComponent {…props} />);
}
it(‘Loads correctly’, () => {
expect(component.state(‘isLoading’)).toEqual(true);
expect(component.find(‘InnerComponent’).length).toBe(1);
}
}
If you want to test component interactions with your redux store you need to wrap your component with a <Provider />
import { MyComponent } from ‘app/MyComponent’;
import { Provider } from 'react-redux'
describe(‘<MyComponent />’, () => {
let component;
beforeEach(() => {
props = {
id: ‘23423’
}
component = shallow(<Provider><MyComponent {…props} /></Provider>);
}
You should definitely read the testing section of the redux documentation
Not sure why I'm getting this error in my simple Main.test file.
The constructor of Main.js
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
location: splitString(props.location.pathname, '/dashboard/')
}
if (R.isEmpty(props.view)) {
isViewServices(this.state.location)
? this.props.gotoServicesView()
: this.props.gotoUsersView()
}
}
Main.test
import React from 'react'
import * as enzyme from 'enzyme'
import toJson from 'enzyme-to-json'
import { Main } from './Main'
import Sidebar from '../../components/Common/sidebar'
const main = enzyme.shallow(<Main />);
describe('<Main /> component', () => {
it('should render', () => {
const tree = toJson(main);
expect(tree).toMatchSnapshot();
});
it('contains the Sidebar', () => {
expect(main.find(Sidebar).length).toBe(1);
});
});
Is there a way to mock up the 'pathname'?
It seems you might have a few errors one being that in your test your not passing in any props.
And another from you accessing this.props in your constructor.
See your if statement but I'll put the fix here to be explicit
if (R.isEmpty(props.view)) {
isViewServices(this.state.location)
? props.gotoServicesView()
: props.gotoUsersView()
}
In Main.test
const location = { pathname: '/dashboard/' };
const main = enzyme.shallow(<Main location={ location }/>);