Stub a React hook using cypress - reactjs

I'm trying to get my head around the way that cypress does things and I come from mostly a PHP background so I'm a bit confused.
I have a very simple React component
import React from 'react';
import LoginButton from '../atom/LoginButton';
import LogoutButton from '../atom/LogoutButton';
import { useAuth0 } from '#auth0/auth0-react';
const AuthenticationButton = () => {
const { isAuthenticated } = useAuth0();
return isAuthenticated ? <LogoutButton /> : <LoginButton />;
};
export default AuthenticationButton;
and I am trying to write a test to ensure that the login/logout button appears
import {mount} from '#cypress/react18'
import AuthenticationButton from './AuthenticationButton'
import { useAuth0 } from '#auth0/auth0-react';
it('should display login if not authenticated', () => {
mount(<AuthenticationButton/>)
cy.get('button').contains('Log in');
});
it('should display logout if authenticated', () => {
cy.stub(useAuth0, 'isAuthenticated').returns(true);
mount(<AuthenticationButton/>);
cy.get('button').contains('Log out');
});
I currently get the error Cannot stub non-existent own property isAuthenticated. I think because isAuthenticated is a property and not a function. Is there a way I can mock the property to be true?

Related

Why do i get an error when using render() method in react jest testing

Im trying to create a test for my component this is what i got so far
import { render, screen } from '#testing-library/react';
import Characters from '../../pages/charaters/characters';
import { renderHook } from '#testing-library/react-hooks';
describe('<Characters/>', () => {
const handleSearchByName = jest.fn();
test("Renders <Characters/>", () => {
render(<Characters/>);
});
});
but i get the error in the render method render();
""Characters" refers to a value, but is used here as a type. Did you mean "typeof Characters"?"

React-Admin custom login page and React Hook

I'm using React-Admin 3.14 and I would like to have a custom login page. When I use the one below with the useLogin hook, I have a hook related error which I can't figure out.
import React from 'react';
import { useLogin,useNotify } from 'react-admin';
import Button from '#material-ui/core/Button'
const LoginPage = () => {
const HandleClick = () => {
console.log('Clicked login ...');
const login = useLogin;
const notify = useNotify;
login({ username:'john', password:'doe' }).catch(() => notify("Invalid email or password"));
}
return(
<Button variant="contained" onClick={HandleClick}>Login</Button>
);
}
export default LoginPage;
UseLogin is a callback to the login method of the Reac-Admin 3.14 authProvider (https://marmelab.com/react-admin/doc/3.14/Authentication.html#uselogin-hook).
The error that I get is:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
I think I must be breacking Rules of Hooks somewhere ... but which one?
Thanks for your help.
C
Your useLogin and useNotify not executed
Change const login = useLogin to const login = useLogin()
Change const notify = useNotify to const notify = useNotify()
import React from 'react';
import { useLogin,useNotify } from 'react-admin';
import Button from '#material-ui/core/Button'
const LoginPage = () => {
const login = useLogin();
const notify = useNotify();
const HandleClick = () => {
console.log('Clicked login ...');
login({ username:'john', password:'doe' }).catch(() => notify("Invalid email or password"));
}
return(
<Button variant="contained" onClick={HandleClick}>Login</Button>
);
}
export default LoginPage;

Client Side Unit Testing Meteor/React w/Enzyme

so i've been stuck for several days on an issue while implementing Unit Testing and Integration testing in a large production application that was built in Meteor/React tech stack. I am using the meteortesting:mocha package as recommended by the meteor documentation and enzyme.
The issue i am having is that i am not really grasping how i can mock the withTracker functionality. I am trying to use our dev database as the source for the test users and mock data. All of the props are generated in the tracker and then sent to the component it wraps. (Code sample below). Another issue i am having is that we are using meteor:universe for i18n internationalization. When mounting the component it shows plain text instead of the translated content. Wondering if there's a work around. Thanks in advance!
Component I am testing:
import React, { useState } from "react";
import ABCComponent from "./ABCComponent";
import XYZ from "./XYZComponent";
import * as ROUTE_CONSTANTS from "../../global/RoutesConstants";
import { withRouter } from "react-router-dom";
import { withTracker } from "meteor/react-meteor-data";
import UserAssessments from "../../collections/UserAssessments";
import moment from "moment-timezone";
import { i18n } from "meteor/universe:i18n";
const SortDashboard = (props) => {
const [isSkillsSort, setIsSkillSort] = useState(true);
return (
<div>
{/* Contains some logic to set 'isSetSkillSort' state true or false (business logic hidden for security purposes*/}
{isSkillsSort ? (
<ABCComponent user={props.user} skillsSorts={props.skillsSorts} employeeList={props.directReportEmp} />
) : (
<XYZComponent
user={props.user}
importanceSorts={props.importanceSorts}
employeeList={props.directReportEmp}
/>
)}
</div>
);
};
const SortDashboardTracker = withTracker((props) => {
if (!props.user) return {};
const abcSubscription = Meteor.subscribe("abcSubscription");
if (abcSubscription.ready()) {
const rawData = UserAssessments.find(
{ "assessor._id": Meteor.user().profile._id },
{ sort: { updatedDate: -1 } }
).fetch();
rawData.forEach((assessment) => {
//Do Something (business logic hidden for security purposes)
});
}
const xyzSubscription = Meteor.subscribe("xyzSubscription");
let directReportEmp = [];
if (xyzSubscription.ready()) {
directReportEmp = Meteor.users.find({ "profile.managerId": Meteor.user().username }).fetch();
}
return { importanceSorts, skillsSorts, directReportEmp };
})(SortDashboard);
export default withRouter(SortDashboardTracker);
My Test:
import {Meteor} from 'meteor/meteor';
import React from 'react';
import chai from 'chai';
import sinon, { mock } from 'sinon'
import {mount, shallow, configure, render} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {mockManager,mockEmp1,mockEmp2,mockEmp3,mockUser} from '../../mockUsers'
import SortDashboard from '../../../../imports/components/cllWizard/SortDashboard';
import { withRouter, BrowserRouter as Router } from "react-router-dom";
configure({adapter: new Adapter()});
if (Meteor.isClient) {
describe('WizardComponent', ()=> {
//let returnedText
//importing the mock user we created for testing purposes
const currentUser = mockUser
let props = {user: currentUser}
beforeEach(() => {
// now Meteor.user() will return the user we just imported
sinon.stub(Meteor, 'user');
Meteor.user.returns(currentUser);
// needed in methods
sinon.stub(Meteor, 'userId');
Meteor.userId.returns(currentUser._id);
});
//afterEach specifies that we want to restore the user after running the test
afterEach(() => {
Meteor.user.restore();
Meteor.userId.restore();
});
it('CLIENT: should render the Sort Dashboard', () => {
const wrapper = mount(<Router><SortDashboard.WrappedComponent {...props}/></Router>)
console.log(wrapper.debug())
});
});
}
TLDR;
Need to test a client side component that uses withTracker and withRouter
Need to be able to see the translated text from meteor:universe:i18n in the test
Pulling mock data from the db instead of manually creating it.
The issue may very well be my approach and lack of understanding. Please correct me where-ever necessary. Thanks in advance!

How do you use Enzyme to check for changes to a React Hooks component after onClick?

I am trying to write a simple integration test in my 100% React Hooks (React v16.12) project with Enzyme (v3.10), Jest (v24.0) and TypeScript where if I click a button component in my App container, another component displaying a counter will go up by one. The current value of the counter is stored in the state of the App container (see snippets below).
Basically, I mount the App component to render its children, then try to simulate a click on the button with Enzyme and check the props of the counter display component to see if its value has gone up. But nothing happens. Not only does the onClick handler not get called but I don't seem to be able to retrieve the value prop I pass to the PaperResource component. So basically I can't test the counter display changes when I click on the button in my Enzyme integration test! The test asserts that the value prop goes from 0 to 1, but this assertion fails without an error per seenter code here. Is this because Enzyme support for Hooks is still not there yet or am I doing something daft here? When I run the app on my browser, everything works as expected.
Here's my integration test
import React from 'react';
import App from './App';
import { mount, ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
describe('App', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
act(() => {
wrapper = mount(<App />);
});
});
describe('when make paper button is clicked', () => {
beforeEach(() => {
act(() => {
wrapper.find('.make-paper__button').simulate('click');
});
});
it('should increase paper resource', () => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
});
});
});
And here is my React code
import React, { useState } from 'react';
import './App.scss';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
const App: React.FC = () => {
const [ resources, setResources ] = useState({
paper: 0,
});
const handleMakePaperButtonClick = () => {
setResources({
...resources,
paper: resources.paper + 1,
});
};
return (
<div className="App">
<MakePaperButton onClick={handleMakePaperButtonClick} />
<div className="resources">
<PaperResource value={resources.paper} />
</div>
</div>
);
}
export default App;
My components are very simple
// PaperResource.tsx
import React from 'react';
export default (props: { value: number }) => (
<div className="resources__paper">
<span>Paper: {props.value}</span>
</div>
);
// MakePaperButton.tsx
import React from 'react';
export default (props: { onClick: () => void }) => (
<div className="make-paper__button">
<button onClick={props.onClick}>Make Paper</button>
</div>
);
The only solution I've found so far is wrapping the expect statement in a setTimeout().
it('should increase paper resource', () => {
setTimeout(() => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
}, 0);
});

Mocking external class method inside React component with jest

I'm trying to test component method, which inside performing network call to external resources. After reading docs I still can't figure out how to do so. Can anyone help? Here is my code(some parts hidden for brevity):
My component:
import React from 'react'
import ResourceService from '../../modules/resource-service'
export default class SliderComponent extends React.Component {
setActiveSlide = (activeSlide) => {
ResourceService.getData({
id: activeSlide,
}).then((data) => {
if (data) {
this.setState({
data,
})
}
})
}
}
Resource service:
import axios from 'axios'
export default class ResourceService {
static getData(params) {
return axios.post('/api/get_my_data', params)
.then((resp) => resp.data)
}
}
Desired test (as I understand it):
import React from 'react'
import { mount, configure } from 'enzyme'
import SliderComponent from '../../../app/components/slider'
test('SliderComponent changes active slide when setActiveSlide is
called', () => {
const wrapper = mount(
<SliderComponent />
);
wrapper.instance().setActiveSlide(1);
// some state checks here
});
I need mock ResourceService.getData call inside SliderComponent, and I really can't understand ho to do it...
You can import your ResourceService in your test and mock the method getData with jest.fn(() => ...). Here is an example:
import React from 'react'
import { mount, configure } from 'enzyme'
import ResourceService from '../../../modules/resource-service'
import SliderComponent from '../../../app/components/slider'
test('SliderComponent changes active slide when setActiveSlide is
called', () => {
// you can set up the return value, you can also resolve/reject the promise
// to test different scnarios
ResourceService.getData = jest.fn(() => (
new Promise((resolve, reject) => { resolve({ data: "testData" }); }));
const wrapper = mount(<SliderComponent />);
wrapper.instance().setActiveSlide(1);
// you can for example check if you service has been called
expect(ResourceService.getData).toHaveBeenCalled();
// some state checks here
});
try using axios-mock-adapter to mock the postreq in your test.
It should look something like this (may need a few more tweaks):
import React from 'react'
import { mount, configure } from 'enzyme'
import SliderComponent from '../../../app/components/slider'
import axios from'axios';
import MockAdapter = from'axios-mock-adapter';
test('SliderComponent changes active slide when setActiveSlide is
called', () => {
let mock = new MockAdapter(axios)
//you can define the response you like
//but your params need to be accordingly to when the post req gets called
mock.onPost('/api/get_my_data', params).reply(200, response)
const wrapper = mount(
<SliderComponent />
);
wrapper.instance().setActiveSlide(1);
// some state checks here
});
make sure to check the docs of axios-mock-adapter

Resources