translate is not a function Admin-On-Rest test - reactjs

I am trying to test empty username text field on my login page based on admin-on-rest Login page.
https://github.com/marmelab/admin-on-rest/blob/master/src/mui/auth/Login.js
My testcase is like this.
import Login, { renderTextField } from '../../../modules/core/Login';
import {propTypes, reduxForm, Field, reducer as formReducer} from 'redux-form';
import redux, { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
describe ('<Login/>', () => {
let container
let submitting
let onSave
let store
let tn
beforeEach(()=>{
submitting = false
//tn= sinon.spy()
onSave = Promise.resolve()
})
it("shows help text when first name is set to blank", () => {
store = createStore(combineReducers({ form: formReducer }))
tn = (label) => label;
const props = {
onSave,
submitting: false,
theme: customTheme,
translate: tn,
store
}
container = mount(
<Provider store={store}>
<Login {...props}/>
</Provider>
)
const input = container.find('TextField').first()
input.simulate('blur')
console.log(input.debug())
expect(input.props.errorText).to.equal('Required')
})
})
I get an error in the redux form validate function.
TypeError: translate is not a function
at validate (C:/src/modules/core/Login.js:165:25)
ie this line on the link in above code
errors.username = translate('aor.validation.required');
How can I test this?

You have to include the TranslationProvider too
import { TranslationProvider } from 'admin-on-rest';
/* ... */
it("shows help text when first name is set to blank", () => {
store = createStore(combineReducers({ form: formReducer }))
const props = {
onSave,
submitting: false,
theme: customTheme,
store
}
container = mount(
<Provider store={store}>
<TranslationProvider locale="en">
<Login {...props}/>
</TranslationProvider>
</Provider>
)
const input = container.find('TextField').first()
input.simulate('blur')
console.log(input.debug())
expect(input.props.errorText).to.equal('Required')
})

Related

testing-library/react rendering empty <div /> for a redux toolkit component

The issue it produces is it won't successfully render the MyComponent to the Mock DOM. console.log store displays the correct state in the store as well. But it's just rendering the empty in the body tag.
import React from 'react';
import configureMockStore from 'redux-mock-store';
import * as actions from 'store/reducer/reducer';
import { fireEvent, render, screen } from 'testUtils';
import MyComponent from 'components/MyComponent';
import { initialState } from 'store/reducer/reducer';
const mockStore = configureMockStore();
describe('my component', () => {
let message;
beforeAll(() => {
message = 'testing';
});
it('test 1', () => {
const store = mockStore({
myState: {
...initialState,
message,
},
});
render(<MyComponent />, {
store: store,
});
screen.debug();
expect(screen.queryAllByText(message).length).toBe(1);
});
});
// in testUtils
function render(ui, { store = configureStore(), ...renderOptions } = {}) {
function Wrapper({ children }) {
return (
// ..some other Providers
<Provider store={store}>
{children}
</Provider>
);
}
export {render};
now the screen.debug() only shows
<body>
<div />
</body>
// in MyComponent
const MyComponent = (): JSX.Element => {
const dispatch = useDispatch();
const myState = useSelector(myReducer);
return (
<AnotherComponent
isOpen={myState?.isOpen}
message={myState?.message}
/>
);
};
I found the reason why it's not working. It's because, in myComponent, I had a typo in the conditional statement. Thanks, everyone.

Jest/Enzyme TypeError: (0 , _store.configureStore) is not a function

This is my first time testing a react component with redux store related stuff and really confused on pretty much everything.
Tried all google research and still not knowing what to do.
The component looks like below and got two errors in testing console
TypeError: (0 , _store.configureStore) is not a function
TypeError: Cannot read property 'getState' of undefined
the component:
import PropTypes from 'prop-types';
import __includes from 'lodash/includes';
import __isEmpty from 'lodash/isEmpty';
import __lowerCase from 'lodash/lowerCase';
import __toLower from 'lodash/toLower';
import { connect } from 'react-redux';
import { setEmailErrorClass, setPasswordErrorClass, setConfirmPasswordErrorClass } from '../../../redux/actions';
import './TextboxWrapper.scss';
const { notify } = require('../../../services/functions');
class Textbox extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.defaultValue !== undefined ? this.props.defaultValue : '',
name: this.props.name,
errorClass: '',
errorMessage: ''
};
this.checkErrors = this.checkErrors.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.isError) {
this.setState({ errorClass: 'error' });
}
}
// ...some functions...
render() {
return (
<div data-type={this.props.type} className={`textbox-wrapper ${this.state.errorClass}`}>
{
this.props.type === 'payment' ? (
<div id={this.props.id} className={this.props.extraClass}></div>
) : (
<input
type={this.props.type}
placeholder={this.props.placeholder}
onChange={e => this.onChange(e)}
onBlur={this.checkErrors}
value={this.state.value}
name={this.props.name}
min={this.props.min}
max={this.props.max}
onFocus={this.props.onFocus}
/>
)
}
</div>
);
}
}
const mapDispatchToProps = { setEmailErrorClass, setPasswordErrorClass, setConfirmPasswordErrorClass };
export default connect(null, mapDispatchToProps)(Textbox);
Below is configureStore:
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import rootReducer from './rootReducer';
const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware({
serializableCheck: false
})
});
export default store;
Below is Textbox.test.js:
import React from 'react';
import { Textbox } from './index';
import { Provider } from 'react-redux';
import { mount, shallow } from 'enzyme';
import { configureStore } from '../../../redux/store';
import { browserHistory } from 'react-router';
describe('Textbox', function () {
const baseProps = {[enter image description here][1]
errorClass: 'nope',
};
let store;
beforeAll(() => {
store = configureStore({}, browserHistory);
});
it('renders a wrapping div with accurate className received from props', () => {
const wrapper = mount(
<Provider store={store}>
<Textbox {...baseProps} />
</Provider>
);
const selectWrapper = wrapper.find('div');
expect(
selectWrapper.hasClass(`textbox-wrapper ${baseProps.errorClass}`)
).toEqual(true);
});
Got two errors in console:
[1]: https://i.stack.imgur.com/D0hWk.jpg
'../../../redux/store' doesn't appear to export a configureStore function:
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit';
import rootReducer from './rootReducer';
const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware({
serializableCheck: false
})
});
export default store;
It default exports the store object.
I'm guessing you either want to import and use the configured store object in your test:
import store from '../../../redux/store';
import { browserHistory } from 'react-router';
describe('Textbox', function () {
const baseProps = {
errorClass: 'nope',
};
it('renders a wrapping div with accurate className received from props', () => {
const wrapper = mount(
<Provider store={store}>
<Textbox {...baseProps} />
</Provider>
);
const selectWrapper = wrapper.find('div');
expect(
selectWrapper.hasClass(`textbox-wrapper ${baseProps.errorClass}`)
).toEqual(true);
});
...
}
Or you want to import configureStore from '#reduxjs/toolkit' and import your reducers and instantiate the store object in the test. This ends up effectively the same as above.
import { configureStore } from '#reduxjs/toolkit';
import { browserHistory } from 'react-router';
import rootReducer from './rootReducer';
describe('Textbox', function () {
const baseProps = {[enter image description here][1]
errorClass: 'nope',
};
let store;
beforeAll(() => {
store = configureStore({
reducer: rootReducer,
});
});
it('renders a wrapping div with accurate className received from props', () => {
const wrapper = mount(
<Provider store={store}>
<Textbox {...baseProps} />
</Provider>
);
const selectWrapper = wrapper.find('div');
expect(
selectWrapper.hasClass(`textbox-wrapper ${baseProps.errorClass}`)
).toEqual(true);
});
...
}

TypeError: ref.collection is not a function using React-Redux-Firebase/React-Testing-Library

I have been having a ton of trouble trying to render a react-redux-firebase connected component using react-testing-library. I keep getting hit with a [TypeError: ref.collection is not a function] error that crashes my test as soon as I try to run it.
The component:
#firestoreConnect((props, {getState}) => {
const {auth} = getState();
const {clientId} = auth.data;
return [
{
collection: 'clients',
doc: clientId,
subcollections: [{collection: 'users'}]
},
{
collection: 'clients',
doc: clientId,
subcollections: [{collection: 'scripts'}]
},
{
collection: 'clients',
doc: clientId,
subcollections: [{collection: 'teams'}]
},
{
collection: 'clients',
doc: clientId,
subcollections: [{collection: 'lists'}],
where: [
['status', '==', 'complete'],
['archived', '==', false]
]
}
];
})
#connect(({auth, profile, ggFirestore}, {params}) => {
const {clientRef, displayName, legacyClientId, permissions} = auth.data;
return {
users: ggFirestore.getIn(['users']),
lists: ggFirestore.getIn(['lists']),
scripts: ggFirestore.getIn(['scripts']),
teams: ggFirestore.getIn(['teams']),
profile,
clientRef,
displayName,
legacyClientId,
permissions,
params
};
})
export default class CampaignForm extends Component {
constructor(props) {
super(props);
const campaignTypes = {
'1to1sms': [
VisibilityStep,
TemplatesStep,
ListsStep,
OwnerStep,
NameStep,
SummaryStep
],
'1to1email': [
NameStep,
TemplatesStep,
ListsStep,
SendEmailsStep,
SuccessStep
]
};
this.state = {
step: 0,
contactTypeSteps: campaignTypes[props.params.contactType],
campaignId: '',
draftCampaignId: '',
type: 'organizing',
isProcessing: false,
confirmSummary: false,
contactType: props.params.contactType
};
}
...
render() {
const {
step,
success,
error,
contactTypeSteps,
campaignId,
draftCampaignId,
isProcessing,
confirmSummary,
contactType
} = this.state;
const {params, lists, users, teams, scripts, profile} = this.props;
const isLastStep =
contactTypeSteps.length === step + 1 ||
(contactType === '1to1email' && step === 3);
const StepComponent = contactTypeSteps[step];
if (profile && profile.role === 'volunteer') {
return <PermissionDenied />;
}
if (users && lists && scripts && teams) {
return (
<div className="top-spacer" date-testid="_campaign-form">
<SuccessMessage
message={success}
handleDismiss={() => {
this.setState({success: null});
}}
/>
<ErrorMessage
error={error}
handleDismiss={() => {
this.setState({error: null});
}}
/>
{confirmSummary ? (
<SuccessStep
campaignId={campaignId ? campaignId : draftCampaignId}
campaignType={params.contactType}
{...this.state}
{...this.props}
isOrganizer={profile && profile.role === 'organizer'}
/>
) : (
<StepComponent
handleNext={
isLastStep ? ::this.handleFinalSubmit : ::this.handleNextClick
}
handlePrev={::this.handleBackClick}
handleChange={::this.handleChange}
contactType={params.contactType}
isLastStep={isLastStep}
isProcessing={isProcessing}
{...this.props}
{...this.state}
isOrganizer={profile && profile.role === 'organizer'}
/>
)}
</div>
);
}
return <Loading />;
}
}
My test wrapper:
import '#testing-library/jest-dom/extend-expect';
import 'firebase/firestore';
import { render } from '#testing-library/react';
import firebase from 'firebase';
import { createMemoryHistory } from 'history';
import React from 'react';
import { Provider } from 'react-redux';
import { reactReduxFirebase } from 'react-redux-firebase';
import { MemoryRouter } from 'react-router-dom';
import { compose, createStore } from 'redux';
import { reduxFirestore } from 'redux-firestore';
import rootReducer from '../ducks';
jest.mock('../store/fbTestConfig', () => ({
firebase: {
firestore: jest.fn(() => ({})),
initializeApp: jest.fn(() => {})
}
}));
const mockStore = compose(
reactReduxFirebase(firebase, {}),
reduxFirestore(firebase)
)(createStore);
export function renderWithRedux(
Component,
{
initialState,
store = mockStore(rootReducer, initialState),
route = '/',
history = createMemoryHistory({initialEntries: [route]})
} = {}
) {
return {
...render(
<Provider store={store}>
<MemoryRouter initialEntries={[route]}>{Component}</MemoryRouter>
</Provider>,
{}
),
store,
history
};
}
My test:
import '#testing-library/jest-dom/extend-expect';
import { cleanup } from '#testing-library/react';
import React from 'react';
import CampaignForm from '../../components/Campaigns/CampaignForm';
import { renderWithRedux } from '../renderWithRedux';
beforeEach(cleanup);
test('CampaignForm renders', () => {
const {debug} = renderWithRedux(<CampaignForm />, {
route: 'organize/1to1sms'
});
debug();
});
I tried console logging the component and it looks like it is wrapped properly by firestoreConnect, but the ref key is always set to null. I have tried looking everywhere for some kind of answer but no luck. Any help would be GREATLY appreciated!
So I finally figured it out! The component was expecting props that I did not seed properly when setting up my test.
In my component I had these lines:
const {auth} = getState();
const {clientId} = auth.data;
Where auth is a piece of my application state. The initial state for this was an empty Map, so when I tried to pull clientId from there and use it to get my collections, it threw an error.
The solution was just using my mock store to dispatch the appropriate action to populate the auth Map. Worked like a charm.

Redux form test using jest

I am trying to test redux form submit for some code similar to this file.
https://github.com/marmelab/admin-on-rest/blob/master/src/mui/auth/Login.js
My code is like this
const middlewares = [];
const mockStore = configureMockStore(middlewares);
it("submit button", () => {
userLogin = jest.fn();
const initialState = {
admin: {
notification: {
text: "",
type: "info"
}
},
};
store = mockStore(initialState);
tn = label => label;
const props = {
submitting: false,
theme: customTheme,
translate: tn,
store,
location: {
state: {
nextPathname: "/"
}
},
userLogin: userLogin
};
container = mount(
<Provider store={store}>
<TranslationProvider locale="en">
<Login {...props} /> //Login is connected component
</TranslationProvider>
</Provider>
,
{
context: { store: mockStore(initialState) }
}
);
const username = container.find("TextField").first();
username.simulate("change", { target: { value: "admin" } });
const password = container.find("TextField").last();
password.simulate("change", { target: { value: "Superuser" } });
const form = container.find("form");
form.simulate("submit");
console.log(username.debug());
expect(userLogin).toHaveBeenCalled();
});
I face two problems.
When I print the username then I dont see the update value that I update through simulate.
Secondly, the expect clause fails. How do I ensure that userLogin function in my code got called.
Expected mock function to have been called.
This is how I test my redux-forms using JEST snapshot testing.
import React from 'react'
import renderer from 'react-test-renderer'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import YourReduxFormComponent from 'path where it sitting in your project'
import { reduxForm } from 'redux-form'
jest.mock('react-dom')
const spy = jest.fn()
const initialStateValues = {/* initial state values that your form component expects */}
const Decorated = reduxForm({
form: 'testForm', onSubmit: { spy }
})(YourReduxFormComponent)
const formFieldValues = {/*Form field values*/}
it('YourReduxFormComponent renders correctly', () => {
const store = createStore((state) => state, initialStateValues)
const tree = renderer.create(
<Provider store={store}>
<Decorated
{...formFieldValues}
/>
</Provider>
).toJSON()
expect(tree).toMatchSnapshot()
})
//Make your own tests like above to test the values

Enzyme - testing restricted route

So I created a RestrictedRoute component based on Route from react-router (v4) and using branch method from recompose:
import React from 'react';
import { connect } from 'react-redux';
import { compose, branch, renderComponent } from 'recompose';
import { Route, Redirect } from 'react-router-dom';
const RestrictedRoute = (props) => {
const { component, ...otherProps } = props;
return <Route {...otherProps} component={component} />;
};
const mapStateToProps = state => ({
authenticated: state.authentication.session,
});
const branched = branch(
({ authenticated }) => !authenticated,
renderComponent(() => <Redirect to="/" />),
);
const enhanced = compose(
connect(mapStateToProps),
branched,
)(RestrictedRoute);
export default enhanced;
It works perfectly fine but now I need to write some tests that will tell me if the redirect is working properl, so I did this:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import { Redirect, MemoryRouter } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import RestrictedRoute from '../RestrictedRoute';
import { initialAuthenticationState } from 'Reducers/authentication';
describe('<RestrictedRoute />', () => {
const mockStore = configureStore();
let store;
let container;
beforeEach(() => {
store = mockStore({
authentication: { ...initialAuthenticationState }, // authentication.session is false in the initialAuthenticationState
});
container = shallow(
<MemoryRouter>
<Provider store={store}>
<RestrictedRoute />
</Provider>,
</MemoryRouter>
);
})
test('redirects if not authenticated', () => {
expect(container.find(Redirect).length).toBe(1);
});
});
I get the following results, which is not what I expected:
● <RestrictedRoute /> › redirects if not authenticated
expect(received).toBe(expected)
Expected value to be (using ===):
1
Received:
0
What am I missing?
The problem was with the shallow. I shouldn't have used it because it is not its purpose. mount was the function I was looking for.

Resources