Skip to content

Instantly share code, notes, and snippets.

@stipsan
Last active August 7, 2018 22:39
Show Gist options
  • Save stipsan/94829a02ad35787b386d54f63bd352fa to your computer and use it in GitHub Desktop.
Save stipsan/94829a02ad35787b386d54f63bd352fa to your computer and use it in GitHub Desktop.
Testing with Jest: Tip #15
import renderer from 'react-test-renderer'
import Field from '../Field'
// this import is created by our mock, it is inteded to help with testing and
// don't exist in the real package
import { intl } from 'react-intl'
it('should render correctly', () => {
const component = renderer.create(<Field intl={intl} />)
expect(component.toJSON()).toMatchSnapshot()
})
import { Component } from 'react'
import {
FormattedMessage,
intlShape,
injectIntl,
defineMessages
} from 'react-intl'
const messages = defineMessages({
label: {
id: 'email.label',
defaultMessage: 'Email'
},
placeholder: {
id: 'email.placeholder',
defaultMessage: 'Please enter your email address'
}
})
class Field extends Component {
static propTypes = {
intl: intlShape.isRequired
}
render() {
return (
<div>
<label>
<FormattedMessage {...messages.label} />
<input
type="email"
placeholder={this.props.intl.formatMessage(messages.placeholder)}
/>
</label>
</div>
)
}
}
// injectIntl is only necessary for adding the `intl` prop to render the pure string in the placeholder attribute
// In React v16 with Fiber comes the ability for components like FormattedMessage to return a pure
// string, instead of always wrapping in a dom element like <span> so
// hopefully the imperative `formatMessage` API can be dropped soon
export default injectIntl(Field)
// place this file here: <rootDir>/__mocks__/react-intl.js
// this makes it apply globally as needed
const Intl = require.requireActual('react-intl')
// Initialise the real provider so that we don't
// need to reimplement any internals
const defaultProps = {
locale: 'en',
defaultLocale: 'en'
}
const intlProvider = new Intl.IntlProvider(defaultProps, {})
// The exact same `intl` object the real code receives ;-)
const { intl } = intlProvider.getChildContext()
Intl.injectIntl = Node => props => <Node {...props} intl={intl} />
// Override components by implementing the real components
// providing them the context they need in order to function
const {
IntlProvider,
FormattedDate,
FormattedTime,
FormattedRelative,
FormattedNumber,
FormattedPlural,
FormattedMessage,
FormattedHTMLMessage
} = Intl
Intl.FormattedDate = props =>
<IntlProvider {...defaultProps}><FormattedDate {...props} /></IntlProvider>
Intl.FormattedTime = props =>
<IntlProvider {...defaultProps}><FormattedTime {...props} /></IntlProvider>
Intl.FormattedRelative = props =>
<IntlProvider {...defaultProps}>
<FormattedRelative {...props} />
</IntlProvider>
Intl.FormattedNumber = props =>
<IntlProvider {...defaultProps}><FormattedNumber {...props} /></IntlProvider>
Intl.FormattedPlural = props =>
<IntlProvider {...defaultProps}><FormattedPlural {...props} /></IntlProvider>
Intl.FormattedMessage = props =>
<IntlProvider {...defaultProps}><FormattedMessage {...props} /></IntlProvider>
Intl.FormattedHTMLMessage = props =>
<IntlProvider {...defaultProps}>
<FormattedHTMLMessage {...props} />
</IntlProvider>
// Set displayName so that snapshots don't use "Uknown" as component name
Intl.FormattedDate.displayName = 'FormattedDate'
Intl.FormattedTime.displayName = 'FormattedTime'
Intl.FormattedRelative.displayName = 'FormattedRelative'
Intl.FormattedNumber.displayName = 'FormattedNumber'
Intl.FormattedPlural.displayName = 'FormattedPlural'
Intl.FormattedMessage.displayName = 'FormattedMessage'
Intl.FormattedHTMLMessage.displayName = 'FormattedHTMLMessage'
// Special hook for tests, real package does not export this
Intl.intl = intl
module.exports = Intl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment