How to test `dangerouslySetInnerHTML` attribute with Testing Library and Jest? - unit-testing

I have a component called Previewer :
export const createMarkup = (htmlStr: string) => {
return { __html: marked(htmlStr, markedConfig) };
};
export const Previewer = ({ content }: Props) => {
return (
<div
id='preview'
dangerouslySetInnerHTML={createMarkup(content)}
role='textbox'
></div>
);
};
I want to test if that div has an attribute called dangerouslySetInnerHTML and display the provided text:
test("should return a div with an id = preview", () => {
const defaultContent = "# Welcome to React Markdown";
render(<Previewer content={defaultContent} />);
const element = screen.getByRole("textbox");
expect(element).toHaveAttribute("id", "preview"); // passed
expect(element).toHaveAttribute("dangerouslySetInnerHTML", createMarkup(defaultContent)); // failed
});

Related

Relay fragment not returning mocked data in test

I'm trying to unit test this component (which uses a Relay JS fragment):
const Row = (me) => {
const {name, email} = useFragment(RowFragment, me)
return ...
}
const RowFragment = graphql`
fragment RowFragment_me on User {
email
name
}
`
To test it, I've wrapped it in a QueryRenderer with a mocked environment - similarly to how the docs suggest. Here is the contents on my test:
it('should mock response and render component', () => {
const environment = createMockEnvironment()
const TestComponent = props => {
return (
<Row me={props.viewer.me} />
)
}
const renderer = render(
<RelayEnvironmentProvider environment={environment}>
<QueryRenderer
environment={environment}
query={TestQuery}
render={({ props }) => {
if (props) return <TestComponent {...props} />
return <div>Loading...</div>
}}
variables={variables}
/>
</RelayEnvironmentProvider>
)
act(() => {
environment.mock.resolveMostRecentOperation(operation =>
MockPayloadGenerator.generate(operation)
)
})
}
The line const {name, email} = useFragment(RowFragment, me) now fails because the response from useFragment is undefined. I can see that me is correctly passed into the component.
Am I doing something wrong?

AntDesign validation Typography editable text

I'm trying to validate when the user is typing in the Antd Design Text, so that it does not let him write other value than numbers and onchange does not return anything.
"antd": "^4.3.3",
"react": "^16.13.1"
import React from "react";
import { Typography } from "antd";
const { Text } = Typography;
export default class Demo extends React.Component {
state = {
price: 0,
};
onChange = (price) => {
let isValid= validatePriceRegex(price);
console.log(isValid, "Price:", price);
this.setState({ price});
};
handleChange = (e) => {
// In this part, I want to validate the input when the user is typing the value
console.log(e); // dont return anything
};
render() {
return (
<Text
onChange={this.handleChange}
editable={{ onChange: this.onChange }}
>
{this.state.price.toString()}
</Text>
);
}
}
const validatePriceRegex = (price) => {
const regex = /^\$?\d+(,\d{3})*(\.\d*)?$/;
return price && regex.test(price);
};
you seems to have 2 handle on change function. the handleChange and the handleChange
editable Text component only accepts onChange inside of the editable props, so you have to do in the same on change function
import React from "react";
import { Typography } from "antd";
const { Text } = Typography;
export default class Demo extends React.Component {
state = {
price: 0
};
onChange = price => {
let isValid = validatePriceRegex(price);
console.log(isValid, "Price:", price);
// you validate here
if (isValid) {
this.setState({ price });
}
};
// handleChange = e => {
// // In this part, I want to validate the input when the user is typing the value
// console.log(e); // dont return anything
// };
render() {
return (
<Text editable={{ onChange: this.onChange }}>
{this.state.price.toString()}
</Text>
);
}
}
const validatePriceRegex = price => {
const regex = /^\$?\d+(,\d{3})*(\.\d*)?$/;
return price && regex.test(price);
};

How to pass props to test component?

I am learning about unit-testing in Vue and try to test component according to this article https://alligator.io/vuejs/testing-vue-with-jest/
I have a component
<template>
<div>
<h1 :style="headingStyles">{{title}}</h1>
</div>
</template>
<script>
export default {
data() {
return {
headingStyles: {
color: this.color
}
};
},
props: ["title", "color"]
};
</script>
and file with test cases
import Vue from 'vue';
import FancyHeading from '../../src/components/FancyHeading';
function mountComponentWithProps (Component, propsData) {
console.log('props Data', propsData)
const Constructor = Vue.extend(Component);
const vm = new Constructor({
propsData
}).$mount();
return vm.$el;
}
describe('FancyHeading.vue', () => {
it('should be the correct color', () => {
const headingData = mountComponentWithProps(FancyHeading, { color: 'blue' });
const styleData = headingData.style.getPropertyValue('color');
console.log(styleData)
expect(styleData).toEqual('blue');
});
it('should have the correct title', () => {
const headingData = mountComponentWithProps(FancyHeading, { title: 'Hello, Vue!' });
const titleData = headingData.textContent;
expect(titleData).toEqual('Hello, Vue!');
});
});
When I run yarn test:unit I receive error
FancyHeading.vue › should be the correct color
expect(received).toEqual(expected) // deep equality
Expected: "blue"
Received: ""
Looks like color is empty xtring, but I don't understand why. Can someone explain me and help to pass test?

How to stop redux-form or React from changing htmlFor and id when creating Jest snapshots?

I've got a wizard form made with redux-forms v6 and it looks something like:
--
index.js - Holds page number in local state, is connected to application level state
PageOne - wrapped with reduxForm decorator (form: 'wizForm')
PageTwo - wrapped with reduxForm decorator (form: 'wizForm')
--
PageOne and PageTwo both contain additional components that render sections of the form (initial fields, vehicle information, driver information...), and each of those sections render their own components for each question in that section.
Since there's a lot of nested components and I want to test that PageOne and PageTwo call the props passed from index.js, I've resorted to using Enzyme's mount() function with a fake store. I want to MatchSnapshot() with Jest to compare whether index.js is rendering PageOne or PageTwo, after certain buttons are clicked to go back and forth from pages.
The problem is when I do create snapshots, other than creating a 16,000 line snapshot, the snapshot will NEVER match the previous one even if I don't change anything. I'm not sure if it's redux-form that's doing it or React, but the htmlFor and the id keep changing between snapshots, test after test after test.
We use css-modules too, but I don't think that's causing the problem, and we did configure Jest to work with css-modules too, modifying "moduleNameWrapper" to mock .css files. Does anyone know how to fix this or where I should look?
tests:
describe('<VehicleAddition />', () => {
let props;
beforeEach(() => {
props = {
...,
};
});
it('Renders initially', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
});
it('Renders <PageTwo> when <PageOne> form is submitted', () => {
const component = shallow(<VehicleAddition {...props} />);
expect(toJson(component)).toMatchSnapshot();
component.find('ReduxForm') // reduxForm HOC wraps the <form> in a <ReduxForm> component
.first()
.simulate('submit');
expect(toJson(component)).toMatchSnapshot();
expect(component.state().page).toEqual(2);
});
it('PageTwoStuffs', () => {
// Render the form, click 'next', assert it's page two
// click 'previous'
jest.enableAutomock();
const store = createStore(
combineReducers({
route: jest.fn(() => Immutable.fromJS({})),
language: jest.fn(() => Immutable.fromJS({})),
global: jest.fn(() => Immutable.fromJS({})),
form: formReducer,
}),
Immutable.fromJS({}),
);
const component = mount(
<Provider store={store}>
<VehicleAddition {...props} />
</Provider>
);
// CAN'T check the state of <VehicleAddition /> because it can only be done on root component, says the error message.
expect(toJson(component)).toMatchSnapshot();
index.js:
export class VehicleAddition extends React.Component { // eslint-disable-line
constructor(props) {
super(props);
this.state = {
page: 1,
};
}
nextPage = () => {
this.setState({ page: this.state.page + 1 });
}
previousPage = () => {
this.setState({ page: this.state.page - 1 });
}
render() {
return (
<div>
{page === 1 &&
<PageOne
{...this.props}
/>
}
{page === 2 &&
<PageTwo
{...this.props}
/>
}
</div>
);
}
}
PageOne.js
class PageOne extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<InitialFields
autoPolicies={autoPolicies}
changeField={this.changeField}
getFormValues={getFormValues}
policies={policies}
primary={primary}
/>
<VehicleBeingAddedFields
changeField={this.changeField}
getFormValues={getFormValues}
fetchVehMakes={fetchVehMakes}
fetchVehModels={fetchVehModels}
policies={policies}
vehMakes={vehMakes}
vehModels={vehModels}
/>
...
<div className="btn-group btn-group-float-right">
<button
type="submit"
onClick={this.handleClick}
disabled={pristine || submitting}
className="btn-primary"
>
Next
</button>
</div>
</form>
);
}
}
PageTwo.js:
class PageTwo extends React.Component { // eslint-disable-line
render() {
const {
...
} = this.props;
return (
<form onSubmit={handleSubmit}>
...
<div className="btn-group btn-group-float-right">
<button type="button" className="btn" onClick={previousPage}>Previous</button>{' '}
<button type="submit" disabled={pristine || submitting} className="btn-primary">Submit</button>
</div>
</form>
);
}
}
Example of the parts of the snapshot that constantly changes:
I solved it by passing a hardcoded id value from the test cases
import React from 'react';
import renderer from 'react-test-renderer';
import { reduxForm } from 'redux-form';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { mount } from 'enzyme'
import TodoItem from './TodoItem';
import injectTapEventPlugin from 'react-tap-event-plugin';
function setup() {
const spy = jest.fn();
const store = createStore(() => ({}));
const Decorated = reduxForm({ form: 'testForm' })(TodoItem);
const props = {
remove: jest.fn(),
TodoItemReduxFormInitialName: "fullName",
snapshotTestId:"4"
}
const mockedComponent = <Provider store={store}>
<Decorated {...props} />
</Provider>;
const enzymeWrapper = mount(mockedComponent)
injectTapEventPlugin();
return {
props,
mockedComponent,
enzymeWrapper
}
}
describe('TodoItem Component', () => {
it('should render the snapshot', () => {
const {mockedComponent} = setup()
const tree = renderer.create(
mockedComponent
).toJSON();
expect(tree).toMatchSnapshot();
});
//not required as snapshot testing covers it
it('should render Number', () => {
const {enzymeWrapper} = setup()
const fieldProps = enzymeWrapper.find('Field').at(0).props();
expect(fieldProps.hintText).toEqual('Item Number');
expect(fieldProps.name).toEqual('fullName.itemNumber');
});
//not required as snapshot testing covers it
it('should render remove button', () => {
const {enzymeWrapper} = setup()
const button = enzymeWrapper.find('RaisedButton').at(0).props();
expect(button.label).toEqual("remove")
});
});

Enzyme: How to test onSubmit function passed as prop?

I am fairly new with enzyme. I have two components under test.
form.jsx
const LoginForm = ({ style, handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<Button type='submit'>
Login
</Button>
</form>
);
};
LoginForm.propTypes = {
handleSubmit: PropTypes.func.isRequired
};
I am using this component in another component as follows:
Component.jsx
export default class Login extends Component {
constructor(props) {
super(props);
this.onLogin = this.onLogin.bind(this);
}
onLogin(event) {
event.preventDefault();
this.props.loginUser();
}
render() {
return (
<LoginForm style={loginFormStyles} handleSubmit={this.onLogin} />
);
}
}
Login.propTypes = {
auth: PropTypes.object.isRequired, //mapStateToProps
loginUser: PropTypes.func.isRequired //mapDispatchToProps
};
I have written tests for form and they are passing.
form-test.js
it('should have a onSubmit handler', () => {
const props = {
handleSubmit: () => {}
};
const wrapper = shallow(<LoginForm {...props} />);
expect(_.isFunction(wrapper.props().onSubmit)).to.be.true;
});
it('should should call handlesubmit on form submission', () => {
const handleSubmit = sinon.spy();
const wrapper = shallow(<LoginForm handleSubmit={handleSubmit} />);
wrapper.simulate('submit');
expect(handleSubmit).to.have.been.called;
});
These tests are passing. The confusing part is:
1- How do I test onLogin function in Component.jsx from form.jsx?
2- Vice versa, if I have to trigger onSubmit of form.jsx from component.jsx how would I do that?
First of all, you can rename the Component.jsx to something else.
And for the test you can do something as below,
import Component from '../src/login';
import { stub } from 'sinon';
describe('login', () => {
it('should call onsubmit', () => {
const onSubmit = stub()
.withArgs('username', 'password');
const loginComponent = mount(<LoginForm handleSubmit={onSubmit} /> );
loginComponent.simulate('submit');
expect(onSubmit.calledOnce).to.equal(true);
});
});
I have not tested this but it is close to what you are looking at.
Update:
I tested this and it is working.