React/ Enzyme - test failing in component that renders content with a map function - reactjs

I have a React project where I have a component that maps throught an array of objects and render other components, like this:
return (
historyParts.map((historyPart, historyPartIndex) =>
(<div key={`historyPart${historyPartIndex}` // eslint-disable-line react/no-array-index-key
}
>
<div>
{historyPart.link &&
<Element>
<NavLink
to={createLocationForHistoryItems(handlingLocation, historyPart.link.code)}
>
{findLinkText(historyPart[0].link, intl)}
</NavLink>
</Element>
}
<BubbleText
bodyText={findText(historyPart.summary)}
className="bubble-panel__tekst"
/>
</div>
</div>
)));
This is a test that I wrote for this component:
import React from 'react';
import { shallowWithIntl, intlMock } from 'testHelpers/intl-enzyme-test-helper';
import { expect } from 'chai';
import { HistoryDescriptionType9} from './HistorikkMalType9';
const historyPart = {
cause: null,
actionPoint: null,
summary: 'adsadsd',
link: {
code: 'UTTAK',
codeType: 'SKJERMLENKE_TYPE',
name: 'Uttak',
},
};
const historyParts = [historyPart , historyPart ];
const handlingLocation = {};
describe('HistoryDescriptionType9', () => {
it('should render HistoryDescriptionType9', () => {
const wrapper = shallowWithIntl(<HistoryDescriptionType9
historyParts ={historyParts }
handlingLocation={handlingLocation}
intl={intlMock}
/>);
const bubbleText = wrapper.find('BubbleText');
expect(bubbleText).to.have.length(historyParts.length);
});
});
So, since I am mapping an array with 2 objects, there should be 2 BubbleText components rendered. But, I get a message that the test fails:
AssertionError: expected { length: 0 } to have a length of 2 but got 0
+ expected - actual
I have also tried with importing the component and using it in find function explicitly, like this:
import BubbleText from './bubbleText';
const bubbleText = wrapper.find(BubbleText);
But, I got the same error message.
I assume that the test is failing because of the map function. How can I fix this?

idk how your BubbleText component looks like but this issue is maybe because you are passing inside tests summary: 'adsadsd' instead of "BubbleText"

Related

React createProtal called outsite a JSX component not updating the DOM

I am trying to render a dynamically generated react component in a react app using createProtal.
When I call createProtal from a class the component is not rendered.
Handler.ts the class the contains the business logic
export class Handler {
private element: HTMLElement | null;
constructor(selector: string) {
this.element = document.getElementById(selector);
}
attachedEvent() {
this.element?.addEventListener("mouseenter", () => {
let cancel = setTimeout(() => {
if (this.element != null)
this.attachUi(this.element)
}, 1000)
this.element?.addEventListener('mouseleave', () => {
clearTimeout(cancel)
})
})
}
attachUi(domNode: HTMLElement) {
createPortal(createElement(
'h1',
{className: 'greeting'},
'Hello'
), domNode);
}
}
Main.tsx the react component that uses Handler.ts
const handler = new Handler("test_comp");
export default function Main() {
useEffect(() => {
// #ts-ignore
handler.useAddEventListeners();
});
return (
<>
<div id="test_comp">
<p>Detect Mouse</p>
</div>
</>
)
}
However when I repleace attachUi function with the function below it works
attachUi(domNode: HTMLElement) {
const root = createRoot(domNode);
root.render(createElement(
'h1',
{className: 'greeting'},
'Hello'
));
}
What am I missing?
React uses something called Virtual DOM. Only components that are included in that VDOM are displayed to the screen. A component returns something that React understands and includes to the VDOM.
createPortal(...) returns exactly the same as <SomeComponent ... />
So if you just do: const something = <SomeComponent /> and you don't use that variable anywhere, you can not display it. The same is with createPortal. const something = createPortal(...). Just use that variable somewhere if you want to display it. Add it to VDOM, let some of your components return it.
Your structure is
App
-children
-grand children
-children2
And your portal is somewhere else, that is not attached to that VDOM. You have to include it there, if you want to be displayed.
In your next example using root.render you create new VDOM. It is separated from your main one. This is why it is displayed

Testing a CallBack function in a function which get triggered forButton Onclick using jest

I have a React child component which has button
export function Banner({argumentSetter}){
function handleOnClick(){
argumentSetter(argument.READ);
}
return(
<div>
<Button onClick={handleOnClick}>
<Icon name="delete" type="filled">
Discard
</Icon>
</Button>
</div>
)
}
And I have my argumentSetter in my parent component defined as following,
const [argument,setArgument] = useState<Argument>(argument.EDIT);
argumentSetter = useCallBack((val)=>{
setArgument(val);
},[argument]);
return(
<div>
<Banner argumentSetter={argumentSetter}/>
</div>
)
How to get 100% test coverage using jest.
To test the banner, your code should be like the following
import React from "react";
import { mount } from "enzyme";
import { Banner } from "./Banner.js";
import { argument } from "./arguments.js";
it("Button click leads to argument.READ", async () => {
let promiseResolve = null;
const argPromise = new Promise((resolve) => {
promiseResolve = resolve;
});
const argumentSetter = (arg) => promiseResolve(arg);
const banner = mount(<Banner argumentSetter={argumentSetter} />);
banner.find("button").simulate("click");
const newArg = await argPromise;
expect(newArg).toEqual(argument.READ);
});
Explanation:
We create an externally fulfillable promise variable, called argPromise which will resolve when promiseResolve is called, which is called when the argumentSetter is called. Hence, when the button click is simulated, it will resolve the updated argument to newArg variable (which should be argument.READ), and hence you can test if it matches your expectation.
This should hence cover all lines of your Banner component during testing.

Test for a component: How to access to a property of a tag with getByTestId

I'm doing a component test in react (My first) and I want to verify a number, when I pass it the value, it returns undefined and I remove the value to see what it returned and it was fine, find the element
import React, { useState } from 'react';
import { render, cleanup, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import { NumberUpDown } from '../../components/number-
updown/NumberUpDown';
import '#testing-library/jest-dom/extend-expect'
const UpDownNumber = () => {
const [quantity, setQuantity] = useState<number>(1);
const packageType = 'box'
return (
<NumberUpDown
value={quantity}
valueToShow={
packageType === 'box' || 'pack' || 'piece' || 'bag' || 'sbox'
? quantity : quantity * 12
}
min={1}
max={5000}
step={1}
onChange={value => setQuantity(value)}
/>
);
};
describe('Plus or minus in the product modal', () => {
afterEach(cleanup);
beforeEach(() => render(<UpDownNumber />));
it('Validate is if exists', () => {
expect(screen.getByTestId('product-minus')).toBeInTheDocument();
expect(screen.getByTestId('product-input')).toBeInTheDocument();
expect(screen.getByTestId('product-plus')).toBeInTheDocument();
});
it('Validate function onclick', () => {
const minusButton = screen.getByTestId('product-minus');
const plusButton = screen.getByTestId('product-plus');
const input = screen.getByTestId('product-input');
userEvent.click(plusButton);
userEvent.click(plusButton);
expect(getByRole('textbox', { name: /email/i })).toHaveValue('test#email.com);
expect((input as HTMLInputElement).value).toBe(3);
userEvent.click(minusButton);
expect((input as HTMLInputElement)).toBe(2);
});
});
Expected: 3
Received: <ion-input class="value-cell" data-testid="product-input" type="number"
value="3" />
expect((input as HTMLInputElement).value).toBe(3);
Expected: 3
Received: undefined
I need that when I access the tag, when it finds it, get the value...
You already use #testing-library, so I suggest taking it one step further and add https://www.npmjs.com/package/#testing-library/jest-dom as a devDependency. If using a Create React App based app, you can add an import like to your setupTests.js file e.g.
import '#testing-library/jest-dom/extend-expect';
You can then write tests to check the value of a field using something like:
expect(getByRole('textbox', { name: /email/i })).toHaveValue('test#email.com);
Using the jest-dom lets you write tests that read far nicer, but that is just my opinion.

how to use spyOn on a class less component

I am trying to apply spyOn to check whether my fucntion download is called on mouse click but I am getting the error. I am already follwoing this question but still no leads. Can anyone tell me where I went wrong. I cannot figure out any clue.
Error
Argument of type '"download"' is not assignable to parameter of type '"context"'.
mcb = jest.spyOn(fileDownlaod.instance(), "download");
my react component is:
const Filer = ({Filey} ) => {
const download = () => {
Filey()
.then((res: Response) => res.blob())
.then((data: Blob) => {
const URL = URL.createObjectURL(data);
});
};
return (
<>
<button
onMouseOver={() => download()}
onClick={() => download()}
>
</button>
</>
);
};
export default Filer;
my jest test is :
import React from 'react';
import Filer from './Filer';
import { mount, ReactWrapper } from 'enzyme';
let filer: ReactWrapper<any>;
describe('Filer', () => {
it('clicked download', () => {
filer = mount(
<Filer />
);
const _download = () => {
//some thing
}
mcb = jest.spyOn(filer.instance(), "download").mockImplementation(_download);
filer.find('button').simulate('click')
expect(mcb.mock.calls.length).toEqual(1);
});
});
If you look at the answer you are already following. In the end it has mentioned that spyOn does not work on functional components inner functions.
This is what has been said:
Keep in mind that any methods scoped within your functional component are not available for spying
So you can spy on props passed.
So the correct implementation that should work, can be:
it('clicked download', () => {
Filey = jest.fn().mockImplementation(_Filey)
filer = mount(
<Filer Filey={Filey}/>
);
expect(Filey).toHaveBeenCalled();
});

React, Enzyme and Istanbul - code coverage missing functions being executed by tests

I'm testing a React component with 3 functions, I've written tests that use all 3 of these functions and the test pass however in the code coverage report I'm only getting 33%. The component is below.
const AddWidget = ({ }: Props) => {
var id = generateGuid().slice(0, 8);
var showWidget = () => {
document.getElementById(id).style.zIndex = "10";
}
var hideWidget = () => {
document.getElementById(id).style.zIndex = "200";
}
return (
<div onMouseLeave={hideWidget} onMouseEnter={showWidget} className="addWidget" >
<div className="divide" />
<div id={id} className="cover" />
<div>
<CircularButton type={CircularButtonType.DarkAdd} small={true} />
<p>Add a widget here</p>
</div>
</div>
);
}
export default AddWidget;
And my tests...
import * as React from 'react';
import * as Enzyme from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';
import AddWidget from './AddWidget';
Enzyme.configure({ adapter: new Adapter() });
const addWidget = Enzyme.mount(<AddWidget />, { attachTo: document.body });
describe('AddWidget', () => {
test('renders without crashing', () => {
expect(addWidget.find(AddWidget).length).toBe(1);
});
test('should render parent element with class "addWidget"', () => {
expect(addWidget.find('div').at(0).hasClass('addWidget')).toBe(true);
});
test('should cover component from view when mouse is not hovering', () => {
addWidget.simulate('mouseEnter');
addWidget.simulate('mouseLeave');
var covers = document.getElementsByClassName('cover');
for (var n = 0; n < covers.length; n++) {
expect((covers[n] as HTMLElement).style.zIndex).toBe("200");
}
});
test('should show component from view onMouseEnter', () => {
addWidget.simulate('mouseEnter');
var covers = document.getElementsByClassName('cover');
for (var n = 0; n < covers.length; n++) {
expect((covers[n] as HTMLElement).style.zIndex).toBe("10");
}
});
});
The tests specify it's the showWidget and hideWidget functions that aren't being tested but the last 2 tests definitely run these functions otherwise the tests wouldn't pass.
Is this a code coverage bug? Is it that it doesn't like that I'm using pure Javascript functions or am I fundamentally misunderstanding function code coverage?
EDIT: coverage report images below
I found what the issue was, I was running the tests with the command react-scripts test --coverage --coverageDirectory=output/coverage --coverageReporters text --env=jsdom which was updating the cobertura.xml file but not any of the html. I thought the html read the coberatura file and displayed it but that's not the case. Adding the html flag to coverageReporters fixed the issue.

Resources