I am trying to test a button click and buttons functionality which navigates the page to another page. But I think my button clic in testing is not firing and also don't know how to do test if its rendering the target page on click or not
my component
import {
TextInput,
Stack,
Form,
SelectItem,
Select,
TextArea,
Theme,
Button
} from '@carbon/react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { API_PREFIX } from '../../config/api-routes';
import styles from './AddVaultInstance.module.scss';
import { useSelector } from 'react-redux';
import React, { useState } from 'react';
import {
isJsonString,
onlyLettersAndNumbersAndUnderscodeAndHyphen,
isValidConfigString
} from '../../utils/ValidationUtil';
function AddVaultInstance() {
const instanceOUapi = `${API_PREFIX}/instance`;
const navigate = useNavigate();
const ouOwner = useSelector((state) => state.root.ouOwner);
const [errors, setErrors] = useState({});
const options = {
headers: {
'Content-Type': 'application/json',
'x-tenant': ouOwner
}
};
const formDataToJson = (formData) => {
var object = {};
formData.forEach((value, key) => {
if (!Reflect.has(object, key)) {
object[key] = value;
return;
}
if (!Array.isArray(object[key])) {
object[key] = [object[key]];
}
object[key].push(value);
});
return object;
};
const createInstancesByOu = async (formData) => {
const res = await axios.post(instanceOUapi, formData, options);
console.log('results', res);
};
const handleSubmit = (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const formDataJson = formDataToJson(formData);
// starting validation
if (!onlyLettersAndNumbersAndUnderscodeAndHyphen(formDataJson.id)) {
errors['id'] = true;
} else {
errors['id'] = false;
}
if (!onlyLettersAndNumbersAndUnderscodeAndHyphen(formDataJson.name)) {
errors['name'] = true;
} else {
errors['name'] = false;
}
if (typeof formDataJson.instance_type === 'undefined') {
errors['type'] = true;
} else {
errors['type'] = false;
}
if (!isJsonString(formDataJson.config)) {
errors['config'] = true;
} else {
if (
isValidConfigString(formDataJson.instance_type, formDataJson.config)
) {
errors['config'] = false;
} else {
errors['config'] = true;
}
}
setErrors(errors);
console.log(errors);
if (
errors['id'] === false &&
errors['name'] === false &&
errors['type'] === false &&
errors['config'] === false
) {
const configuration = JSON.parse(formDataJson.config);
formDataJson.config = configuration;
createInstancesByOu(formDataJson);
navigate('/');
}
};
const handleCancel = (e) => {
e.preventDefault();
navigate('/');
};
return (
<>
<Theme theme="white">
<Form className={styles.form} onSubmit={(e) => handleSubmit(e)}>
<Stack gap={7}>
<h3>Vault Registration</h3>
<TextInput
readOnly
defaultValue="tenant"
id="tenant"
labelText="Tenant"
name="tenant"
style={{
border: '0',
box: 'none',
background: 'transparent'
}}
/>
<TextInput
readOnly
defaultValue={ouOwner}
id="orgnization_unit_id"
labelText="Organization Unit"
name="orgnization_unit_id"
style={{
border: '0',
box: 'none',
background: 'transparent'
}}
/>
<TextInput
id="id"
labelText="ID"
name="id"
placeholder="Vault Identification"
invalid={errors.id}
invalidText="Id only allows letters, numbers '-' or '_' and length from 8 to 1024 characters"
required
/>
<TextInput
id="name"
labelText="Name"
name="name"
placeholder="Vault Name"
invalid={errors.name}
invalidText="Name only allows letters, numbers '-' or '_' and length from 8 to 1024 characters"
required
/>
<Select
id="instance_type"
name="instance_type"
labelText="Type"
defaultValue="placeholder-item"
helperText="Type of backing vault being registered"
invalid={errors.type}
invalidText="Please select a type"
>
<SelectItem
disabled
hidden
value="placeholder-item"
text="Select Vault Instance"
/>
<SelectItem value="ibmcloud" text="IBM Cloud Secrets Manager" />
<SelectItem value="hashicorp" text="Hashi Corp Vault" />
</Select>
<TextArea
name="config"
labelText="Configuration"
helperText="HostName, credentials etc"
cols={50}
rows={4}
id="config"
placeholder="Placeholder Text"
invalid={errors.config}
invalidText="Invalid JSON String."
required
/>
<div className={styles.formActions}>
<Button kind="secondary" onClick={handleCancel} role="cancel">
Cancel
</Button>
<Button type="submit">Create</Button>
</div>
</Stack>
</Form>
</Theme>
</>
);
}
export default AddVaultInstance;
my test file: it has "AddVaultInstance is rendering and Cancel Button function is being called" this function, which is not working and I don't know how to perform the other step.
import React from 'react';
import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import react from '@vitejs/plugin-react';
import { useSelector } from 'react-redux';
import AddVaultInstance from '../../../src/components/instance/AddVaultInstance';
import { ticks } from 'd3';
const mockedNavigator = vi.fn();
const mockReactReduxx = vi.fn();
it.todo('render AddVaultInstance');
//console.log('myscreen: ', screen);
vi.mock("react-redux", () => ({
useSelector: vi.fn()
}));
test('AddVaultInstance Verify Cancel Button ', () => {
render(<AddVaultInstance />);
// expect(screen.getByRole("cancel", { name: "Cancel" })).toBeDefined();
const mockCallBack = vi.fn();
//both works
// name in by Text is nont case sensitive
expect(screen.getByRole("cancel")).toBeDefined();
expect(screen.getByText(/cancel/i)).toBeDefined;
});
test('AddVaultInstance Verify Create Button ', () => {
render(<AddVaultInstance />);
const mockCallBack = vi.fn();
//both works
// name in by Text is nont case sensitive
expect(screen.getByText(/Create/i)).toBeDefined;
});
test('AddVaultInstance is rendering and Cancel Button function is being called', () => {
render(<AddVaultInstance />);
// expect(screen.getByRole("cancel", { name: "Cancel" })).toBeDefined();
const cancelButton = screen.getByText(/cancel/i);
const handleCancel = vi.fn();
console.log("cancelButton: ", cancelButton);
const isfired = fireEvent.click(cancelButton);
console.log("isfired: ", isfired);
expect(handleCancel).toHaveBeenCalledTimes(1);
// TODO: how to test after click its navigating to this location "/""
});
beforeEach(() => {
vi.mock('react-router-dom', () => ({
...vi.importActual('react-router-dom'), // technically it passes without this too, but I'm not sure if its there for other tests to use the real thing so I left it in
useNavigate: () => mockedNavigator
}));
vi.mock('react-redux', () => ({
...vi.importActual('react-redux'),
useSelector: vi.fn((fn) => fn())
}));
// to handle the const in the component
vi.mock('react-redux', () => {
const ouOwner = {
data: {
ouOwner: '34234.1'
}
}
});
vi.mock('react-redux', () => ({
useSelector: vi.fn().mockImplementation((func) => func(ouOwner))
}));
}); // end of beforeEach
afterEach(() => {
// restoring date after each test run
vi.clearAllMocks();
}); // end of afterEach
I am getting following error: expected "spy" to be called 1 times Looking forward to hearing from you soon.