Jest + react-navigation not locating route / params - javascript

I am trying to write a test for an app using react-navigation and I am running into issues of the route and params being read correctly.
I am getting an error of
TypeError: Cannot read property 'params' of undefined
on const [leadId] = useState(route.params.leadId);
my components looks like
export default function AComponent() {
const route = useRoute();
const navigation = useNavigation();
const dispatch = useDispatch();
const [leadId] = useState(route.params.leadId);
}
I have tried following https://callstack.github.io/react-native-testing-library/docs/react-navigation/ but I received Warning: React.createElement: type is invalid when wrapping the component.
My test looks like
import React from 'react';
import { Provider } from 'react-redux';
import { NavigationContainer } from '#react-navigation/native';
import { render, fireEvent, cleanup } from 'react-native-testing-library';
import configureMockStore from 'redux-mock-store';
import AComponent from 'components/contact/AComponent';
const mockStore = configureMockStore([]);
describe('<AComponent />', () => {
let getByTestId, store;
beforeEach(() => {
store = mockStore({});
({ getByTestId } = render(
<Provider store={store}>
<AComponent />
</Provider>
));
});
});
my mock is
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: jest.fn(),
};
});
I am not sure if I am wrapping the components incorrectly, or if I am missing something else.
Any ideas or help would be greatly appreciated.
Thanks.

Hey I just solved this myself and here is my solution
Change
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: jest.fn(),
};
});
To
jest.mock('#react-navigation/native', () => ({
...jest.requireActual('#react-navigation/native'),
useNavigation: () => ({ goBack: jest.fn() }),
useRoute: () => ({
params: {
<yourParamName>: '<paramValue>',
<yourParamName2>: '<paramValue2>',
etc...
}
}),
}));
In my case I put this code block into my setup.ts file and then in my jest config inside of package.json I pointed to it.
Example
"setupFiles": [
"./node_modules/react-native-gesture-handler/jestSetup.js",
"./jest/setup.ts"
]
Then in the test itself
const navigation = { navigate: jest.fn() };
const { getByTestId, getByText, queryByTestId } = render(<App navigation={navigation}/>);

Related

react-rte giving TypeError: r.getEditorState is not a function in next js

I have a nextjs project and it has a react-rte component
the react-rte component is displayed correctly but when I go to some other component and click back in the browsers back button I get the following error:
Unhandled Runtime Error TypeError: r.getEditorState is not a function
When I comment out the react-rte componnet the error no longer occurs
react-rte component
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
You can add a condition to check value before rendering RichTextEditor
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
import { useRouter } from "next/router";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState();
const router = useRouter();
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
setValue(module.createEmptyValue());
};
importModule();
}, [router.pathname]);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
//if `value` from react-rte is not generated yet, you should not render `RichTextEditor`
if (!value) {
return null;
}
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func
};
export default MyStatefulEditor;
You can verify the implementation with the live example and the sandbox
Try this
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
useEffect(() => {
// set the state value using the package available method
setValue(RichTextEditor.createEmptyValue())
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Like I mention in my previous comment, I notice you are importing the react-rte package twice.
In the useEffect hook you do an import to initialise the value state, by looking at the example code found here
You can achieve that using RichTextEditor.createEmptyValue() which comes from the already imported package.
You will noticed that I change the import, is not dynamic, try that and if is it works if so then try doing the dynamic import if that is what you need.

How to test that a function prop was called in react-testing-library

There are 3 files:
File 1: helpers.js
export const helpers = () => ({
bar: () => 'Bar was called',
});
File 2: TestComponent.js
import React from 'react';
import { helpers } from './helpers';
const TestComponent = () => {
const { bar } = helpers();
return (
<><button onClick={bar}/></>
);
};
export default TestComponent;
File 3: TestComponent.test.js
import React from 'react';
import userEvent from '#testing-library/user-event';
import { screen, render } from '#testing-library/react';
import TestComponent from './TestComponent';
import { helpers } from './helpers';
jest.mock('./helpers', () => ({
helpers: jest.fn(),
}));
test('bar is called', () => {
helpers.mockImplementation(() => ({
bar: jest.fn(),
}));
render(
<TestComponent />,
);
userEvent.click(screen.getByRole('button'));
expect(???????).toHaveBeenCalled();
});
This line is the key:
expect(???????).toHaveBeenCalled();
The question: How can I test if bar function was called? I was expecting that something similar to expect(helpers().bar) would work. But it doesn't.
save the function in a variable and use it in expect
test('bar is called', () => {
const bar = jest.fn()
helpers.mockImplementation(() => ({bar}));
render(
<TestComponent />,
);
userEvent.click(screen.getByRole('button'));
expect(bar).toHaveBeenCalled();
});

How to mock useLocation correctly?

I have a component that uses useLocation hook to get the path from the URL.
const { pathname } = useLocation();
useEffect(() => { }, [pathname]);
While I am trying to mock the location using ,
import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '#testing-library/react';
import { shallow } from 'enzyme';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: 'https://URL/'
})
}));
describe('<ExampleComponent />', () => {
it('should render correctly', () => {
shallow(<ExampleComponent />);
});
});
I am getting this error while I run the test,
TypeError: Cannot read property 'location' of undefined
Try mocking the useLocation as jest.fn().mockImplementation
jest.mock('react-router', () => ({
...jest.requireActual("react-router") as {},
useLocation: jest.fn().mockImplementation(() => {
return { pathname: "/testroute" };
})
}));
Below is how I have done this in my tests. * Note I am using typescript
import routeData from 'react-router';
describe('Login Page UnitTests', () => {
const useLocation = jest.spyOn(routeData, 'useLocation');
beforeEach(() => {
useLocation.mockReturnValue({ search: 'testQueryParameters'} as any);
});
// Add unit tests
}
Ensure that you clear the mock to avoid issue with data in subsequent tests
The correct way to mock useLocation is below:
import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '#testing-library/react';
import { MemoryRouter} from 'react-router-dom';
import { shallow } from 'enzyme';
const renderComponent = () => {
return (
<MemoryRouter
initialEntries={["/one", "/two", { pathname: 'https://URL/' }]}
initialIndex={1}>
<ExampleComponent />
</MemoryRouter>
);
}
describe('<ExampleComponent />', () => {
it('should render correctly', () => {
shallow(renderComponent());
});
});

React + Redux : Test failing with jest

I wonder why the test is failing when I use it with redux hooks:
The code is working finem but the tests are failing for some reason. I am unable to test if the component is being rendered or not.
Component:
import React, { useEffect, useState } from 'react';
import { fetchAllApis } from '../../../redux/actions/marketplace/marketplaceActions';
import { useDispatch, useSelector, connect } from 'react-redux';
import ApiCard from '../ApiCard/ApiCard';
import Spinner from '../../../components/Extras/Spinner/Spinner';
const ApiSection = ({ apiList, error, loading, categories }) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchAllApis({ page, category: categories }));
}, [dispatch, categories]);
const renderApiCards = () => {
return apiList.map((each) => (
<ApiCard key={each.apiId} info={each} data-test="ApiCard" />
));
};
if (loading) {
return <Spinner data-test="Spinner" />;
}
if (error) {
return <h1 data-test="Error">Error while fetching</h1>;
}
return (
<div className="ApiSection" data-test="ApiSection">
<div className="ApiSection__cards">{renderApiCards()}</div>
</div>
);
};
const mapStateToProps = ({ marketplaceApiState }) => {
const { apiList, error, loading } = marketplaceApiState;
return {
error,
loading,
apiList: Object.values(apiList),
};
};
export default connect(mapStateToProps)(ApiSection);
Here is the test for the above component:
Test:
import React from 'react';
import { mount } from 'enzyme';
import ApiListSection from './ApiListSection';
import { findByTestAttr, createTestStore } from '../../../../testUtils';
import { Provider } from 'react-redux';
const setup = (props = {}) => {
let initialState = {
marketPlaceState: {
apiList: {
a: { apiId: 'a', name: 'name', description: 'desc', categories: 'cat'}
},
},
};
const store = createTestStore(initialState);
const wrapper = mount(
<Provider store={store}>
<ApiListSection {...props} />
</Provider>
);
return wrapper;
};
describe('ApiListSection Component', () => {
let component;
beforeEach(() => {
component = setup();
});
// assertions
it('Should render without failing', () => {
const apiSection = findByTestAttr(component, 'ApiSection');
expect(apiSection.length).toBe(1); // <===== FAILING HERE !!!!!
});
});
I would really appreciate the help, thanks in advance

setup() not triggered when testing Vue Components (composition-api)

I’m using vue 2.6 with #vue/compisiton-api.
import { createLocalVue, shallowMount } from '#vue/test-utils';
import VueCompositionApi from '#vue/composition-api';
import Page from '#/components/templates/Page.vue';
import Component from './Component.vue';
const localVue = createLocalVue();
localVue.use(VueCompositionApi);
jest.mock('#vue/composition-api', () => ({
reactive: jest.fn().mockReturnValue({
isFetchingData: true,
}),
computed: jest.fn(),
watch: jest.fn(),
}));
describe('Component', () => {
it('test', async () => {
const wrapper = shallowMount(Component, {
localVue,
stubs: {
Page,
},
});
expect(wrapper.findComponent(Page).exists()).toBe(true);
});
});
For some reason when I shallowMount Component the setup() method is not triggered. Is there any specific extra config needed for that?
Thanks in advance
That's an old question, but maybe someone will stumble upon it like me.
My solution was to register the composition-api plugin just like you do it with Vuex:
import VueCompositionAPI from '#vue/composition-api'
const localVue = createLocalVue()
localVue.use(VueCompositionAPI)
return shallowMount(YourComponent, {
localVue,
// [...]
})

Categories