I can't pass the test with fireEvent.change(getByLabelText()); - javascript

I am learning testing and there is a test that I can not get right, it is supposed to change the value of the input should call the function onPriceInputChange, but it does not, I also tried from the browser console, the value is set, but the function is not called the input has a onChange={onPriceInputChange}
I leave the code of the test and the code of my component,
three props go into the component which are priceFrom, priceTo and the onPriceInputChange function. priceFrom and priceTo are part of a useState of a parent component which is set with the onPriceInputChange function also in the parent component.
What can I change to make the test pass? I have checked the documentation and everything is apparently ok.
the following are the tests:
describe('FilterForm component', () => {
it('should invoke the onPriceInputChange callback after "price from" input value changed', () => {
// given
const onPriceInputChange = jest.fn();
const { getByLabelText } = render(<FilterForm
priceFrom=''
onPriceInputChange={onPriceInputChange}
/>);
// when
fireEvent.change(getByLabelText('Price From:'), { target: {
name: 'priceFrom',
value: 123
}});
// then
expect(onPriceInputChange).toHaveBeenCalledWith('priceFrom', 123);
});
it('should invoke the onPriceInputChange callback after "price to" input value changed', () => {
// given
const onPriceInputChange = jest.fn();
const { getByLabelText } = render(<FilterForm
priceFrom=''
onPriceInputChange={onPriceInputChange}
/>);
// when
fireEvent.change(getByLabelText('Price To:'), { target: {
name: 'priceTo',
value: 456
}});
// then
expect(onPriceInputChange).toHaveBeenCalledWith('priceTo', 456);
});
and the following is the FilterForm component:
export const FilterForm = (props) => {
const onPriceInputChange = (e) => {
props.onPriceInputChange(e.target.name,e.target.value)
}
return (
<div>
<label htmlFor="priceFrom">Price From:</label>
<input
type="number"
id="priceFrom"
name="priceFrom"
value={parseInt(props.priceFrom)}
placeholder="Price from..."
onChange={onPriceInputChange}/>
<label htmlFor="priceTo">Price To:</label>
<input
type="number"
id="priceTo"
name="priceTo"
value={parseInt(props.priceTo)}
placeholder="Price to..."
onChange={onPriceInputChange}/>
</div>
)
}

Related

Set a dropdown's value in React Testing Library

For testing an input it works like this:
it('test input', () => {
const { getByTestId, getByLabelText } = render(<MyComponent />);
const myButton = getByTestId('submit-button');
expect(myButton).toBeInTheDocument();
fireEvent.change(getByLabelText('first-name'), {
target: { value: 'ted' }
});
// do something
});
the above test is setting the value of first-name input to "ted" and moves forward.
I want to do something similar but for a drop-down selector.
This is the component:
<SelectInput
label='my-dropdown'
onChange={onChange}
options={myOptions}
/>;
myOptions is an array of this shape:
const myOptions = [
{ id: '0', name: 'zero' },
{ id: '1', name: 'one' },
{ id: '2', name: 'two' }
];
it works fine in the application, no errors from this part.
Here comes the testing of it, I did something but it doesn't work:
it('test dropdpwn', () => {
const { getByTestId, getByLabelText } = render(<MyComponent />);
const saveButton = getByTestId('submit-button');
expect(saveButton).toBeInTheDocument();
fireEvent.change(getByLabelText('my-dropdown'), {
target: { value: { id: '0', name: 'zero' } }
});
// do something
});
the above code doesn't work, it doesn't set the dropdown with that value.
Any ideas on how to solve this? Not sure if it's important, but all those inputs are inside a react-hook-form and at the end it should test that the onSubmit is working (it works only if all inputs are set).
So, since the select behavior is being achieved using a button and spans.
You need to first click the button this would bring all the options on the screen and then you need to click one of those options.
And then you can finally test that the selected option is now on the screen.
it("test dropdpwn", async () => {
const { getByTestId, getByLabelText } = renderWithClientInstance(
<MapSignalModal title={title} open={true} toggle={toggle} />
);
userEvent.click(screen.getAllByTestId("selectButton")[0]);
userEvent.click(screen.getByText("sensor pool 1"));
expect(
await screen.findByText(screen.getByText("sensor pool 1"))
).toBeInTheDocument();
});
Also, to be really sure you can try the following, this should fail because "sensor pool 1" option is not initially on the screen.
And it should pass when the text is changed to "sensor pool 0" because that's there on the screen initially.
it("test dropdpwn", async () => {
const { getByTestId, getByLabelText } = renderWithClientInstance(
<MapSignalModal title={title} open={true} toggle={toggle} />
);
expect(screen.getByText("sensor pool 1")).toBeInTheDocument();
// if you replace the above text to "sensor pool 0", it should work
});

(REACT) How to set the value of a state as an object which has attributes which are also objects?

I want to receive the value of some input fields and set them as the newValue state, but some of the attributes of the state are objects themselves. The format in which I want newValue state to be :
{
name : {
firstName : 'Eithan',
lastName :'Auralius'
},
rank : 7
}
Right now, the object is getting saved like :
{
name.firstName : 'Eithan',
name.lastName : 'Auralius',
rank : 7
}
Is there a way to achieve this by tweaking the getValue function or the input field?
//State to store the created or updated user string
const [newValue, setNewValue] = useState();
//onChange triggered function to update
const getValue = (event)=> {
//Setting the value of the state using the changed input fields
setNewValue((newValue)=> {
return {
...newValue,
[event.target.name] : event.target.value,
}
});
}
const submitData = (event)=> {
event.preventDefault();
console.log(newValue);
}
return (
<div className='loginRegister'>
<form onSubmit={submitData}>
<div className='descriptionInput'><div className='description'>First Name</div>
{//the name prop is used to set the field name in the getValue function}
<input type="text" name='name.firstName' defaultValue={member.name.firstName} onChange={getValue}></input></div>
<button>Submit</button>
</form>
</div>
);
You can try this.
import { useState } from "react";
export default function App() {
let defaultState = {
name: {
firstName: "",
lastName: ""
},
rank: 0
};
const [newValue, setNewValue] = useState(defaultState);
const submitData = (event) => {
event.preventDefault();
console.log(newValue);
};
//onChange triggered function to update
const getValue = (event) => {
//Setting the value of the state using the changed input fields
setNewValue((newValue) => {
return {
name: {...newValue.name, [event.target.name]: event.target.value},
rank: newValue.rank
};
});
};
return (
<div>
<form onSubmit={submitData}>
<div>
<div>First Name</div>
<input
type="text"
name="firstName"
defaultValue={newValue.name.firstName}
onChange={getValue}
></input>
</div>
<button>Submit</button>
</form>
</div>
);
}

Unable to perform click on Vuetify vSwitch when testing with Jest

I am currently writing tests for a Vue Component which implements a Vuetify Switch. As part of the testing I want to check the functionality of the vuetify switch. I am having troubling triggering a click on the switch to then verify that the switches value has changed (and once I have done that I will verify that the value bound to the switch has changed as well)
I have looked at the API docs for Vuetify and there are no methods to directly set the state of a Vuetify switch which is bewildering in my opinion. Because of this I am trying to perform a click on the VSwitch component using wrapper.find().trigger('click') but this isn't changing the switch value, leading me to believe the click isn't doing anything at all.
Below are two tests
the first checks that the switch has the correct state on creation, which is passing
The second tries to perform a click event and check that the state has changed, which is failing
Any help in resolving this problem would be greatly appreciated.
switch.vue
<template>
<v-row>
<v-col>
<label class="label-text" :for="`${fieldLabel}`">{{labelText}}</label>
<v-row>
<label class="left-label">{{toggleLeftText}}</label>
<v-switch
:id="`${fieldLabel}`"
v-model="toggleState"
class="ma-0 pa-0"
:data-qa="`${fieldLabel}Checkbox`"
>
</v-switch>
<label class="right-label">{{toggleRightText}}</label>
</v-row>
<!--Hidden input field includes switch value in form when submitted-->
<input type="hidden" :value="toggleState" :name="`${fieldLabel}`">
</v-col>
</v-row>
</template>
<script>
export default {
name: "Switch",
props: {
fieldLabel: {
type: String,
required: true
},
labelText: {
type: String,
required: true
},
toggleLeftText: {
type: String,
required: true
},
toggleRightText: {
type: String,
required: true
},
toggleValue: {
type: Boolean,
required: true
},
},
data: function () {
return {
toggleState: this.toggleValue
}
}
}
</script>
switch.spec.js
describe('Switch', () => {
const toggleState = true;
const localVue = createLocalVue();
localVue.use(Vuetify, {
components: {
VRow,
VCol,
VSwitch,
InputError
}
});
const wrapperFactory = () => {
return shallowMount(Switch, {
localVue,
vuetify: new Vuetify(),
propsData: testProps,
});
};
const testProps = {
labelText: "Test Label",
fieldLabel: "testLabel",
toggleLeftText: "No",
toggleRightText: "Yes",
toggleValue: toggleState
};
let wrapper;
beforeEach(() => {
wrapper = wrapperFactory(testProps);
});
afterEach(() => {
wrapper.destroy();
});
it("should have correct toggle value", () => {
const vSwitch = wrapper.find(VSwitch);
expect(vSwitch.vm.value).toBe(toggleState);
});
it("should have correct toggle value after click", async () => {
const vSwitch = wrapper.find(VSwitch);
await vSwitch.trigger('click');
expect(vSwitch.vm.value).toBe(!toggleState);
});
});
I might be a bit late for answering your question, but this way you should be able to get your v-switch.
const vSwitch = wrapper.find({ name: 'v-switch' });
and then trigger the event with
vSwitch.$emit('change', <true or false>);, depending on what you're testing.
The limit with this approach is that if you have multiple v-switches in your code, you would need to target them with a data-test-id, for example like this:
<v-switch data-test-id="my-switch-1"> ... </v-switch>;
<v-switch data-test-id="my-switch-2"> ... </v-switch>;
and then I defined a helper function on top of my test file, like so:
const getSwitchComponent = (wrapper: Wrapper<Vue>, testId: string): Wrapper<Vue> => {
const switches = wrapper.findAll({ name: 'v-switch' });
const component = switches.wrappers.find(wrapper =>
wrapper.contains(`[data-test-id="${testId}"]`),
);
if (!component) {
throw Error(`Element not found: ${testId}`);
}
return component;
};
which will let you do something like this:
const mySwitch1 = getSwitchComponent(wrapper, 'my-switch-1');
const mySwitch2 = getSwitchComponent(wrapper, 'my-switch-2');
and trigger the change event so:
mySwitch1.vm.$emit('change', false);
mySwitch2.vm.$emit('change', true);

How to Create an AutoComplete in React

What I have here is a function where I call the codigo, and the nombre, in the DB
  table registrations. What I want to achieve is that the digital code that is like an autocomplete to fill in the name when you select the code.
enter image description here
class Matriculas extends Component {
state = {
status: "initial",
data: []
}
componentDidMount = () => {
this. getInfo()
}
getInfo= async () => {
try {
const response = await getAll('matriculas')
console.log(response.data)
this.setState({
status: "done",
data: response.data
});
} catch (error) {
this.setState({
status: "error"
});
}
};
render() {
const data = [...this.state.data];
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Input
width='150px'
type="text"
placeholder="Nombre completo"
value={data.nombre} />
</ColumnContainer>
</RowContainer>
</Container>
)
}
};
export default Matriculas;
What you most likely want to use is react-select
You can pass options to the select (which would be your names) and it will return values that match whatever you type in the search bar.
import Select from 'react-select'
const options = [
{ value: 'mike', label: 'Mike' },
{ value: 'john', label: 'John' },
{ value: 'vanessa', label: 'Vanessa' }
]
const MyComponent = () => (
<Select options={options} />
)
So you can take that example, and the examples in the link, and put it in your code:
import Select from 'react-select'
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Select
value={this.state.nameValue}
onChange={event => {this.setState({nameValue: e.value})}
options={options} />
</ColumnContainer>
</RowContainer>
</Container>
When using onChage, it returns an event, which has the value of the selected name. You can use that to set the state's nameValue, and then use that name value in the rest of your component as well
Once you get this up and running, it also worth looking at the async select, which allows you to give an async function that returns values (your getInfo function, for example)
-- edit --
If you want to define the onChange event elsewhere, it would look like this:
handleChange = event => {
// event.value will be the value of the select
this.setState({optionSelected: event.value});
}
and then in your onChange, tell it that is the function you want but do not invoke it (don't write it with parentheses):
<Select
value={this.state.optionSelected}
onChange={this.handleChange}
options={options} />

Testing functions with arguments in React with Jest Enzyme

I have a function named toggleFilter() in a react component which looks like this:
toggleFilter = (filterType, filterName) => {
const filterApplied = this.state.appliedFilterList[filterType].includes(filterName);
if (filterApplied) {
//Remove the applied filter
this.setState(prevState => ({
appliedFilterList: {
...prevState.appliedFilterList,
[filterType]: prevState.appliedFilterList[filterType].filter(filter => filter !== filterName)
}
}));
} else {
//Add the filter
this.setState(prevState => ({
appliedFilterList: {
...prevState.appliedFilterList,
[filterType]: [...prevState.appliedFilterList[filterType], filterName]
}
}));
}
};
This function is being passed to the child components as :
<ChildComponent toggleFilter={this.toggleFilter} />
So, i am trying to test this toggleFilter() function like this:
it("checks for the function calls", () => {
const toggleFilterMockFn = jest.fn();
const component = shallow(
<ProductList
headerText="Hello World"
productList={data}
paginationSize="10"
accessFilters={["a 1", "a 2"]}
bandwidthFilters={["b 1", "b 2"]}
termsFilters={["t 1", "t 2"]}
appliedFilterList={appliedFilter}
toggleFilter={toggleFilterMockFn}
/>
);
component.find(FilterDropdownContent).prop("toggleFilter")({ target: { value: "someValue" } });
});
But I get the error saying :
TypeError: Cannot read property 'includes' of undefined
What may be causing the issue? Can someone please help me with this.
EDIT 1: I tried the below test case:
expect(toggleFilterMockFn).toHaveBeenCalledWith(appliedFilter, "access");
But I get the below error :
expect(jest.fn()).toHaveBeenCalledWith(expected)
Expected mock function to have been called with:
[{"access": ["Access Type Of The Service"], "bandwidth": ["the allowed band width ", "the allowed band width"], "term": ["term associated with the service"]}, "access"]
But it was not called.
You can't render a parent and test a child function like that. Instead, you should render <FilterDropdownContent /> directly, and then write a test that simulates an event (like click) and checks to see if the function was called.
Something like this for example:
import React from 'react';
import { shallow } from 'enzyme';
describe('<FilterDropdownContent />', () => {
let wrapper, toggleFilter;
beforeEach(() => {
toggleFilter = jest.fn();
wrapper = shallow(
<FilterDropdownContent
toggleFilter={toggleFilter}
/>
);
});
describe('when clicking the .toggle-filter button', () => {
it('calls `props.toggleFilter()` with the correct data', () => {
wrapper.find('.toggle-filter').simulate('click');
expect(toggleFilter).toHaveBeenCalledWith({ target: { value: 'someValue' } });
});
}):
});
In this example, clicking a link with the .toggle-filter class calls the function, but you should be able to adapt this to your specific implementation.

Categories