How to test logic in ComponenWillMount using Enzyme/Jest - javascript

I am beginner in react unit testing with enzyme/jest,
I want to test my logic inside componentWillMount method.
I want to test based on my context object whether redirect happens or not based on my business logic
class ActivateSF extends Component {
constructor(props) {
super(props);
this.className = 'ActivateSF.js'
this.state = {
messages: null,
}
}
render() {
return (
<SDPActivateInterstitialUI
context={this.props.context}
messages={this.state.messages}
/>
);
}
componentWillMount() {
let context = this.props.context
if(!context.userInfo){
return this.callIdentify(context)
}
let externalLP = ExternalLandingPageUtil.getExternalLandingPageUrl(context);
if (externalLP) {
window.location.replace(`${externalLP}`);
return;
}
if (context.userInfo)
{
console.log("user identified prior to activation flow")
if (UserInfoUtil.isSubsribedUser(context))
{
window.location = '/ac'
}
else
{
this.callPaymentProcess(context)
}
}
}

You can try beforeEach to mount and in your test you call .unmount and perform your test on it.
beforeEach(() => {
const myComponent= mount(<MyComponent myprop1={...} />);
});
describe('<MyComponent/>', () => {
it('actually unmounts', () => {
...
...
myComponent.unmount();
... Do unmount tests here
});
});
Example straight from the enzyme docs: https://airbnb.io/enzyme/docs/api/ShallowWrapper/unmount.html
import PropTypes from 'prop-types';
import sinon from 'sinon';
const spy = sinon.spy();
class Foo extends React.Component {
constructor(props) {
super(props);
this.componentWillUnmount = spy;
}
render() {
const { id } = this.props;
return (
<div className={id}>
{id}
</div>
);
}
}
Foo.propTypes = {
id: PropTypes.string.isRequired,
};
const wrapper = shallow(<Foo id="foo" />);
expect(spy).to.have.property('callCount', 0);
wrapper.unmount();
expect(spy).to.have.property('callCount', 1);

Related

How to dynamically import a module in Javascript / ReactJs / React Native

I would like to dynamically import a module from a path importPath set via Props.
var importPath;
class MainComponent extends Component {
state = {}
render() {
// Set var importPath = "path_to_module here;"
// importPath = this.props.myModulePath
return (
<ComponentToImport myComponentPath="./ToastExample" />);
}
}
export default MainComponent;
Then :
class ComponentToImport extends Component {
ToastExample: (async () => {
await import (this.props.revPath)
})()
async sayHiFromJava() {
this.state.ToastExample.showJ('Awesome', ToastExample.SHORT);
}
render() {
return (<ToastExample />);
}
}
How can I go about this?
Thank you all in advance.
How do I attach ToastExample in import ToastExample from importPath; to await import("importPath"); so that I can return(<ToastExample />);
UPDATE
I have tried :
class ComponentToImport extends Component {
ToastExample: (async () => {
await import (this.props.revPath)
})()
async sayHiFromJava() {
this.state.ToastExample.showJ('Awesome', ToastExample.SHORT);
}
render() {
return (<ToastExample />);
}
}
but I get the error :
error: bundling failed: index.js: index.js:Invalid call at line 28: import(_this.props.myComponentPath)
I guess this is the way:
import("importPath").then(() => {
// your code
});
or
await import("importPath");
// your code
see more here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Is it what your are looking for?
const ToastExample = await import('importPath');
EDIT: Please read the official doc to set up your webpack or Babel (https://reactjs.org/docs/code-splitting.html)
class ComponentToImport extends Component {
constructor(props) {
super(props);
this.state = { module: null };
}
componentDidMount() {
const { path } = this.props;
import(`${path}`).then(module => this.setState({ module: module.default }));
}
render() {
const { module: Component } = this.state;
return <div>{Component && <Component />}</div>;
}
}
If you want to pass component to child component one way is to pass through child props.
import myComponentPath from "./ToastExample"
<ComponentToImport>
<myComponentPath />
<ComponentToImport/>
and then
class ComponentToImport extends Component {
render() {
return (this.props.children);
}
}
May be this helps.
Thanks

this.context is null in jest/Enzyme

I have a react class without anything to render. It has few function which uses const name = { this.context } inside a function. However, name (in below example) is always undefined. I am not sure what I am doing wrong.
I have tried setting context wrap.setContenxt({name: true}) but still when the function is called it shows this.context as {}.
person.js
export class Person extends Component {
constructor(props) {
super(props);
}
someMethod() {
const { name } = this.context;
const { record } = this.props;
if (record && name) {
this.setState({
awesome: true
});
}
}
componentDidMount() {}
render() {
return null
}
}
test.js
describe("this.context test", () => {
const props = { record: true }
const context = { name: false }
it('should test someMethod()', () => {
const wrap = shallow( <Person {...props} />, {context})
const instance = wrap.instance()
instance.someMethod();
expect(instance.state.awesome).toBe(true) // pass
})
});
I expect that it should not set state since name is false
Update:
One of thing I missed from documentation is:
The root component you are rendering must have a contextTypes static property.
Based on that if I update test.js as follow, it works:
describe("this.context test", () => {
// ***this was missing***
Person.contextTypes = {
name: PropTypes.boolean
};
const props = { record: true }
const context = { name: false }
it('should test someMethod()', () => {
const wrap = shallow( <Person {...props} />, {context})
const instance = wrap.instance()
instance.someMethod();
expect(instance.state.awesome).toBe(true) // fail
})
});

React Native async execution

I'd like to execute some async function to fetch some data from db without freezing the UI.
This is the code I wrote
export default class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value:0
};
}
componentWillMount() {
this.fetchData().then(result => { this.setState(value:result);});
}
async fetchData() {
var appState = await someMethod()
return appState;
}
someMethod() {
return new Promise(resolve => {
resolve(queryFromDB())
});
}
queryFromDB() {
// Returns a value fetched from Realm
let events = this.realm.objects("Event");
return events.length;
}
render() {
return (
<Text> {this.state.value} </Text>
);
}
}
The problem is that it does execute on the main thread, freezing the app.
What's the error?
Seems like your code has syntax errors. You have written all your code inside the constructor. Try this.
export default class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
value:0
};
}
componentWillMount() {
this.fetchData().then(result => { this.setState(value:result);});
}
async fetchData() {
var appState = await someMethod()
return appState;
}
someMethod() {
return new Promise(resolve => {
resolve(queryFromDB())
});
}
queryFromDB() {
// Returns a value fetched from Realm
let events = this.realm.objects("Event");
return events.length;
}
render() {
return (
<Text> {this.state.value} </Text>
);
}
}

Getting the ref from a dynamic component when using Redux, React and react-router-dom 4.x

I have the following class
class MatchBox extends React.Component {
constructor(props) {
super(props);
this.countdownHandler = null;
this.showBlocker = true;
this.start = this.start.bind(this);
}
start() {
...
}
render() {
...
return (
<div style={ styles.mainContainer } className="fluid-container">
...
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));
which is used in this class
class GameBox extends React.Component {
constructor(props) {
super(props);
...
}
render() {
var mainElement = null;
switch(this.props.mainElement.element) {
case 'SEARCHING': mainElement = <SearchingBox gameType={ this.props.gameType }/>; break;
case 'MATCH': mainElement = <MatchBox ref='matchBox'/>; break;
default: mainElement = <SearchingBox/>;
}
return (
<div style={ styles.mainContainer } className="fluid-container">
{ mainElement }
</div>
);
}
};
function mapStateToProps(state) {
...
}
function matchDispatchToProps(dispatch) {
...
}
export default withRouter(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(GameBox));
And I can't get the ref of the object MatchBox. I tried with this.refs.matchBox and is null, also tried getting directly from ref(ref={(r) => { // r is null } }) and I don't know what to try anymore.
I'm using react-router-dom 4 and I don't know if function withRouter affect the outcome component.
It's not pretty, but I think this is the solution. withRouter exposes the child ref via a wrappedComponentRef callback, which gets us to the connect hoc. That exposes its child ref via getWrappedInstance if you pass the withRef attribute as you did. So you just have to combine both of those.
class GameBox extends React.Component {
matchboxRefCallback = (connectHOC) => {
this.matchboxRef = connectHOC ? connectHOC.getWrappedInstance() : null;
}
render() {
return <MatchBox wrappedComponentRef={this.matchboxRefCallback}/>;
}
}
Much more cleaner solution would be to create a HOC. which will forward the ref to actual component
const matchBoxHOC = (WrappedComponent) => {
class MatchBoxHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent {...rest} ref={forwardRef} />;
}
}
const WithRouterMatchBoxHOC = withRouter(MatchBoxHOC, { withRef: true });
return React.forwardRef((props, ref) => {
return <WithRouterMatchBoxHOC {...props} forwardRef={ref} />;
});
}
Call is like
export default matchBoxHOC(connect(mapStateToProps, matchDispatchToProps, null, { withRef: true })(MatchBox));

How to force rendering if the global variable value changes?

#File 1:
let ticketEnable = false;
export default class SupportTicketMain extends Component {
constructor () {
super();
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Section className="ticketMain" primary={true}>
<TicketHeader expanded={ticketEnable}/>
</Section>
);
}
};
export function setTicketEnablement (value) {
ticketEnable = value;
}
#file 2:
import { setTicketEnablement } from file1;
export default class SupportTicketTabs extends Component {
constructor () {
super();
this.state = {
ticketDetailExpanded: false
};
this._expandClick = this._expandClick.bind(this);
}
_expandClick() {
this.setState({ticketDetailExpanded: !this.state.ticketDetailExpanded});
setTicketEnablement(this.state.ticketDetailExpanded);
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Button className="expander" type="icon" onClick={this._expandClick}>
{expandIcon}
</Button>
);
}
};
Here a button click in supportTicketTabs class of #file2 will update global variable in #File1 , but SupportTicketMain render doesn't update if the global variable value changes! please guide me on this.
ticketEnable should be a prop passed into SupportTicketMain. The component that wraps both SupportTicketTabs and SupportTicketMain should be handing down a callback as a prop that modifies the value of ticketEnable (toggleTicketEnable) and the value of ticketEnable
class Main extends Component {
constructor (props) {
super(props);
this.onToggleTicketEnable = this.onToggleTicketEnable.bind(this);
this.state = {
ticketEnabled: false;
};
}
onToggleTicketEnable() {
this.setState({ ticketEnabled: !this.state.ticketEnabled });
}
render () {
return (
<App centered={false}>
<SupportTicketMain ticketEnable={this.ticketEnabled} />
<SupportTicketTabs onToggleTicketEnable={this.onToggleTicketEnable}/>
</App>
);
}
}

Categories