I have a react and typescript class containing reusable API calls set up using axios to be imported and utilised to interact with a REST endpoint:
<code>export default class NetworkConfigService {
private authService: AuthService;
private networkId: string | null;
private axiosInstance: AxiosInstance;
this.authService = new AuthService();
this.networkId = localStorage.getItem("selectedNetworkCode");
const headers: AxiosRequestConfig = {
"Content-Type": "application/json",
accept: "application/json, text/plain, */*"
this.axiosInstance = axios.create({
baseURL: config.NetworkConfigEndpoint,
this.axiosInstance.interceptors.request.use(async (config) => {
const token = await this.authService.getAccessToken();
config.headers["Authorization"] = `Bearer ${token}`;
public async getAllNetworkEntities(): Promise<[{ utc_data: UtcData }]> {
const response = await this.axiosInstance.get<
>("/api/Configurations/networks", {
networkIDs: this.networkId
const responseData = response.data;
public async getPlansForParent(
const response = await this.axiosInstance.get<Plan[]>(
`/api/Configurations/${this.networkId}/plans/${parent}/${parentName}`
const responseData = response.data;
<code>export default class NetworkConfigService {
private authService: AuthService;
private networkId: string | null;
private axiosInstance: AxiosInstance;
constructor() {
this.authService = new AuthService();
this.networkId = localStorage.getItem("selectedNetworkCode");
const headers: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
accept: "application/json, text/plain, */*"
},
responseType: "json"
};
this.axiosInstance = axios.create({
baseURL: config.NetworkConfigEndpoint,
...headers
});
this.axiosInstance.interceptors.request.use(async (config) => {
try {
const token = await this.authService.getAccessToken();
config.headers["Authorization"] = `Bearer ${token}`;
return config;
} catch (error) {
throw error;
}
});
}
public async getAllNetworkEntities(): Promise<[{ utc_data: UtcData }]> {
try {
const response = await this.axiosInstance.get<
[{ utcData: UtcData }]
>("/api/Configurations/networks", {
params: {
networkIDs: this.networkId
}
});
const responseData = response.data;
return responseData;
} catch (error) {
throw error;
}
}
public async getPlansForParent(
parent: EntityType,
parentName: string
): Promise<Plan[]> {
try {
const response = await this.axiosInstance.get<Plan[]>(
`/api/Configurations/${this.networkId}/plans/${parent}/${parentName}`
);
const responseData = response.data;
return responseData;
} catch (error) {
throw error;
}
}
}
</code>
export default class NetworkConfigService {
private authService: AuthService;
private networkId: string | null;
private axiosInstance: AxiosInstance;
constructor() {
this.authService = new AuthService();
this.networkId = localStorage.getItem("selectedNetworkCode");
const headers: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
accept: "application/json, text/plain, */*"
},
responseType: "json"
};
this.axiosInstance = axios.create({
baseURL: config.NetworkConfigEndpoint,
...headers
});
this.axiosInstance.interceptors.request.use(async (config) => {
try {
const token = await this.authService.getAccessToken();
config.headers["Authorization"] = `Bearer ${token}`;
return config;
} catch (error) {
throw error;
}
});
}
public async getAllNetworkEntities(): Promise<[{ utc_data: UtcData }]> {
try {
const response = await this.axiosInstance.get<
[{ utcData: UtcData }]
>("/api/Configurations/networks", {
params: {
networkIDs: this.networkId
}
});
const responseData = response.data;
return responseData;
} catch (error) {
throw error;
}
}
public async getPlansForParent(
parent: EntityType,
parentName: string
): Promise<Plan[]> {
try {
const response = await this.axiosInstance.get<Plan[]>(
`/api/Configurations/${this.networkId}/plans/${parent}/${parentName}`
);
const responseData = response.data;
return responseData;
} catch (error) {
throw error;
}
}
}
This is imported into a component and defined, the function is then called in a useEffect to ensure data is fetched upon the mounting of a component:
<code>const networkConfigService = useMemo(() => new NetworkConfigService(), []);
const fetchData = useCallback(async () => {
const allNetworkItems = await networkConfigService.getAllNetworkEntities();
console.log(allNetworkItems);
dispatch(addEntityData({ utc_data: allNetworkItems[0].utc_data }));
console.error("Error fetching data:", error);
}, [networkConfigService, dispatch]);
<code>const networkConfigService = useMemo(() => new NetworkConfigService(), []);
const fetchData = useCallback(async () => {
try {
const allNetworkItems = await networkConfigService.getAllNetworkEntities();
console.log(allNetworkItems);
if (allNetworkItems) {
dispatch(addEntityData({ utc_data: allNetworkItems[0].utc_data }));
}
} catch (error) {
console.error("Error fetching data:", error);
}
}, [networkConfigService, dispatch]);
useEffect(() => {
fetchData();
}, [fetchData]);
</code>
const networkConfigService = useMemo(() => new NetworkConfigService(), []);
const fetchData = useCallback(async () => {
try {
const allNetworkItems = await networkConfigService.getAllNetworkEntities();
console.log(allNetworkItems);
if (allNetworkItems) {
dispatch(addEntityData({ utc_data: allNetworkItems[0].utc_data }));
}
} catch (error) {
console.error("Error fetching data:", error);
}
}, [networkConfigService, dispatch]);
useEffect(() => {
fetchData();
}, [fetchData]);
Similarly the plansforParent is called in a method within the component to fetch the relevant plans based on a parent selection:
<code>const handleSearchFieldSelection = async (selectedEquipment: Equipment) => {
formik.setFieldValue("selectedEquipment.name", selectedEquipment.name);
formik.setFieldValue("selectedEquipment.description", selectedEquipment.description);
formik.setFieldValue("selectedEquipment.alternativeName", selectedEquipment.alternativeName);
setAction((prevAction) => {
selectedEquipment: selectedEquipment
const plans = await networkConfigService.getPlansForParent(formik.values.entityType, selectedEquipment.name);
const newPlans = plans ? plans : [];
<code>const handleSearchFieldSelection = async (selectedEquipment: Equipment) => {
formik.setFieldValue("selectedEquipment.name", selectedEquipment.name);
formik.setFieldValue("selectedEquipment.description", selectedEquipment.description);
formik.setFieldValue("selectedEquipment.alternativeName", selectedEquipment.alternativeName);
setAction((prevAction) => {
return {
...prevAction,
switchPlan: {
...prevAction,
selectedEquipment: selectedEquipment
}
};
});
const plans = await networkConfigService.getPlansForParent(formik.values.entityType, selectedEquipment.name);
const newPlans = plans ? plans : [];
setPlans(newPlans);
};
</code>
const handleSearchFieldSelection = async (selectedEquipment: Equipment) => {
formik.setFieldValue("selectedEquipment.name", selectedEquipment.name);
formik.setFieldValue("selectedEquipment.description", selectedEquipment.description);
formik.setFieldValue("selectedEquipment.alternativeName", selectedEquipment.alternativeName);
setAction((prevAction) => {
return {
...prevAction,
switchPlan: {
...prevAction,
selectedEquipment: selectedEquipment
}
};
});
const plans = await networkConfigService.getPlansForParent(formik.values.entityType, selectedEquipment.name);
const newPlans = plans ? plans : [];
setPlans(newPlans);
};
In the test file I have mocked the route to the class containing the API methods and axios:
<code>jest.mock("../../../../../../API/NetworkConfig/NetworkConfig.service");
const axios = require("axios");
let netWorkConfigService = new NetworkConfigService();
<code>jest.mock("../../../../../../API/NetworkConfig/NetworkConfig.service");
jest.mock("axios");
const axios = require("axios");
let netWorkConfigService = new NetworkConfigService();
</code>
jest.mock("../../../../../../API/NetworkConfig/NetworkConfig.service");
jest.mock("axios");
const axios = require("axios");
let netWorkConfigService = new NetworkConfigService();
I’ve tried setting up a beforeEach to fetch data initially I tried mocking axios and its response but the console.log in useEffect came back undefined.
const utc: { utc_data: UtcData }[] = [];
const resp = { data: utc };
axios.get.mockImplementation(() => Promise.resolve(resp));
<code>beforeEach(() => {
const utc: { utc_data: UtcData }[] = [];
const resp = { data: utc };
axios.get.mockImplementation(() => Promise.resolve(resp));
});
</code>
beforeEach(() => {
const utc: { utc_data: UtcData }[] = [];
const resp = { data: utc };
axios.get.mockImplementation(() => Promise.resolve(resp));
});
As that failed I then tried using a jest spy to intercept API calls to the networkConfigService and method but the data still returns undefined.
let netWorkConfigService = new NetworkConfigService();
const mockGetAllNetworkEntities = jest.spyOn(netWorkConfigService, "getAllNetworkEntities");
mockGetAllNetworkEntities.mockResolvedValue([]);
<code> beforeAll(() => {
let netWorkConfigService = new NetworkConfigService();
const mockGetAllNetworkEntities = jest.spyOn(netWorkConfigService, "getAllNetworkEntities");
mockGetAllNetworkEntities.mockResolvedValue([]);
});
</code>
beforeAll(() => {
let netWorkConfigService = new NetworkConfigService();
const mockGetAllNetworkEntities = jest.spyOn(netWorkConfigService, "getAllNetworkEntities");
mockGetAllNetworkEntities.mockResolvedValue([]);
});
This is a similar case for the API call for handleSearchFieldSelection() method which should call networkConfigService.getPlansForParent(formik.values.entityType, selectedEquipment.name) it returns undefined and hence the last part of my test:
<code>await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
<code>await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
</code>
await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
Fails as no option roles populate for planId due to the undefined response and consequently there is nothing to select.
<code>it("selects all dropdowns and submits the form", async () => {
const user = userEvent.setup();
const selectEntityType = screen.getAllByRole("combobox");
const entityTypeSelect = screen.getByTestId("entityType");
expect(selectEntityType[0]).toBeInTheDocument();
await user.click(selectEntityType[0]);
const optionToSelect = screen.getByText("EntityType.Scoot_Region");
await user.click(optionToSelect);
expect(entityTypeSelect).toHaveValue("Region");
const autocomplete = screen.getAllByRole("combobox");
await userEvent.type(autocomplete[1], "{arrowdown}{enter}");
expect(autocomplete[1]).toHaveValue("TestName-AltName, Test Description");
const selectPlanType = screen.getAllByRole("combobox");
const planTypeSelect = screen.getByTestId("planTypeSelect");
expect(planTypeSelect).toBeInTheDocument();
await user.click(selectPlanType[2]);
const optionToSelect2 = screen.getByText("PlanTypes.LocalControl");
await user.click(optionToSelect2);
expect(planTypeSelect).toHaveValue("LocalControl");
const selectPlanId = screen.getAllByRole("combobox");
const planIdSelect = screen.getByTestId("planIdSelect");
expect(planIdSelect).toBeInTheDocument();
await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
await user.click(option[0]);
const submitButton = screen.getByText("StrategyManager.Save_Action");
await user.click(submitButton);
expect(mockHandleSubmit).toHaveBeenCalled();
<code>it("selects all dropdowns and submits the form", async () => {
const user = userEvent.setup();
renderComponent();
const selectEntityType = screen.getAllByRole("combobox");
const entityTypeSelect = screen.getByTestId("entityType");
expect(selectEntityType[0]).toBeInTheDocument();
await user.click(selectEntityType[0]);
const optionToSelect = screen.getByText("EntityType.Scoot_Region");
await user.click(optionToSelect);
expect(entityTypeSelect).toHaveValue("Region");
const autocomplete = screen.getAllByRole("combobox");
await userEvent.type(autocomplete[1], "{arrowdown}{enter}");
expect(autocomplete[1]).toHaveValue("TestName-AltName, Test Description");
const selectPlanType = screen.getAllByRole("combobox");
const planTypeSelect = screen.getByTestId("planTypeSelect");
expect(planTypeSelect).toBeInTheDocument();
await user.click(selectPlanType[2]);
const optionToSelect2 = screen.getByText("PlanTypes.LocalControl");
await user.click(optionToSelect2);
expect(planTypeSelect).toHaveValue("LocalControl");
const selectPlanId = screen.getAllByRole("combobox");
const planIdSelect = screen.getByTestId("planIdSelect");
expect(planIdSelect).toBeInTheDocument();
await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
await user.click(option[0]);
const submitButton = screen.getByText("StrategyManager.Save_Action");
await user.click(submitButton);
expect(mockHandleSubmit).toHaveBeenCalled();
});
</code>
it("selects all dropdowns and submits the form", async () => {
const user = userEvent.setup();
renderComponent();
const selectEntityType = screen.getAllByRole("combobox");
const entityTypeSelect = screen.getByTestId("entityType");
expect(selectEntityType[0]).toBeInTheDocument();
await user.click(selectEntityType[0]);
const optionToSelect = screen.getByText("EntityType.Scoot_Region");
await user.click(optionToSelect);
expect(entityTypeSelect).toHaveValue("Region");
const autocomplete = screen.getAllByRole("combobox");
await userEvent.type(autocomplete[1], "{arrowdown}{enter}");
expect(autocomplete[1]).toHaveValue("TestName-AltName, Test Description");
const selectPlanType = screen.getAllByRole("combobox");
const planTypeSelect = screen.getByTestId("planTypeSelect");
expect(planTypeSelect).toBeInTheDocument();
await user.click(selectPlanType[2]);
const optionToSelect2 = screen.getByText("PlanTypes.LocalControl");
await user.click(optionToSelect2);
expect(planTypeSelect).toHaveValue("LocalControl");
const selectPlanId = screen.getAllByRole("combobox");
const planIdSelect = screen.getByTestId("planIdSelect");
expect(planIdSelect).toBeInTheDocument();
await user.click(selectPlanId[3]);
const option = screen.getAllByRole("option");
await user.click(option[0]);
const submitButton = screen.getByText("StrategyManager.Save_Action");
await user.click(submitButton);
expect(mockHandleSubmit).toHaveBeenCalled();
});