I am writing tests for my component using react testing library and I am using nested describe blocks for separating tests
const dummyData1 = Array.from({ length: 10 }).map((obj, i) => ({title: "Test" + i,
value: "Test" + i,
key: "Test" + i,
}));
describe("Custom Select", () => {
describe("Single", () => {
afterAll(() => {
cleanup();
});
const loadMore = jest.fn();
const onChange = jest.fn();
loadMore.mockResolvedValueOnce(dummyData2);
const { queryByTestId, getByText, getAllByText, container } = render(
<MultiSelect
data={[{ title: "Test", key: "Test", value: "Test" }, ...dummyData1]}
value="Test"
loadMoreData={loadMore}
onChange={onChange}
/>
);
const selectWrapper = queryByTestId("multi-select-wrapper");
it("Renders successfully -- Single", () => {
expect(selectWrapper).toBeTruthy();
});
it("Value rendered -- Single", () => {
expect(getByText("Test")).toBeInTheDocument();
});
it("Options rendered -- Single", async () => {
act(() => {
fireEvent.click(selectWrapper);
});
const elements = getAllByText("Test");
expect(elements).toHaveLength(1);
});
it("Load More called -- Single", async () => {
act(() => {
fireEvent.click(selectWrapper);
});
const menuEl = container.querySelector(".multi-select-menu");
act(() => {
fireEvent.scroll(menuEl, { target: { scrollY: 700 } });
});
await waitFor(
() => {
expect(loadMore).toBeCalled();
},
{ timeout: 500 }
);
});
it("onChange called -- Single", async () => {
act(() => {
fireEvent.click(selectWrapper);
});
const menuItem = getByText("Test0");
act(() => {
fireEvent.click(menuItem);
});
await waitFor(
() => {
expect(onChange).toHaveBeenCalledWith("Test0");
},
{ timeout: 500 }
);
});
it("Load More called on search -- Single", async () => {
const input = container.querySelector(".multi-select-input");
act(() => {
fireEvent.change(input, { target: { value: "TT" } });
});
await waitFor(
() => {
expect(loadMore).toBeCalledTimes(1);
},
{ timeout: 500 }
);
});
});
describe("Multiple", () => {
beforeAll(() => {
cleanup();
});
const loadMore = jest.fn();
const onChange = jest.fn();
loadMore.mockResolvedValueOnce(dummyData2);
const { queryByTestId, getByText, getAllByText, container } = render(
<MultiSelect
data={[{ title: "Test", key: "Test", value: "Test" }, ...dummyData1]}
value={["Test"]}
loadMoreData={loadMore}
onChange={onChange}
multiple
showCheckboxes
/>
);
const selectWrapper = queryByTestId("multi-select-wrapper");
it("Renders successfully", () => {
expect(selectWrapper).toBeTruthy();
});
it("Value rendered", () => {
expect(getByText("Test")).toBeInTheDocument();
});
});
});
I am getting this error on running the tests. The tests run perfectly if I use them separately. I dont want to render in each test individually but the nested describe is not working
TestingLibraryElementError: Found multiple elements by: [data-testid="multi-select-wrapper"]
Related
I have this function inside a helper:
export const useDAMProductImages = (imageId: string) => {
const {
app: { baseImgDomain },
} = getConfig();
const response: MutableRefObject<string[]> = useRef([]);
useEffect(() => {
const getProductImages = async (imageId: string) => {
try {
const url = new URL(FETCH_URL);
const res = await fetchJsonp(url.href, {
jsonpCallbackFunction: 'callback',
});
const jsonData = await res.json();
response.current = jsonData;
} catch (error) {
response.current = ['error'];
}
};
if (imageId) {
getProductImages(imageId);
}
}, [imageId]);
return response.current;
};
In test file:
import .....
jest.mock('fetch-jsonp', () =>
jest.fn().mockImplementation(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ set: { a: 'b' } }),
}),
),
);
describe('useDAMProductImages', () => {
beforeEach(() => {
jest.clearAllMocks();
cleanup();
});
it('should return empty array', async () => {
const { result: hook } = renderHook(() => useDAMProductImages('a'), {});
expect(hook.current).toMatchObject({ set: { a: 'b' } });
});
});
The problem is that hook.current is an empty array. Seems that useEffect is never called. Can someone explain to me what I'm doing wrong and how I should write the test? Thank you in advance
I have simplified problem into these files
TestComp.tsx
export const TestComp: FC<> = () => {
const [testValue, setTestValue] = React.useState(5);
const where = React.useMemo(() => testValue, [testValue]);
const { data: productsData } = useQuery<QueryProductsResult>(QUERY_PRODUCTS, {
variables: {
where,
},
});
setTimeout(() => {
setTestValue(7);
}, 0);
console.log(productsData, where);
return <div>content</div>;
};
TestComp.test.tsx
const productsData = [
{
... some data ...
},
];
const mockContractPricing = [
{
request: {
query: QUERY_PRODUCTS,
variables: {
where: 7,
},
},
result: {
data: {
products: productsData,
},
},
},
];
describe('dsfgdas', () => {
it('ggg', async () => {
const { container } = render(
<MockedProvider mocks={mockContractPricing} addTypename={false}>
<TestComp />
</MockedProvider>
);
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));
console.log(container.innerHTML);
});
});
So when where is updated it does not work. If I put where=7 since in the beginning it does not work. But if it is changing inside the component it never works out. Even trying to make it where: 7 in the mocked query it doesn't work.
The setTimeout is put only for demonstration purpose.
Is there any way to do it?
Console output:
console.log
undefined 5
console.log
undefined 7
console.log
content
One of my unit tests is failing when I'm trying to fire a click event on a component. The component is being rendered and is enabled.
//component
import {makeEncryptedCall} from '../../foo';
const MyComponent = (props) => {
const onRedirection = async () => {
const param = {foo: 'bar'};
return await c(param)
.then((data) => {
history.push('/some-url');
});
};
return (
<>
<button
onClick={onRedirection}
data-testid='my-button'
/>
</>
)
}
// test
it('should fire redirection flow', () => {
jest.mock('../../foo', () => {
return {
makeEncryptedCall: jest.fn(() => {
const response = {
ok: true,
json: () => {
Promise.resolve({
data: 'superEncryptedStuff';
});
}
};
return Promise.resolve(response);
});
}
});
const component = screen.getByTestId('my-button');
expect(component).toBeEnabled();
fireEvent.click(component);
});
I tried finding solutions related to Call retries were exceeded posted before but they are related to setTimeouts, FakeTimers, or async-mock(which I have already implemented).
Note: The test passes when I comment out fireEvent.click. Test only fails when the event is triggered.
The issue was resolved by wrapping the fireEvent in a waitFor function.
it('should fire redirection flow', async () => {
jest.mock('../../foo', () => {
return {
makeEncryptedCall: jest.fn(() => {
const response = {
ok: true,
json: () => {
Promise.resolve({
data: 'superEncryptedStuff';
});
}
};
return Promise.resolve(response);
});
}
});
const component = screen.getByTestId('my-button');
expect(component).toBeEnabled();
await waitFor(() => fireEvent.click(component));
});
I have a inssue, with tdd. I want to call a event byText and i cant do that because i get this error in my component.
TypeError: Cannot read property 'getByText' of undefined
this is my component:
const createSubject = props => {
render(
<AnnoucementItem
{...defaultProps}
{...props}
/>
);
};
and this is my test file:
import React from 'react';
import { render, fireEvent } from 'react-native-testing-library';
import Enzyme, { shallow } from 'enzyme';
import { useDispatch } from 'react-redux';
import i18n from '../../../../src/i18n';
import AnnoucementItem from '../../../../src/screens/Profile/AnnouncementList/AnnouncementItem';
jest.mock('react-redux');
let { onEdit, toggleVisibility, onDelete } = jest.fn();
describe('<AnnoucementItem />', () => {
const defaultProps = {
navigation: {},
item: {
id: 'annoucement id',
user_id: 'id do usuario',
is_published: true,
images: {
0: {
secure_url: genericurl',
},
},
address: {
city: 'Pelotas',
state: 'RS',
number: '990',
street: 'Rua Doutor Francisco Ferreira Veloso',
neighbourhood: 'Py Crespo',
},
values: {
other: '4.32',
total: '4422.33',
},
positive_ratings: '2',
negative_ratings: '1',
onEdit: onEdit,
toggleVisibility: toggleVisibility,
onDelete: onDelete,
},
toggleVisibility: () => {},
onDelete: () => {},
onEdit: () => {},
numberOfLikes: 3,
SwitchOption: {
switchValue: false,
},
userId: () => {},
isFetching: () => {},
shouldUpdate: () => {},
userAnnouncements: () => {},
formVisibility: () => {},
error: () => {},
fetchUserAnnouncements: () => {},
deleteAnnouncement: () => {},
fillFormAnnouncement: () => {},
changeAnnouncementVisibility: () => {},
};
const createSubject = props => {
render(
<AnnoucementItem
{...defaultProps}
{...props}
/>
);
};
describe('AnnoucementItem component', () => {
it('should delete annoucement', () => {
const { getByText, getByTextId } = createSubject();
fireEvent.CallDelete(getByTextId('DeleteButton'), 'deleteAnnouncement');
fireEvent.press(getByText(i18n.t('profile.announcementList.announcementItem.deleteAnnouncementAlert')));
const {
requestDeleteAnnouncement,
successDeleteAnnouncement,
announcementId,
} = jest.fn();
useDispatch.mockReturnValue(
requestDeleteAnnouncement,
successDeleteAnnouncement,
announcementId);
expect(AnnoucementItem.prototype.onDelete.calledOnce).toBe(true);
expect(requestDeleteAnnouncement).toHaveBeenCalledWith({
actionTypes: 'REQUEST_DELETE_ANNOUNCEMENT',
});
expect(successDeleteAnnouncement).toHaveBeenCalledWith({
actionTypes: 'SUCCESS_DELETE_ANNOUNCEMENT',
dispatch: { deletedId: announcementId },
});
});
});
it('should go to edit annoucement', () => {
const { getByText, getByTextId } = createSubject();
fireEvent.CallEdit(getByTextId('EditButton'), 'EditAnnouncement');
fireEvent.press(getByText(i18n.t('profile.announcementList.announcementItem.editAnnouncement')));
const editAnnouncement = jest.fn();
const navigation = { navigate: jest.fn() };
useDispatch.mockReturnValue(editAnnouncement);
expect(editAnnouncement).toHaveBeenCalledWith({
navigation: { navigation },
});
});
it('should count interested peoples in my annoucement', () => {
const { getByText, item } = createSubject();
const interested = getByText(item.positive_ratings + item.negative_ratings);
expect(interested).toBeTruthy();
});
it('should switch visibleAnnoucement', () => {
});
});
the question is, how can i get a element ByText?
In your test update
const createSubject = props => {
render(
<AnnoucementItem
{...defaultProps}
{...props}
/>
);
};
to
const createSubject = props => {
return render(
<AnnoucementItem
{...defaultProps}
{...props}
/>
);
};
Since createSubject is a function you need to return something from that function in order to use its output.
My Code:
printOrder() {
setTimeout(() => {
window.print();
}, 0);
}
render() {
const { orderDetails, messageTexts, preferences, isLoading, imagePath, featureFlags } = this.props;
const error = get(orderDetails, 'errorCode', '');
return (
<Layout disableWhiteBG><span className={cx('printOrder')}><button className={cx('printOrders')} onClick={e => this.printOrder(e)}><Icon iconType="svg" width="45px" height="45px" viewBox="0 0 45 45" name="print" /></button></span>}
</span>
</Layout>);
}
My test cases
const printOrder = sinon.spy();
const TokenProvider = {
get: TokenProviderGetStub,
logout: TokenProviderLogoutStub,
};
const Cookie = {
load: CookieLoadStub,
};
const Constants = {
...constants,
classic_yoda_combination: true,
};
const User = {
isUserLoggedIn: isUserLoggedInStub,
};
const OrderDetailsScreen = proxyquireStrict('./OrderDetails.jsx', {
'yoda-site-components/lib/helpers/TokenProvider/TokenProvider': TokenProvider,
'yoda-core-components/lib/helpers/Cookies/Cookies': Cookie,
'yoda-site-components/lib/helpers/User/User': User,
'yoda-site-components/lib/components/Layout/Layout': reactStubWithChild('Layout'),
'../../../components/OrderDetails/OrderDetails': reactStubWithChild('OrderDetails'),
'../../../common/redirect': redirectStub,
'../../../common/constant': Constants,
'react-router': {
browserHistory: {
push: pushStub,
},
},
});
describe('Test cases for print Order', () => {
describe('printOrders in DesktopScreen', () => {
before(() => {
wrapper = shallow(
<OrderDetailsScreen {...props} />,
);
wrapper.find('.printOrders').simulate('click');
});
after(() => {
wrapper.unmount();
printOrder.reset();
});
it('Should call the printOrder button', () => {
expect(printOrder.calledOnce).to.be.true;
});
});
describe('printOrders in MobileScreen', () => {
before(() => {
wrapper = shallow(
<OrderDetailsScreen {...props} deviceType={{ isDesktop: false, isMobile: true }} />,
);
wrapper.find('.printOrders').simulate('click');
});
after(() => {
wrapper.unmount();
printOrder.reset();
});
it('Should call the printOrder button', () => {
expect(printOrder.calledOnce).to.be.true;
});
});
});
My question is how do I spy window.print with sinon?