diff --git a/.gitignore b/.gitignore index c1bacf15f7ed9b62e30e55a888ee67cf51c38df3..0b6bedb9e4e1eb47fa4a80e094a7686b1c1e99fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .env .env.* dist/ +node_modules +coverage/ diff --git a/packages/xpub-ui/package.json b/packages/xpub-ui/package.json index 5e080451349a470c3f26037745e282203dc03c5d..b48d1d65094a1d6c1541cbfb66778eabb03d4038 100644 --- a/packages/xpub-ui/package.json +++ b/packages/xpub-ui/package.json @@ -21,18 +21,30 @@ "devDependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", + "babel-jest": "^20.0.3", "babel-preset-env": "^1.6.0", "babel-preset-minify": "^0.2.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-2": "^6.24.1", "css-loader": "^0.28.4", + "enzyme": "^2.9.1", "faker": "^4.1.0", "file-loader": "^0.11.2", - "xpub-styleguide": "^0.0.2", + "jest": "^20.0.4", "react-styleguidist": "^6.0.8", + "react-test-renderer": "^15.6.1", "style-loader": "^0.18.2", "webpack": "^3.5.5", - "webpack-node-externals": "^1.6.0" + "webpack-node-externals": "^1.6.0", + "xpub-styleguide": "^0.0.2" + }, + "jest": { + "moduleNameMapper": { + "\\.css$": "<rootDir>/test/setup/styleMock.js" + }, + "transform": { + "\\.js$": "<rootDir>/test/config/transform.js" + } }, "scripts": { "styleguide": "styleguidist server", @@ -40,6 +52,10 @@ "clean": "rimraf dist", "lint": "eslint src", "prebuild": "npm run clean && npm run lint", - "build": "webpack --progress --profile" + "build": "webpack --progress --profile", + "test": "NODE_ENV=test jest", + "test:watch": "NODE_ENV=test jest --watch", + "test:cover": "NODE_ENV=test jest --coverage", + "test:u": "NODE_ENV=test jest --updateSnapshot" } } diff --git a/packages/xpub-ui/test/AppBar.test.js b/packages/xpub-ui/test/AppBar.test.js new file mode 100644 index 0000000000000000000000000000000000000000..b0cc4289d9931953455d9f228f0170d244ef84b6 --- /dev/null +++ b/packages/xpub-ui/test/AppBar.test.js @@ -0,0 +1,113 @@ +import React from 'react' +import { clone } from 'lodash' +import { shallow } from 'enzyme' +import { Link } from 'react-router' +import renderer from 'react-test-renderer' + +import AppBar from '../src/molecules/AppBar' + +const props = { + brandLink: 'some link', + brandName: 'some brand', + loginLink: 'login link', + logoutLink: 'logout link', + userName: 'some name', +} + +const wrapper = shallow(<AppBar {...props} />) + +describe('AppBar', () => { + test('Snapshot', () => { + const tree = renderer.create( + <AppBar {...props} /> + ).toJSON() + expect(tree).toMatchSnapshot() + }) + + test('Should render correctly', () => { + expect(wrapper.is('div')) + expect(wrapper.children()).toHaveLength(2) + + const brand = wrapper.childAt(0) + expect(brand.is(Link)).toBeTruthy() + expect(brand.children()).toHaveLength(1) + + const rightArea = wrapper.childAt(1) + expect(rightArea.is('div')).toBeTruthy() + expect(rightArea.children()).toHaveLength(2) + + const username = rightArea.childAt(0) + expect(username.is('span')).toBeTruthy() + + const logLink = rightArea.childAt(1) + expect(logLink.is(Link)).toBeTruthy() + }) + + test('Should link the brand to \'/\' if no brand link is given', () => { + const newProps = clone(props) + newProps.brandLink = undefined + const wrapper = shallow(<AppBar {...newProps} />) + + const brand = wrapper.childAt(0) + expect(brand.prop('to')).toBe('/') + }) + + test('Should link the brand to the given prop', () => { + const brand = wrapper.childAt(0) + expect(brand.prop('to')).toBe(props.brandLink) + }) + + test('Should display the brand name', () => { + const brand = wrapper.childAt(0) + const brandName = brand.childAt(0) + + expect(brandName.text()).toBe(props.brandName) + }) + + test('Should not display the username if there is none given', () => { + const newProps = clone(props) + newProps.userName = undefined + const wrapper = shallow(<AppBar {...newProps} />) + + const rightArea = wrapper.childAt(1) + + // If the username does not display, there is only child (login / logout) + expect(rightArea.children).toHaveLength(1) + }) + + test('Should display the username', () => { + const rightArea = wrapper.childAt(1) + expect(rightArea.children()).toHaveLength(2) + + const userName = rightArea.childAt(0) + expect(userName.text()).toBe(props.userName) + }) + + test('Should display the login link if no username is given', () => { + const newProps = clone(props) + newProps.userName = undefined + const wrapper = shallow(<AppBar {...newProps} />) + + const rightArea = wrapper.childAt(1) + const logLink = rightArea.childAt(0) // first el if there is no username + + expect(logLink.is(Link)).toBeTruthy() + expect(logLink.prop('to')).toBe(props.loginLink) + expect(logLink.children()).toHaveLength(1) + + const logLinkText = logLink.childAt(0) + expect(logLinkText.text()).toBe('login') + }) + + test('Should display the logout link if a username is found', () => { + const rightArea = wrapper.childAt(1) + const logLink = rightArea.childAt(1) // 2nd el if there is a username + + expect(logLink.is(Link)).toBeTruthy() + expect(logLink.prop('to')).toBe(props.logoutLink) + expect(logLink.children()).toHaveLength(1) + + const logLinkText = logLink.childAt(0) + expect(logLinkText.text()).toBe('logout') + }) +}) diff --git a/packages/xpub-ui/test/Radio.test.js b/packages/xpub-ui/test/Radio.test.js new file mode 100644 index 0000000000000000000000000000000000000000..ef32d9dea18ed2822141b9b761962f5542c37ac7 --- /dev/null +++ b/packages/xpub-ui/test/Radio.test.js @@ -0,0 +1,58 @@ +import React from 'react' +import { shallow } from 'enzyme' +import renderer from 'react-test-renderer' + +import Radio from '../src/atoms/Radio' + +const props = { + checked: false, + handleChange: jest.fn(), + label: 'TestLabel', + name: 'TestName', + required: true, + value: 'TestValue' +} + +const wrapper = shallow(<Radio {...props} />) + +describe('Radio', () => { + test('Snapshot', () => { + const tree = renderer.create( + <Radio {...props} /> + ).toJSON() + expect(tree).toMatchSnapshot() + }) + + test('Renders correctly', () => { + expect(wrapper.is('label')).toBeTruthy() + expect(wrapper.children()).toHaveLength(2) + + const input = wrapper.childAt(0) + expect(input.is('input')).toBeTruthy() + expect(input.children()).toHaveLength(0) + + const labelText = wrapper.childAt(1) + expect(labelText.text()).toBe(props.label) + expect(labelText.children()).toHaveLength(0) + }) + + test('Input gets the correct props', () => { + const input = wrapper.find('input') + + expect(input.prop('name')).toBe(props.name) + expect(input.prop('value')).toBe(props.value) + expect(input.prop('checked')).toBe(props.checked) + expect(input.prop('required')).toBe(props.required) + }) + + test('Change handler should be called on change', () => { + const input = wrapper.find('input') + + expect(props.handleChange).not.toHaveBeenCalled() + + const event = { target: {} } + input.simulate('change', event) + + expect(props.handleChange).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/xpub-ui/test/RadioGroup.test.js b/packages/xpub-ui/test/RadioGroup.test.js new file mode 100644 index 0000000000000000000000000000000000000000..d2964e6d02079ad2c378ba645c745bb687e0040b --- /dev/null +++ b/packages/xpub-ui/test/RadioGroup.test.js @@ -0,0 +1,116 @@ +import React from 'react' +import { clone } from 'lodash' +import { shallow } from 'enzyme' +import renderer from 'react-test-renderer' + +import Radio from '../src/atoms/Radio' +import RadioGroup from '../src/molecules/RadioGroup' + +const handleChange = () => { return null } + +const props = { + handleChange, + name: 'TestName', + options: [ + { + label: 'Yes', + value: 'yes' + }, + { + label: 'No', + value: 'no' + }, + { + label: 'Maybe', + value: 'maybe' + } + ], + required: true, + value: undefined +} + +const wrapper = shallow(<RadioGroup {...props} />) +const radios = wrapper.find(Radio) + +describe('Radio Group', () => { + test('Snapshot', () => { + const tree = renderer.create( + <RadioGroup {...props} /> + ).toJSON() + expect(tree).toMatchSnapshot() + }) + + test('Renders a number of radio buttons', () => { + expect(wrapper.is('div')).toBeTruthy() + + const len = props.options.length + expect(wrapper.children()).toHaveLength(len) + + let i = 0 + + while (i < len) { + const child = wrapper.childAt(i) + expect(child.is(Radio)).toBeTruthy() + + i++ + } + + expect(radios).toHaveLength(len) + }) + + test('Radios get the correct props', () => { + const radioComps = radios.getNodes() + let i = 0 + + while (i < props.options.length) { + const radio = radioComps[i] + const radioProps = radio.props + + expect(radioProps.label).toEqual(props.options[i].label) + expect(radioProps.value).toEqual(props.options[i].value) + expect(radioProps.name).toEqual(props.name) + expect(radioProps.required).toEqual(props.required) + expect(radioProps.handleChange).toEqual(props.handleChange) + + i++ + } + }) + + test('Value should match the checked radio button', () => { + // With no radio button selected + const radioComps = radios.getNodes() + let i = 0 + + while (i < props.options.length) { + const radio = radioComps[i] + const radioProps = radio.props + + expect(radioProps.checked).toBeFalsy() + i++ + } + + // With the first radio button selected + // (re-initialise the wrapper with changed props) + const newProps = clone(props) + newProps.value = 'yes' + + const newWrapper = shallow(<RadioGroup {...newProps} />) + const newRadios = newWrapper.find(Radio) + const newRadioComps = newRadios.getNodes() + + i = 0 + + while (i < props.options.length) { + const radio = newRadioComps[i] + const radioProps = radio.props + + if (i === 0) { + expect(radioProps.checked).toBeTruthy() + } else { + expect(radioProps.checked).toBeFalsy() + } + + i++ + } + }) +}) diff --git a/packages/xpub-ui/test/YesOrNo.test.js b/packages/xpub-ui/test/YesOrNo.test.js new file mode 100644 index 0000000000000000000000000000000000000000..31bab793d6b0329a6abf7bfcc206b557dbde7901 --- /dev/null +++ b/packages/xpub-ui/test/YesOrNo.test.js @@ -0,0 +1,57 @@ +import React from 'react' +import { shallow } from 'enzyme' +import renderer from 'react-test-renderer' + +import YesOrNo from '../src/molecules/YesOrNo' +import RadioGroup from '../src/molecules/RadioGroup' + +const handleChange = () => { return null } + +const props = { + handleChange, + name: 'TestName', + value: 'Maybe' +} + +const wrapper = shallow(<YesOrNo {...props} />) +const radio = wrapper.find(RadioGroup) + +describe('Yes or No', () => { + test('Snapshot', () => { + const tree = renderer.create( + <YesOrNo {...props} /> + ).toJSON() + expect(tree).toMatchSnapshot() + }) + + test('Renders a RadioGroup', () => { + expect(wrapper.is('RadioGroup')).toBeTruthy() + expect(radio).toHaveLength(1) + }) + + test('Passes the correct options', () => { + const options = radio.props().options + expect(options).toHaveLength(2) + + expect(options[0].value).toEqual('yes') + expect(options[0].label).toEqual('Yes') + + expect(options[1].value).toEqual('no') + expect(options[1].label).toEqual('No') + }) + + test('Passes down the correct name', () => { + const name = radio.props().name + expect(name).toEqual(props.name) + }) + + test('Passes down the correct value', () => { + const value = radio.props().value + expect(value).toEqual(props.value) + }) + + test('Passes down the correct handle change function', () => { + const handle = radio.props().handleChange + expect(handle).toEqual(props.handleChange) + }) +}) diff --git a/packages/xpub-ui/test/__snapshots__/AppBar.test.js.snap b/packages/xpub-ui/test/__snapshots__/AppBar.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..3ea9dd76f9ae3d4ecc47f49421e686b39b85b30f --- /dev/null +++ b/packages/xpub-ui/test/__snapshots__/AppBar.test.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AppBar Snapshot 1`] = ` +<div + className={undefined} +> + <a + className={undefined} + onClick={[Function]} + style={Object {}} + > + some brand + </a> + <div> + <span + className={undefined} + > + some name + </span> + <a + className="" + onClick={[Function]} + style={Object {}} + > + logout + </a> + </div> +</div> +`; diff --git a/packages/xpub-ui/test/__snapshots__/Radio.test.js.snap b/packages/xpub-ui/test/__snapshots__/Radio.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..d13bda93e577da1d0a3af5bffce5474cd9bfd0e5 --- /dev/null +++ b/packages/xpub-ui/test/__snapshots__/Radio.test.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Radio Snapshot 1`] = ` +<label + className={undefined} +> + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={true} + type="radio" + value="TestValue" + /> + TestLabel +</label> +`; diff --git a/packages/xpub-ui/test/__snapshots__/RadioGroup.test.js.snap b/packages/xpub-ui/test/__snapshots__/RadioGroup.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..6443d52a4388fabc6d053427129917481929717f --- /dev/null +++ b/packages/xpub-ui/test/__snapshots__/RadioGroup.test.js.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Radio Group Snapshot 1`] = ` +<div> + <label + className={undefined} + > + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={true} + type="radio" + value="yes" + /> + Yes + </label> + <label + className={undefined} + > + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={true} + type="radio" + value="no" + /> + No + </label> + <label + className={undefined} + > + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={true} + type="radio" + value="maybe" + /> + Maybe + </label> +</div> +`; diff --git a/packages/xpub-ui/test/__snapshots__/YesOrNo.test.js.snap b/packages/xpub-ui/test/__snapshots__/YesOrNo.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..bfe7af7e379cda10b1e455c168c3b7fbcd2c312a --- /dev/null +++ b/packages/xpub-ui/test/__snapshots__/YesOrNo.test.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Yes or No Snapshot 1`] = ` +<div> + <label + className={undefined} + > + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={undefined} + type="radio" + value="yes" + /> + Yes + </label> + <label + className={undefined} + > + <input + checked={false} + className={undefined} + name="TestName" + onChange={[Function]} + required={undefined} + type="radio" + value="no" + /> + No + </label> +</div> +`; diff --git a/packages/xpub-ui/test/config/transform.js b/packages/xpub-ui/test/config/transform.js new file mode 100644 index 0000000000000000000000000000000000000000..4fc948d1f24c1712a09c22c86b55d1992533ae4e --- /dev/null +++ b/packages/xpub-ui/test/config/transform.js @@ -0,0 +1,7 @@ +module.exports = require('babel-jest').createTransformer({ + presets: [ + 'env', + 'react', + 'stage-2' + ] +}); diff --git a/packages/xpub-ui/test/setup/styleMock.js b/packages/xpub-ui/test/setup/styleMock.js new file mode 100644 index 0000000000000000000000000000000000000000..7c6d6c73d3d5a72254e74f72e6725d1402c9ddc8 --- /dev/null +++ b/packages/xpub-ui/test/setup/styleMock.js @@ -0,0 +1 @@ +module.exports = {} \ No newline at end of file