how to test radio button in react testing library(User can select other radio button) - reactjs

I am new to react and writing test case in react. I have two radio buttons (IPV4 and IPV6). I want to test that a user can select another radio button or not. I don't know which event listener to apply on radio button, is it change event or click event?
I broke down my problem into two parts: 1) I am checking whether I can select or deselect one radio button or not 2) I can select another radio button or not. I failed in my 1 part only so please help me. Here is my code and test case.
import React from 'react'
import constants from 'LookingGlass/constants'
import { Label, RadioButton } from 'LookingGlass/common/components'
export const componentTestId = 'SelectorIPVersion'
export default function SelectorIPVersion(props = {}) {
const { checkedVersion, onChange, disabled } = props
const { choices, groupName, label } = constants.SelectorIPVersion
const _onChange = (e) => {
onChange && onChange(e.target.value)
}
let selectorChoices = choices.map((c) => {
return (
<RadioButton
key={c.value}
id={c.value}
name={groupName}
value={c.value}
label={c.label}
checked={c.value === checkedVersion}
onChange={_onChange}
disabled={disabled}
/>
)
})
return (
<div data-testid={componentTestId}>
<Label text={label} />
{selectorChoices}
</div>
)
}
Here is my test case:
Here i am trying select and deselect the radio button but this is giving me error so how can I check that user can select another option or not?
const renderComponent = (props) => render(<SelectorIPVersion {...props} />)
test('Verify that user can select another version', () => {
const { getByRole, debug } = renderComponent({ checkedVersion: 'ipv4' })
const radio = getByRole('radio', { name: 'IPv4' })
expect(radio).toBeChecked()
debug(radio)
fireEvent.click(radio)
expect(radio).not.toBeChecked()
})
Output of Error:
Received element is checked:
<input class="hidden" id="ipv4" name="ipVersion" readonly="" tabindex="0" type="radio" value="ipv4" checked=""/>
When I debug the IPV4 radio button this is the output:
● Console
console.log node_modules/#testing-library/react/dist/pure.js:94
<input
checked=""
class="hidden"
id="ipv4"
name="ipVersion"
readonly=""
tabindex="0"
type="radio"
value="ipv4"
/>
And when I don't pass checkedVersion: 'ipv4' as props radio button is not checked.
Where am I wrong. Is it right or wrong?

Radio buttons are meant to be used in groups, so that when clicking on one of them deselects the currently selected one. To test that a radio button is deselected simply select the other one.
test('Verify that user can select another version', () => {
const { getByRole } = renderComponent({ checkedVersion: 'ipv4' })
const ipv4Radio = getByRole('radio', { name: 'IPv4' })
const ipv6Radio = getByRole('radio', { name: 'IPv6' })
expect(ipv4Radio).toBeChecked()
fireEvent.click(ipv6Radio)
expect(ipv4Radio).not.toBeChecked()
expect(ipv6Radio).toBeChecked()
})
However, as a test this doesn't bring much value since you're just testing that radio buttons work as expected (we already know they do). You should focus on testing your own logic around these buttons instead.

Related

how to test change event radio button using react testing library

I tried using fireEvent click, but i get an error like this.
enter image description here
my code
<div className="flex flex-col">
<DropdownSingle
label={"Value"}
dataTestId="select-value"
value={getValuez}
onOk={(value: string) =>
handleChange(value, "valuez")
}
withSearchFilter={false}
withFooter={false}
loading={false}
size={"middle"}
className={"dropdown-style"}
defaultValue={valuezOptions[0].id}
options={valuezOptions}
optionValueKey={'id'}
optionLabelKey={'name'}
/>
</div>
it('changes style of div as checkbox is checked/unchecked', () => {
const { getByTestId } = render(<MyCheckbox />)
const checkbox = getByTestId(CHECKBOX_ID)
const div = getByTestId(DIV_ID)
expect(checkbox.checked).toEqual(false)
expect(div.style['display']).toEqual('none')
fireEvent.click(checkbox)
expect(checkbox.checked).toEqual(true)
expect(div.style['display']).toEqual('block')
fireEvent.click(checkbox)
expect(checkbox.checked).toEqual(false)
expect(div.style['display']).toEqual('none')
})
What I am doing here is. let suppose I have a checkbox that is always is visible, checking and unchecking it, causes the dropdown next to it to be shown or hidden. when I click the checkbox it fires an action that changes the parameter to be either false or true. Mainly I'm trying to test that when I click the checkbox the dropdown is either displayed or not

Selecting created option on menu close/select blur using creatable component

Is there some way to instruct react-select to select an option on menu close or select blur, but only if it is the one created (not from default list)?
Context:
I have a list of e-mail addresses and want to allow user to select from the list or type new e-mail address and then hit Submit button. I do the select part with react-select's Creatable component and it works.
import CreatableSelect from 'react-select/creatable';
<CreatableSelect
options={options}
isMulti={true}
isSearchable={true}
name={'emailAddresses'}
hideSelectedOptions={true}
isValidNewOption={(inputValue) => validateEmail(inputValue)}
/>
But what happens to my users is that they type new e-mail address, do not understand they need to click the newly created option in dropdown menu and directly hit the Submit button of the form. Thus the menu closes because select's focus is stolen and form is submitted with no e-mail address selected.
I look for a way how can I select the created option before the menu is closed and the typed option disappears.
You can keep track of the inputValue and add the inputValue as a new option when the onMenuClose and onBlur callbacks are triggered.
Keep in mind that both onBlur and onMenuClose will fire if you click anywhere outside of the select area. onMenuClose can also fire alone without onBlur if you press Esc key so you will need to write additional logic to handle that extra edge case.
function MySelect() {
const [value, setValue] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const isInputPreviouslyBlurred = React.useRef(false);
const createOptionFromInputValue = () => {
if (!inputValue) return;
setValue((v) => {
return [...(v ? v : []), { label: inputValue, value: inputValue }];
});
};
const onInputBlur = () => {
isInputPreviouslyBlurred.current = true;
createOptionFromInputValue();
};
const onMenuClose = () => {
if (!isInputPreviouslyBlurred.current) {
createOptionFromInputValue();
}
else {} // option's already been created from the input blur event. Skip.
isInputPreviouslyBlurred.current = false;
};
return (
<CreatableSelect
isMulti
value={value}
onChange={setValue}
inputValue={inputValue}
onInputChange={setInputValue}
options={options}
onMenuClose={onMenuClose}
onBlur={onInputBlur}
/>
);
}
Live Demo

How do I trigger the change event on a react-select component with react-testing-library?

Given that I can't test internals directly with react-testing-library, how would I go about testing a component that uses react-select? For instance, if I have a conditional render based on the value of the react-select, which doesn't render a traditional <select/>, can I still trigger the change?
import React, { useState } from "react";
import Select from "react-select";
const options = [
{ value: "First", label: "First" },
{ value: "Second", label: "Second" },
{ value: "Third", label: "Third" },
];
function TestApp() {
const [option, setOption] = useState(null);
return (
<div>
<label htmlFor="option-select">Select Option</label>
<Select
value={option}
options={options}
onChange={option => setOption(option)}
/>
{option && <div>{option.label}</div>}
</div>
);
}
export default TestApp;
I'm not even sure what I should query for. Is it the hidden input?
My team has a test utility in our project that lets us select an item easily after spending too much time trying to figure out how to do this properly. Sharing it here to hopefully help others.
This doesn't rely on any React Select internals or mocking but does require you to have set up a <label> which has a for linking to the React Select input. It uses the label to select a given choice value just like a user would on the real page.
const KEY_DOWN = 40
// Select an item from a React Select dropdown given a label and
// choice label you wish to pick.
export async function selectItem(
container: HTMLElement,
label: string,
choice: string
): Promise<void> {
// Focus and enable the dropdown of options.
fireEvent.focus(getByLabelText(container, label))
fireEvent.keyDown(getByLabelText(container, label), {
keyCode: KEY_DOWN,
})
// Wait for the dropdown of options to be drawn.
await findByText(container, choice)
// Select the item we care about.
fireEvent.click(getByText(container, choice))
// Wait for your choice to be set as the input value.
await findByDisplayValue(container, choice)
}
It can be used like this:
it('selects an item', async () => {
const { container } = render(<MyComponent/>)
await selectItem(container, 'My label', 'value')
})
You can try the following to get it working:
Fire focus event on the ReactSelect component .react-select input element.
Fire a mouseDown event on the .react-select__control element
Fire a click on the option element that you want to select
You can add a className and classNamePrefix props with the value of "react-select" in order to specifically select the component you are trying to test.
PS: In case you are still stuck I'd encourage you to take a look at this conversation from where the above answer is borrowed - https://spectrum.chat/react-testing-library/general/testing-react-select~5857bb70-b3b9-41a7-9991-83f782377581

How to set ErrorMessage on TextField dynamically on Button onClick method

I need to bind ErrorMessage to textfield only when user press button. In this there are nice examples how to use errormessage but the problem is that I don't know how to make append errorMeesage after user click
<TextField id='titleField' value={titleValue} required={true} label={escape(this.props.description)} onGetErrorMessage={this._getErrorMessage} validateOnLoad={false} />
And this is a call of a button:
private _buttonOnClickHandler() {
let textvalue = document.getElementById('titleField')["value"];
if(textvalue === "")
{
//call onGetErrorMessage or something that will set ErrorMeesage and input will be red
}
return false;
}
Thank you
The easiest way I can think of to accomplish this is by predicating the onGetErrorMessage on a state check, which tracks whether the button has been clicked.
<TextField
id='titleField'
value={titleValue}
required={true}
label={escape(this.props.description)}
// Only allow showing error message, after determining that user has clicked the button
onGetErrorMessage={this.state.buttonHasBeenClicked ? this._getErrorMessage : undefined}
validateOnLoad={false}
/>
Then in your button click handler, simply set that state value:
private _buttonOnClickHandler() {
this.setState({ buttonHasBeenClicked: true });
return false;
}
As long as you instantiate buttonHasBeenClicked to be false, then this method will meet the requirement that (a) before the user clicks the button, no error messages are shown by the TextField, and (b) after the user clicks the button, error messages start to be shown. You retain the ability to use _getErrorMessage(value) to customize the error message based upon the current value in the TextField.
You need to set state for displaying/hiding error messages based on user input, Check below code
import React, { Component } from 'react';
class App extends Component {
state = {
isErrorMessageHidden: true
}
clickHandler = () => {
let textValue = document.getElementById('titleField').value;
if(textValue === '')
this.setState({isErrorMessageHidden: false});
else
this.setState({isErrorMessageHidden: true});
}
render() {
return (
<div>
<button onClick={this.clickHandler}>Check</button>
<input type='text' id='titleField' />
<p
style={{'color':'red'}}
hidden={this.state.isErrorMessageHidden}
>Please fill the form</p>
</div>
);
}
}
export default App;
I hope this helps.
Yet another approach would be to handle all the validations via state values.
The control definition goes like this
<TextField placeholder="Enter a venue location" required onChange={this._onVenueChange} {...this.state.errorData.isValidVenue ? null : { errorMessage: strings.RequiredFieldErrorMessage }}></TextField>
On the Onchange event, you validate the control's value and set the error state value as below,
private _onVenueChange = (event: React.FormEvent<HTMLTextAreaElement | HTMLInputElement>, newValue?: string): void => {
this.setState(prevState => ({
errorData: {
...prevState.errorData,
isValidVenue: newValue && newValue.length > 0,
isValidForm: this.state.errorData.isValidForm && newValue && newValue.length > 0
}
}));
this.setState(prevState => ({
formData: {
...prevState.formData,
venue: newValue
}
}));
}
In the above example, I am using two objects within states, one for capturing the values and other for capturing whether the field is error-ed or not.
Hope this helps..
Cheers!!!

How to simulate selecting from dropdown in Jest / enzyme testing?

I'm trying to write jest tests for my React component that has a dropdown like this:
<select id="dropdown" onChange={this.handlechange} ref={this.refDropdown}>
{this.props.items.map(item => {
return (
<option key={item.id} value={item.id}>
{item.name}
</option>
);
})}
</select>
and the handler looks like this:
handlechange = () => {
const sel = this.refDropdown.current;
const value = sel.options[sel.selectedIndex].value;
//...
}
I want to simulate a user selecting the 2nd entry (or anything other than the first) in the list but am having trouble. If I simulate a "change" event it does fire the call to handlechange() but selectedIndex is always set to zero.
I tried this code in the jest test but it doesn't cause selectedIndex to be accessible in the handler.
const component = mount(<MyComponent/>);
component.find("#dropdown").simulate("change", {
target: { value: "item1", selectedIndex: 1 }
});
What happens is almost correct. If I look at the incoming event, I can see e.value is set to "item1" as I set, but it doesn't act like the selection was actually made.
I've also tried trying to send "click" simulations to the Option element directly but that does nothing.
What's the right way to simulate a selection from a dropdown?
Try this approach:
wrapper.find('option').at(0).instance().selected = false;
wrapper.find('option').at(1).instance().selected = true;
You can trigger a change event since you have your this.handlechange trigger onChange:
const component = mount(<MyComponent/>);
component.find('#dropdown').at(0).simulate('change', {
target: { value: 'item1', name: 'item1' }
});
I would say you have to add .at(0) because Enzyme will find a list of values even if you only have one element with that ID.
Try changing the html "onInput" to "onChange" because you are simulating the "change" event in jest.
Short Answer -
Use the following snippet
import userEvent from "#testing-library/user-event";
userEvent.selectOptions(screen.getByTestId("select-element-test-id"), ["option1"]);
Detailed Answer -
.tsx file
.
.
<Form.Select
aria-label="Select a value from the select dropdown"
required
onChange={(e) => {
console.log("Option selected from the dropdown list", e.target.value);
optionChangedHandler(e.target.value);
}}
data-testid="select-element-test-id"
>
...CODE FOR RENDERING THE LIST OF OPTIONS...
</Form.Select>
.
.
.test.tsx file
import userEvent from "#testing-library/user-event";
it("Check entire flow", async () => {
render(
<YourComponent/>
);
// CHECK IF SELECT DROPDOWN EXISTS
const selectDropdown = await waitFor(
() => screen.getByTestId("select-element-test-id"),
{
timeout: 3000,
}
);
expect(selectDropdown ).toBeInTheDocument();
//"option2" is the element in the select dropdown list
userEvent.selectOptions(screen.getByTestId("select-element-test-id"), [
"option2",
]);
}
The above code will trigger the onChange function of the select element.

Resources