How to assert a React component with multiple <a> tags has an <a> tag with a given href - reactjs

I'm trying to write a mocha test for a React component. Basically the component needs to render an <a> tag with its href set to a value in a property that is passed in. The issue is that the component can render multiple <a> tags in an unpredictable order and only one of them has to have the correct href.
I'm using enzyme, chai and chai-enzyme
The following is a cut down version of my real code, but neither of the tests are passing:
const TestComponent = function TestComponent(props) {
const { myHref } = props;
return (
<div>
Link 1<br />
<a href={myHref}>Link 2</a><br />
Link 3
</div>
);
};
TestComponent.propTypes = {
myHref: React.PropTypes.string.isRequired
};
describe('<TestComonent />', () => {
it('renders link with correct href', () => {
const myHref = 'http://example.com/test.htm';
const wrapper = Enzyme.render(
<TestComponent myHref={myHref} />
);
expect(wrapper).to.have.attr('href', myHref);
});
it('renders link with correct href 2', () => {
const myHref = 'http://example.com/test.htm';
const wrapper = Enzyme.render(
<TestComponent myHref={myHref} />
);
expect(wrapper.find('a')).to.have.attr('href', myHref);
});
});

It turns out that I was approaching this wrong. Rather than try to get the assertion part of the expression to work with multiple results from the query, it is better to change the find query to be more specific. It is possible to use attribute filters in a similar way to jQuery. As such my test becomes like this:
const TestComponent = function TestComponent(props) {
const { myHref } = props;
return (
<div>
Link 1<br />
<a href={myHref}>Link 2</a><br />
Link 3
</div>
);
};
TestComponent.propTypes = {
myHref: React.PropTypes.string.isRequired
};
describe('<TestComonent />', () => {
it('renders link with correct href', () => {
const myHref = 'http://example.com/test.htm';
const wrapper = Enzyme.render(
<TestComponent myHref={myHref} />
);
expect(wrapper.find(`a[href="${myHref}"]`)).be.present();
});
});

Related

Unable to find an element with React testing library testing a ternary operator

In my React component <MyBlock /> I have the following conditional rendering:
So when variant="one" prop is passed it will return <Container /> component, when variant="two" is passed it will render <Scroller />.
{variant === 'one' ? (
<Container items={items} testId="container" />
) : (
<Scroller items={items} testId="scroller" />
)}
With React testing library I am testing this component:
it('should render the container correctly', () => {
const { getByTestId } = mount(<MyBlock data={MockData} variant="one" />);
expect(getByTestId('container')).toBeDefined();
});
This test pass and works fine.
Now I want to test the other variant:
it('should render the container correctly', () => {
const { getByTestId } = mount(<MyBlock data={MockData} variant="two" />);
expect(getByTestId('scroller')).toBeDefined();
});
But then I get:
Unable to find an element by: [data-testid="scroller"]
What's the issue here?
I figured out. I have to use queryByTestId instead of getByTestId:
it('should render the container correctly', () => {
const { queryByTestId } = mount();
expect(queryByTestId('scroller')).toBeDefined();
});
Now it's working fine for both.

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.

Testing icon based on dynamically imported svg in react-testing-library

It's my first question here and I've been coding for only a year so please be patient. I looked for similar problems on the website but couldn't find anything that worked for me.
I created an Icon component where I dynamically import the requested SVG.
I first used this solution which was working well but when I tried to test this component with react-testing library and snapshot I realised that the snapshot was always the span that is returned when nothing is imported. I first thought it was linked to the use of useRef() because I saw people saying refs didn't work with Jest so I changed my Icon component to be this:
const Icon: FC<IconProps> = ({ type, onClick, tooltip, className }: IconProps) => {
const [IconProps, setIconProps] = useState({
className: className || 'icon'
});
const tooltipDelay: [number, number] = [800, 0];
useEffect(() => {
setIconProps({ ...IconProps, className: className || 'icon' });
}, [className]);
const SVG = require(`../../svg/${type}.svg`).default;
const spanClassName = "svg-icon-wrapper";
if (typeof SVG !== 'undefined') {
if (tooltip) {
return (
(<Tooltip title={tooltip.title} delay={tooltip.delay ? tooltipDelay : 0}>
<i className={spanClassName}>
<SVG {...IconProps} onClick={onClick} data-testid="icon" />
</i>
</Tooltip>)
);
}
return (
<SVG {...IconProps} onClick={onClick} data-testid="icon" />
);
}
return <span className={spanClassName} data-testid="span" />;
};
export default Icon;
Here is my test
it('matches snapshot of each icon', async (done) => {
jest.useFakeTimers();
const type = 'check';
const Component = <Icon type={type} />;
const renderedComp = render(Component);
setTimeout(() => {
const { getByTestId } = renderedComp;
expect(getByTestId('icon')).toMatchSnapshot();
done();
}, 3000);
jest.runAllTimers();
});
I added timeout because I thought it might be link to the time it takes to import the SVG but nothing I tried worked.
So the main problem is:
how can I have the svg being imported for this component to return an icon (with data-testid='icon') and not a span (with data-testid='span')
Help would be much appreciated. My app works perfectly and I'm stuck with this testing for a while now.

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();
});

How to add test cases for Link using jest /enzyme

I am trying to write some test cases using jest and enzyme,I am unable to add test cases for Link and Please suggest if any more test cases needs to be added
these is styled component, and when should we spy /mock function
displayErr.tsx
export const DisplayErr = React.memo<Props>(({ text, SearchLink }) => (
<Section>
<h1>{text}</h1>
{!SearchLink && <Link to={getPath('SEARCH')}>some text</Link>}
</Section>
));
displayErr.test.tsx
describe('Error Msg', () => {
it('styled component', () => {
const output = mount(<DisplayErr text="hello world" SearchLink={true} />);
const link = output.find(Link).find({ to: '/Search' });
expect(output.find(Section).text()).toEqual('hello world');
//expect(link).toBe('<div class="link">Login</div>');//error
});
});
Here Link will render based on the SearchLink props. As you are passing it as true, it wont be available in the DOM. Pass SearchLink=false and execute the unit tests.
describe('Error Msg', () => {
it('styled component', () => {
const output = mount(<DisplayErr text="hello world" SearchLink=false/>)
expect(output.find(Link).props().to).toEqual('/search');
});
Let me know if you are facing the same issue.

Resources