How to check if alert has been called in Jest testing React using Typescript? - reactjs

I have a HTML select with some options which when I press a button get submitted to a server. Depending on the response an alert is shown.
The method looks like this:
const sendOrder = async (json : string) => {
const request = await fetchUrl("<irrelevant url>", "POST", json);
if(request.status === 200){
const order = await request.json();
alert(`Created Order #${order.data.id}`)
}else{
alert("Order failed")
}
}
Now I have mocked the server using msw and my test looks something like this:
it('should create an order', async () => {
render(<ArticleList />);
render(<ShoppingCart afterSubmit={()=> {}}/>)
const articlesList = getNullSafeElement('articles');
await waitFor(() => {
expect(articlesList.children.length).toBe(2);
})
const orderButton = getNullSafeElement('submitBtn');
const shoppingCart = document.querySelector('#cart') as HTMLSelectElement;
const addressInput = document.querySelector('#address') as HTMLInputElement;
addressInput.value = "testAddress"
expect(shoppingCart.options.length).toBe(0);
fireEvent.click(articlesList.children[0]);
expect(shoppingCart.children.length).toBe(1);
fireEvent.click(orderButton);
});
I know for sure that this test runs as expected and that the request is definetly beeing sent to the mocked server by fireEvent.click(orderButton).
However, if I now want to do something like this expect(global.alert).toHaveBeenCalledTimes(1), it does not work.
Now I know that I have to mock the call and I saw some posts on SO about this issue
Mock or asser whether window.alert has fired
Test alert in React Native
But neither global.alert = jest.fn(), nor jest.spyOn(global, 'alert') have worked out. Both result in 0 calls.
So my question is: How can I test if alert() has been called in my test?
(I am creating a frontend using create-react-app with the typescript template and testing with Jest)

Related

Browser not Defined error in Jest-Image-Snapshot Testing in React

I want to implement https://github.com/americanexpress/jest-image-snapshot#-api in my React application and tried writing one of the test cases but gives me an error saying the browser is not defined when trying to run a test using the command "npm test".I am using a library called Jest-Image-snapshot by americanexpress
import { toMatchImageSnapshot } from 'jest-image-snapshot';
expect.extend({ toMatchImageSnapshot });
jest.setTimeout(10000);
it('renders correctly', async () => {
const page = await browser.newPage();
await page.goto('https://localhost:3000');
const image = await page.screenshot();
expect(image).toMatchImageSnapshot();
});

Why can't react testing library access elements with Selenium?

I have a selenium / jest project. I have installed react testing library and a few packages, but when I try to make valid assertions, I get an error. Code:
import { Vocal } from '../../Models/vocalModel';
import {screen} from '#testing-library/dom';
const vocal = new Vocal()
// Test Variables here
let validEmail = 'email#email.com'
let validPassword = 'password'
let invalidEmail = 'ThisEmailIsNotValid'
let invalidPassword = 'ThisPassWordIsNotValid'
describe("Login Test Suite", () => {
beforeAll(async ()=>{
await vocal.navigate();
});
afterAll( async ()=>{
await vocal.quit();
});
test('A user can login and logout', async () =>{
await vocal.userLogin(validEmail, validPassword);
await vocal.userLogout();
let getSignUpButtonText = await vocal.getText(vocal.signUpButtonLogin)
console.log(getSignUpButtonText)
expect(screen.getByText("Sign Up")).toBeVisible();
})
});
When I run this test, the browser runs and the test fails at the final assertion. I get the following error: "TestingLibraryElementError: Unable to find an element with the text: Sign Up. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.".
See photo:
Image of Error here
I've consoled 'getSignUpButtonText' which is defined as the text of that button on the page and it returns "Sign Up" so I know that text is on the page. Why can't screen.getByText('Sign Up') find it?
I have tried a few different packages but I cant seem to get away from this error.

Using Jest Mock API Calls to SetState

I have an API call which runs whenever a certain component mounts. If this API call is successful the response data is used to update the state of one of my React Hooks.
The issue I am having is either related to asynchronicity or a poorly formatted mock API call, but no matter what I try I cannot get this test to work.
Here is a simplified version of the API:
const getOrg =() => {
axios.get(URL, config)
.then(response => response.data)
.then(data => {
setOrgTitle(data.name)
}
}
Basically the API is triggered and my setOrgTitle hook is updated from the response.
const [orgTitle, setOrgTitle] = useState("");
Now in my return statement I am displaying the value of orgTitle:
<h1 className={styles.titleText} id="document-folders-h1">
{orgTitle} Folders
</h1>
Alright, so the component is pretty simple. When I am trying to test things my two ideas were to either set the initial orgTitle hook state in my test or to mock the API. After some research I decided mocking the API was the way to go.
So I have a mockAxios component:
const mockAxios = {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
module.exports = mockAxios;
And my test file:
import mockAxios from "../../mockTests/DocumentFoldersMock";
it("fetches results for getAdminOrg", () => {
axios.get.mockImplementation(() =>
Promise.resolve({ data: { name: "GETtest" } })
);
const wrapper = mount(
<AppProviders>
<DocumentFolders />
</AppProviders>
);
const orgTitle = wrapper.find("#document-folders-h1");
expect(orgTitle.text()).toContain("GETtest Folders");
});
I am mocking the response data, however I am not sure how to run the setOrgTitle function which is called in the .then of my actual axios call. How can I do this from my mock axios call using my mock response?
The result of the Jest test says expected("GETtest Folders") received(" Folders") so I am pretty sure that I am either having an issue with asynchronicity or an issue calling the hook update.

Jest - how to mock a class in jest

I've been trying to mock a test in jest through the methods that they have on their documentation. By mocking the whole class but I can't seem to get it to work properly.
https://jestjs.io/docs/en/es6-class-mocks
jest.mock('../../../../../src/SubscriptionOrder');
SubscriptionOrder.prototype.createChargebeeSubscription = jest.fn(() => 'response');
const test = new SubscriptionOrder(
'subscription',
[{}],
'errorMethods',
'customerMethods',
);
test.createChargebeeSubscription();
I'd expect this to mock the createChargebeeSubscription method and return the string response but it seems to be returning undeifined
Then this is the piece of code I'm trying to run a test for as well.
const subscriptionOrder = new SubscriptionOrder(
'subscription',
subscriptionRequest,
errorMethods,
customerMethods,
);
const response = await subscriptionOrder.createChargebeeSubscription(token);
this.setState({ successfulSubmit: response });
I want to update the state to the string response but getting undefined instead. so it appears I'm kinda mocking something but just not properly.
You can use spyOn as follows to do the mocking for you. I also recommend that you set up and tear down this spy once you are finished.
So here's a sample piece of code which will do what you want:
describe('createChargebeeSubscription() method behaviour', () => {
let createChargebeeSubscriptionSpy;
let testResponse;
beforeAll(() => {
// Lets create an instance of your class first
const subscriptionOrder = new SubscriptionOrder(
'subscription',
subscriptionRequest,
errorMethods,
customerMethods
);
// Now use a spy to mock the return value from your function
createChargebeeSubscriptionSpy = jest.spyOn(subscriptionOrder, 'createChargebeeSubscription').mockImplementation(() => {
return 'response';
});
// Finally invoke the method being tested
testResponse = subscriptionOrder.createChargebeeSubscription();
});
afterAll(() => {
// Restore the functionality (ie. disable the spy) of your method
createChargebeeSubscriptionSpy.mockRestore();
});
it('verifies that the expected response was returned', () => {
expect(testResponse).toBe('response');
});
});

Why does react hook throw the act error when used with fetch api?

I keep getting Warning: An update to App inside a test was not wrapped in act(...). in my test suite whenever I make an API request and update the state.
I'm making use of react-testing-library. I also tried using ReactDOM test utils, got the same result. One other thing I tried was wrapping the container in act, still got the same result.
Please note that: My App works and my test passes. I just need to know what I was doing wrong or if it's a bug in the react-dom package that's making that error show up. And it's bad to mock the console error and mute it.
global.fetch = require('jest-fetch-mock');
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await wait();
expect(content.querySelectorAll('.content--item').length).toBe(2);
});
Here's the hook implementation:
const [data, setData] = useState([]);
const [error, setError] = useState('');
const fetchInitData = async () => {
try {
const res = await fetch(API_URL);
const data = await res.json();
if (data.fault) {
setError('Rate limit Exceeded');
} else {
setData(data.results);
}
} catch(e) {
setError(e.message);
}
};
useEffect(() => {
fetchInitData();
}, [isEqual(data)]);
It's a known problem, check this issue in Github https://github.com/kentcdodds/react-testing-library/issues/281
For anyone who stumbles upon this more than a year later as I did, the issue Giorgio mentions has since been resolved, and wait has since been replaced with waitFor, as documented here:
https://testing-library.com/docs/dom-testing-library/api-async/
That being the case, I believe the solution to the warning now should be something like this:
import { render, waitFor } from '#testing-library/react';
// ...
it('should clear select content item', async () => {
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
await waitFor(() =>
expect(content.querySelectorAll('.content--item').length).toBe(2);
);
});
In my case, I had an App component loading data asynchronously in a useEffect hook, and so I was getting this warning on every single test, using beforeEach to render App. This was the specific solution for my case:
beforeEach(async () => {
await waitFor(() => render(<App />));
});
To get rid of the act() warning you need to make sure your promises resolve synchronously. You can read here how to do this.
Summary:
The solution for this is a bit involved:
we polyfill Promise globally with an implementation that can resolve
promises 'immediately', such as promise
transpile your javascript with a custom babel setup like the one in this repo
use jest.runAllTimers(); this will also now flush the promise task queue
I had this problem and gave up using wait and async instead used jest faketimers and so on, so your code should be something like this.
global.fetch = require('jest-fetch-mock');
it('should clear select content item', /*async */ () => {
jest.useFakeTimers();
fetch.mockResponseOnce(JSON.stringify({ results: data }));
const { container } = render(<App />);
const content = container.querySelector('.content');
// await wait();
act(() => {
jest.runAllTimers();
});
expect(content.querySelectorAll('.content--item').length).toBe(2);
});

Resources