React unit test to find the element in the component - reactjs

any help is appreciated. I was bit stuck with writing unit test in react. I need to check if action button and link button exists in the component.
here is my code for component. It is rendering child component and passing button as render props
export interface CloseableNotificationBannerProps {
title?: string;
message: string;
variant: "default" ;
icon: "info";
actionButton?: React.ReactNode;
showLinkButton: boolean;
}
export const CloseableNotificationBanner: React.FC<CloseableNotificationBannerProps> =
({
title,
message,
variant,
icon,
actionButton,
showLinkButton
}) => {
const [show, setShow] = useState(false); // extract into props to deafult it
const handleClick = () => setShow(prevState => !prevState);
return (
show ?
<BasicNotificationBanner
title={title}
message={message}
variant={variant}
icon={icon}
actionButton={actionButton}
closeButton={showLinkButton && <LinkButton
variant="transparent"
color="neutrals.dn40"
onClick={handleClick}>×</LinkButton>}
/> : null
);
};
For now It is able to mount the component successfully but unable to find action button and link button. Below is my unit test:
// Given
const content = {
message: chance.string(),
icon: chance.pickone(["info", "check_circle", "warning"]),
variant: chance.pickone(["default", "information", "success", "error", "warning"]),
actionButton: <Button>button</Button>,
showLinkButton: true
};
// When
const actual = mount(<CloseableNotificationBanner content={content}/>);
const button = actual.find(Button);
// Then
expect(actual.exists()).toBeTruthy();
expect(button.exists()).toBeTruthy();
output shows that
Error: expect(received).toBeTruthy()
Received: false
for button. Please help. I have tried wit both shallow and render

The right way to do this is to use the
toBeInTheDocument() function.
Like this:
expect(actual).toBeInTheDocument();
expect(button).toBeInTheDocument();

So, you need find particular button by its id or by its class name like
expect(actual.find('#actionButton').length).toBe(1); // if it has actionButton id
expect(actual.find('.button-class').at(0).length).toBe(1); // if its has many buttons and all have button-class and its on first position
expect(actual.find('button').length).toBe(2); // 2, if you have only two buttons

solved it using expect(actual.children().props().actionButton).toEqual(content.actionButton);

Related

How can I fix this Unit Test?

I'm fairly new to unit testing my .tsx files and I am currently having trouble testing this (sorry if the format is off)
//this is Banner.tsx
import React, {useCallback} from "react";
type Properties = {
close: () => void;
text: string;
const Banner: React.FC<Properties> = ({close, text}) => {
const onClick = useCallback(() => {
close();},
[close, text]);
return (
<div className = "BannerBox">
<div className = "banner">
<span className = "popup"> onClick={onClick}[x]
</span>
{text}
</div>
</div>
);
};
export default Banner;
//this is App.tsx
import Banner from "./Components/Banner";
function App(): JSX.Element {
const [isOpen, setIsOpen]=useState(false);
const toggleBanner = () => {
SetIsOpen(!isOpen);
};
return (
<div>
<input type = "button"
value = "popup"
onClick={toggleBanner}/>
<p>hi</p>
{isOpen && <Banner text = {"hello"} close={() => isOpen(false)}/>}
</div>
export default App;
this is what i have so far
//Banner.test.tsx
test("Check that all type Properties are being used", () => {
render(<Banner />);
})
it gives this error -> "type {} is missing the following properties from type Banner: close and text"
"type {} is missing the following properties from type Banner: close and text"
Read this error message carefully.
Banner is a functional component. That means it's a function that that takes it's props as an object. And it's typed to receive two props, close and text. These props are required.
But you are providing no props in your test. Since the props argument is always an object, and you have no props, then the props argument is an empty object.
So now that error tells you that your function expects an object, but the one you provided is missing the close and text props.
You need to satisfy the required props of your component. Whether you are in a test or not, the contract of those types must must be fulfilled.
That means you want something like this:
//Banner.test.tsx
test("Check that all type Properties are being used", () => {
render(<Banner text="Hello, World!" close={() => null} />);
})
In additional there several syntax errors in your components. And your code will be much easier to understand if you use proper indenting to inform you of the structure of your code.

I have a Modal in React that is showing when first rendering the App

I have a Modal with a Form inside, which i want closed when i first render the app.
This Modal should only open when i click a "Add more" button.
Problem is it's happening the opposite, the Modal is already open when i render the app.
I've already worked with Modals and i know i can use useState() to toggle if it's open or not.
This is the all the code regarding the Modal.
I'm using React Bootstrap.
// App.tsx
const [showFormModal, setShowFormModal] = useState(false);
const closeForm = () => {
setNewTable({});
setShowFormModal(false);}
<TableForm
show={showFormModal}
handleClose={closeForm}
onInputChange={onInputChange}
addPost={addNewTable}
/>
this is the code regarding the Modal inside the component:
//TableForm
const TableForm = (
show: any,
handleClose: any,
onInputChange: any,
addPost: any
) => {
const [validate, setValidate] = useState(false);
const checkAndAddStarship = async (e: any) => {
setValidate(true);
try {
await addPost(e);
setValidate(false);
handleClose();
} catch (e) {
window.alert(e);
}
};
console.log(show);
return (
<Modal size="xl" centered show={show} onHide={handleClose}>
<Modal.Header closeButton onClick={handleClose}>
<Modal.Title>Add New Table</Modal.Title>
Check the type you are receiving in your component. maybe do show : boolean instead of show : any
Thank you Himura,
I solved the problem:
I passed the props to TableForm in the wrong way: basically i told my App that the 'show' variable was an entire object with the other 3 props.
This was my error:
const TableForm = (
show: any,
handleClose: any,
onInputChange: any,
addPost: any
)
I was able to make it work simply changing it to this:
const TableForm = ({show, handleClose, onInputChange, addPost})
i was forgetting {}

How to show JSDoc based prop tooltips in VSCode for React functional component prop?

I have a React functional component:
interface ISpecialButton {
text: string;
}
const SpecialButton: FC<ISpecialButton > = ({
/* Prop description */
text
} = {
//code
});
And would like a developer who uses it in a JSX template to see "Prop description" when he hovers over text prop when adding the SpecialButton to a template. I have tried various ways to do it with JSDoc, but none of these work. What is the correct way to use JSDoc for a React functional component so that VSCode shows tooltips?
The interface is the one that should be documented so every other place it's going to show the docs when hovering over it. Something like this:
interface ISpecialButton {
/**
* Prop description
*/
text: string;
}
const SpecialButton: React.FC<ISpecialButton> = ({
text
}) => {
return <>{ text }</>;
};
const Template: React.FC = () => {
return <>
<SpecialButton text='' />
</>
}

How to test if component hast only the allowed props and uses only the allowed classes with Jest?

I have a Button component that looks like this:
const Button = ({
children,
type,
btnStyles,
onClick,
}: any) => {
//...
}
export default Button
I wan't to make sure that the button is only used with the allowed props defined above. My test currently looks like this:
it('should only have the allowed props', () => {
const tree = shallow(
<Button
type='button'
buttonStyles='a-custom-button'
onClick={() => {
console.log('Custom Button Clicked')
}}
>
<i></i>
Button
</Button>
)
expect(tree.prop('type')).toBeDefined()
expect(tree.prop('buttonStyles')).toBeDefined()
})
I also want to make sure that only allowed class names can be used for buttonStyles. For example the button can only contain the classes .a-custom-button and .a-custom-button-styles.
How can I do this with Jest?
#Clarity is correct. You're using TypeScript so you could use a union of valid class names:
type ButtonProps = {
btnStyles: 'a-custom-button' | 'a-custom-button-styles';
}
const Button = ({
btnStyles,
}: ButtonProps) => {
console.log(btnStyles);
return null;
}
Button({ btnStyles: 'a-custom-button' }) // works
Button({ btnStyles: 'a-custom-button-styles' }) // also works
Button({ btnStyles: 'invalid-class-name' }) // doesn't work - invalid prop value in TypeScript
Playground:
https://www.typescriptlang.org/play?#code/C4TwDgpgBAQgrsYB7AdgBQE5LAZygXigG8AoKKAI2BQGVQAbCHALigHIBDAWgGM4dkAWy4UEyFGygAfdtz4Ckw0YlRcBIRjjYBuEgF8SJHqgGwxqAlAAUpcuSq0GTADT7W8Feiy4AlAQB8xGTkxig4SIwAdPRIAOZWDnQaTD66dhgQwHAYKFAocPT0ugYkHuI2lNRJmqycvPxCIuYSUHp+dh0A9J1QAO5IGADWOKXNFYlOLLL1CkrNapOSbVDdUBz04X0Dw6Oe41WTtQCWKABu60cAJrz0HDg4XCgcghBL7XarJ+f0V1Bg3lBvnBoAAVcAQGg8DBHMDAKAQDBYDAkIA

How can I display a Persona component in a CommandBar in React Fabric-UI?

I am trying to display a Persona component on the far right of my CommandBar component, which I use as a header for my application.
Here's a code snippet
const getFarItems = () => {
return [
{
key: 'profile',
text: <Persona text="Kat Larrson" />,
onClick: () => console.log('Sort')
}
]
}
const FabricHeader: React.SFC<props> = () => {
return (
<div>
<CommandBar
items={getItems()}
farItems={getFarItems()}
ariaLabel={'Use left and right arrow keys to navigate between commands'}
/>
</div>
);
}
This throws a type error because the text prop expects a string and not a component. Any help would be appreciated!
Under the ICommandBarItemProps there is a property called commandBarButtonAs that the docs state:
Method to override the render of the individual command bar button.
Note, is not used when rendered in overflow
And its default component is CommandBarButton which is basically a Button
Basically there are two ways to do this.
Keep using Button, and apply your own renderer. Basically the IButtonProps you can add onRenderChildren which would allow you to add any Component such as Persona to render. This example would show you how it is done https://codepen.io/micahgodbolt/pen/qMoYQo
const farItems = [{
// Set to null if you have submenus that want to hide the down arrow.
onRenderMenuIcon: () => null,
// Any renderer
onRenderChildren: () => ,
key: 'persona',
name: 'Persona',
iconOnly: true,
}]
Or add your own crazy component not dependent on CommandBarButton but that means you need to handle everything like focus, accessibility yourself. This example would show you how it is done https://codepen.io/mohamedhmansour/pen/GPNKwM
const farItems = [
{
key: 'persona',
name: 'Persona',
commandBarButtonAs: PersonaCommandBarComponent
}
];
function PersonaCommandBarComponent() {
return (
);
}

Resources