Hello I'm trying to test a function from a function but tells me this error.
TypeError: Cannot read property 'getNextServiceIconStyle' of null
Code
function IssueNextServiceIcon ({ nextService, intl }) {
return (
<div styles[getNextServiceIconStyle(nextService.approaching, nextService.overDue)])}>
<NextServiceIcon className={styles['icon']} />
</div>
)
function getNextServiceIconStyle (approaching, overDue) {
if (overDue) {
return 'next-service-overdue'
}
else if (approaching) {
return 'next-service-approaching'
}
return ''
}
}
Test
test('should', () => {
const wrapper = shallow(<IssueNextServiceIcon {...mockPropsForComponent} />)
const instance = wrapper.instance()
const expectedResult = 'next-service-overdue'
expect(instance.getNextServiceIconStyle(true, false)).toEqual(expectedResult)
})
Any suggestion for the test?
There a few syntax errors and unclosed braces, but if I understood your intent correctly, you'd do smth like this:
function IssueNextServiceIcon({ nextService, intl }) {
function getNextServiceIconStyle(approaching, overDue) {
if (overDue) {
return "next-service-overdue";
} else if (approaching) {
return "next-service-approaching";
}
return "";
}
const styleKey = getNextServiceIconStyle(
nextService.approaching,
nextService.overDue
);
return (
// Or if you need to pass className: className={styles[styleKey]}
<div styles={styles[styleKey]}>
<NextServiceIcon className={styles["icon"]} />
</div>
);
}
Regarding the test, you cannot use wrapper.instance() because this is not a class component. What you could do is to render your component and check that it has proper styles applied:
test('it should have correct styling', () => {
const wrapper = shallow(<IssueNextServiceIcon {...mockPropsForComponent} />)
expect(component.find('NextServiceIcon').prop('style')).toHaveProperty('color', 'red') // test for the actual css you have
})
Related
Consider I have a function that is called from inside the ComponentDidUpdate. I need to unit test the canCheck function.
ComponentDidUpdate(prevProps,prevState){
if(prevState.isFull){
callA();
}
if(canCheck(prevProps)){
callB()
}
}
The canCheck function is as follows
canCheck(prevProps){
const {
isFocused,
getTheStartState,
index,
loading
} = this.props;
if (!isFocused) {
return false;
}
return (
index === 0 &&
prevProps.getTheStartState &&
!getTheStartState &&
!loading
);
}
Things I have tried include
const ShallowRenderer = require('react-test-renderer/shallow');
describe('canCheck', () => {
test('should return correctly value', () => {
const renderer = new ShallowRenderer();
renderer.render(
<TestContainer
isFocused
getTheStart
{...dummy}
/>
);
const instance = renderer.getMountedInstance();
instance.componentDidUpdate(
{
getTheStartState: false
loading: true
},
{ someState: true }
);
expect(renderer.getMountedInstance().canCheck()).toBeFalsy();
});
});
Though I have mentioned getTheStartState in the prevProps of componentDidUpdate I always get the error as cannot read getTheStartState of undefined. Are there any suggestions of how we can unit test such function, I could not find much resource for this online!
Hi I want to render a string with looping logic behind it so I decided to put a function that will return the string
function Leasing(){
let {idLeasingExact} = useParams()
const checkParam = () =>{
//return(idLeasingExact)
dropdownItems.map((item,index) => {
if(idLeasingExact == item.path){
console.log(idLeasingExact)
console.log(item.path)
console.log(item.title)
return(
item.title
)
}
})
}
return(
<div>
<h1>
{idLeasingExact ? checkParam() : "Leasing"
}
</h1>
</div>
)
}
export default Leasing;
here is the dropdown item
export const dropdownItems = [
{
title:'SMF',
path:'1',
cName:'dropdown-link'
},
{
title:'BFI',
path:'2',
cName:'dropdown-link'
},
{
title:'ADIRA',
path:'3',
cName:'dropdown-link'
}
]
I use param and that param will be used in function checkParam to return the result
the checkParam() should return the title(SMF BFI ADIRA) as the result
for example, if it's leasing/1
it should've return the title of SMF
or if it's leasing/2
it should've returned the title of BFI
but it returns null,
although the console log on the browser shows the right item.title just like the example
help appreciated I'm stuck here thanks
So you're not wanting to actually do a map. You gotta find the item on dropdownItems with path equals to idLeasingExact. Try changing your checkParam() function to something like this:
const checkParam = () => {
const item = dropdownItems.find(x => x.path == idLeasingExact);
if (item) return item.title;
else {
// do whatever you want if path is not found, for example:
return 'No title found.'
}
}
What your code is doing, is some sort of a conditional mapping and the function checkParam() is not actually returning something if you take a close look (the function inside the map does return the .title, not the checkParam()!)
map() returns a list. checkParam() is not returning that list. Just add return to the function-
const checkParam = () =>{
return dropdownItems.map((item,index) => {
if(idLeasingExact == item.path){
return(
item.title
)
}
})
}
Also, you can add your JSX logic in checkParam itself like this-
const checkParam = () => {
return dropdownItems.map((item, index) => {
if (idLeasingExact == item.path) {
return <h1>{item.title}</h1>
} else {
return <h2>{"Leasing"}</h2>
}
})
}
return <div>{checkParam()}</div>
This will give you more control based on idLeasingExact value (e.g. conditional styling)
I have a component which mainly returns html. All codes are simplified to address the issue.
//MainRender.js
const clientId = 8;
const clientName = "Foo";
export default function MainRender(props){
const theValue = props.value;
function changeOnClick(elem, id, name){
return (
// elem: HTMLImgElement which triggers other event
// id: number
// name: string
console.log(elem, id, name);
)
};
function helper1(arg) {
return (
<div className="helper1" onClick={(e) => changeOnClick(e.target.value, clientId, clientName)}>
<span> Helper1: {arg.isHelper1} </span>
</div>
);
}
function helper2(arg) {
return (
<div className="helper2" >
<span> Helper2: {arg.isHelper2} </span>
</div>
)};
if(theValue.isHelper1){
return helper1(theValue)
} else {
return helper2(theValue)
}
};
and my test is,
import { mount } from 'enzyme';
import React from 'react';
import MainRender from './MainRender';
describe("Testing MainRender", () => {
it("it should return true when click is fired", ()=> {
const tempValue = {"value": {"isHelper1":true, "isHelper2": false}};
const wrapper = mount(<MainRender {...tempValue} />);
const helper1 = wrapper.find('.helper1');
const spyElem = jest.spyOn(document, 'getElementById');
const mockElem = document.createElement('img');
spyElem.mockReturnValue(mockElem);
const mockHelper1 = jest.spyOn(MainRender, 'changeOnClick');
helper1.simulate('click', {target: {elem:spyElem, id:7, name:'foo'});
expect(mockHelper1.toHaveBeenCalled()).toBe(true);
}
});
I just want to test the onClick function is actually called. I have been reading and researching the documents and examples about jest and enzyme but I am not sure I create correct mock HTML element that I keep getting error.
TypeError: Cannot read property '_isMockFunction' of undefined
How can I create mock Element and check the click event?
Thanks for any advice!
I have a code similar to this one:
function Component1(...) {
...
function checkIfCtrlKey(event) {
return event.ctrlKey;
}
return (
{ checkIfCtrlKey() && (<Component2 .../>) }
);
}
The sense behind this is that the Component2 is just rendered if the Ctrl-key is being pressed. When running this code, I get following error message: TypeError: Cannot read property 'ctrlKey' of undefined
What is my mistake? Is there a solution or other possibility to implement my need?
You need to put an event listener on the window object and within that hander you can set some state to switch between a true and false
Something like this.
function Component1() {
const [ctrlActive, setCtrlActive] = useState(false)
useEffect(() => {
window.addEventListener('keydown', (e => {
if("Do a check here to see if it's CTRL key") {
setCtrlActive(!ctrlActive)
}
}), [])
})
return ctrlActive ? <Component2 /> : null
}
You can use Vanilla JS for that like this:
import React, { useEffect, useState } from "react";
export default function App() {
const [ctrlPressed, setCtrlPressed] = useState(false);
const handleKey = (e) => setCtrlPressed(e.ctrlKey);
useEffect(() => {
window.addEventListener("keydown", handleKey);
window.addEventListener("keyup", handleKey);
return function cleanup() {
window.removeEventListener("keydown", handleKey);
window.removeEventListener("keyup", handleKey);
};
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{ctrlPressed ? <h2>You're pressing CTRL Key</h2> : null}
</div>
);
}
Working example over here
Your making mistake here,
{ checkIfCtrlKey() && (<Component2 .../>) }
refer
function checkIfCtrlKey(event) {
return event.ctrlKey;
}
How u suppose that checkIfCtrlKey will be passed with event arg when your calling like this checkIfCtrlKey() ??
You might wanted to attach it to window,
function Component1() {
const [ctrlKeyPressed, setCKP] = useState(false)
const handleKey = ev => {
setCKP(ev.ctrlKey)
}
useEffect(() => {
window.addEventListener('keydown', handleKey);
window.addEventListener('keyup', handleKey);
return () => {
window.removeEventListener('keydown', handleKey);
window.removeEventListener('keyup', handleKey);
}
}, [])
return (
<>
...
{ctrlKeyPressed && <Component2 />}
...
</>
)
}
Shows Component2 as long as ctrlKey is pressed
I've started using Flow type on top of a project created with create-react-app tool. I struggle to make a simple scenario work where a class property is filled with element reference in render method but throws 2 errors. What am I doing wrong? All the checks should prevent those warnings.
class MyComponent extends React.Component<*> {
input: ?HTMLInputElement;
componentDidUpdate = () => {
if (this.input) {
this.input.focus();
if (this.input.value) {
const valueLength = this.input.value.length;
this.input.setSelectionRange(valueLength, valueLength);
}
}
};
render() {
return <input ref={ref => (this.input = ref)} />;
}
}
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/todo/index.js:38:28
Property value is missing in null or undefined [1].
[1] 33│ input: ?HTMLInputElement;
34│
35│ componentDidUpdate = () => {
36│ if (this.input) {
37│ this.input.focus();
38│ if (this.input.value) {
39│ const valueLength = this.input.value.length;
40│ this.input.setSelectionRange(valueLength, valueLength);
41│ }
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/todo/index.js:40:28
Cannot call this.input.setSelectionRange because property setSelectionRange is missing in null or undefined [1].
[1] 33│ input: ?HTMLInputElement;
34│
35│ componentDidUpdate = () => {
36│ if (this.input) {
37│ this.input.focus();
38│ if (this.input.value) {
39│ const valueLength = this.input.value.length;
40│ this.input.setSelectionRange(valueLength, valueLength);
41│ }
42│ }
43│ };
Since you're calling methods, flow assumes that things could change at anytime. You need to keep a reference to the input and then you're all good. Something like below:
class MyComponent extends React.Component<*> {
input: ?HTMLInputElement;
componentDidUpdate = () => {
const { input } = this
if (input) {
input.focus();
if (input.value) {
const valueLength = input.value.length;
input.setSelectionRange(valueLength, valueLength);
}
}
};
render() {
return <input ref={ref => (this.input = ref)} />;
}
}