From 53132fa900ec9d14da1b39fb02373f0efb56ff10 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya <alejandro@alejandrocelaya.com> Date: Thu, 1 Nov 2018 13:15:09 +0100 Subject: [PATCH] Created CreateShortUrl test --- src/short-urls/CreateShortUrl.js | 68 ++++++++++++++------------ src/short-urls/helpers/PreviewModal.js | 28 +++++------ src/short-urls/helpers/QrCodeModal.js | 26 +++++----- test/short-urls/CreateShortUrl.test.js | 66 +++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 60 deletions(-) create mode 100644 test/short-urls/CreateShortUrl.test.js diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 64330ba6..5e0163f7 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -5,12 +5,22 @@ import { assoc, dissoc, isNil, pick, pipe, replace, trim } from 'ramda'; import React from 'react'; import { connect } from 'react-redux'; import { Collapse } from 'reactstrap'; +import * as PropTypes from 'prop-types'; import DateInput from '../utils/DateInput'; import TagsSelector from '../tags/helpers/TagsSelector'; import CreateShortUrlResult from './helpers/CreateShortUrlResult'; -import { createShortUrl, resetCreateShortUrl } from './reducers/shortUrlCreation'; +import { createShortUrl, createShortUrlResultType, resetCreateShortUrl } from './reducers/shortUrlCreation'; + +const normalizeTag = pipe(trim, replace(/ /g, '-')); +const formatDate = (date) => isNil(date) ? date : date.format(); export class CreateShortUrlComponent extends React.Component { + static propTypes = { + createShortUrl: PropTypes.func, + shortUrlCreationResult: createShortUrlResultType, + resetCreateShortUrl: PropTypes.func, + }; + state = { longUrl: '', tags: [], @@ -24,27 +34,31 @@ export class CreateShortUrlComponent extends React.Component { render() { const { createShortUrl, shortUrlCreationResult, resetCreateShortUrl } = this.props; - const changeTags = (tags) => this.setState({ tags: tags.map(pipe(trim, replace(/ /g, '-'))) }); + const changeTags = (tags) => this.setState({ tags: tags.map(normalizeTag) }); const renderOptionalInput = (id, placeholder, type = 'text', props = {}) => ( - <input - className="form-control" - type={type} - placeholder={placeholder} - value={this.state[id]} - onChange={(e) => this.setState({ [id]: e.target.value })} - {...props} - /> + <div className="form-group"> + <input + className="form-control" + id={id} + type={type} + placeholder={placeholder} + value={this.state[id]} + onChange={(e) => this.setState({ [id]: e.target.value })} + {...props} + /> + </div> ); - const createDateInput = (id, placeholder, props = {}) => ( - <DateInput - selected={this.state[id]} - placeholderText={placeholder} - isClearable - onChange={(date) => this.setState({ [id]: date })} - {...props} - /> + const renderDateInput = (id, placeholder, props = {}) => ( + <div className="form-group"> + <DateInput + selected={this.state[id]} + placeholderText={placeholder} + isClearable + onChange={(date) => this.setState({ [id]: date })} + {...props} + /> + </div> ); - const formatDate = (date) => isNil(date) ? date : date.format(); const save = (e) => { e.preventDefault(); createShortUrl(pipe( @@ -75,20 +89,12 @@ export class CreateShortUrlComponent extends React.Component { <div className="row"> <div className="col-sm-6"> - <div className="form-group"> - {renderOptionalInput('customSlug', 'Custom slug')} - </div> - <div className="form-group"> - {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} - </div> + {renderOptionalInput('customSlug', 'Custom slug')} + {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} </div> <div className="col-sm-6"> - <div className="form-group"> - {createDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })} - </div> - <div className="form-group"> - {createDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })} - </div> + {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })} + {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })} </div> </div> </Collapse> diff --git a/src/short-urls/helpers/PreviewModal.js b/src/short-urls/helpers/PreviewModal.js index 13b1ef61..8496be7a 100644 --- a/src/short-urls/helpers/PreviewModal.js +++ b/src/short-urls/helpers/PreviewModal.js @@ -10,21 +10,19 @@ const propTypes = { isOpen: PropTypes.bool, }; -const PreviewModal = ({ url, toggle, isOpen }) => { - return ( - <Modal isOpen={isOpen} toggle={toggle} size="lg"> - <ModalHeader toggle={toggle}> - Preview for <ExternalLink href={url}>{url}</ExternalLink> - </ModalHeader> - <ModalBody> - <div className="text-center"> - <p className="preview-modal__loader">Loading...</p> - <img src={`${url}/preview`} className="preview-modal__img" alt="Preview" /> - </div> - </ModalBody> - </Modal> - ); -}; +const PreviewModal = ({ url, toggle, isOpen }) => ( + <Modal isOpen={isOpen} toggle={toggle} size="lg"> + <ModalHeader toggle={toggle}> + Preview for <ExternalLink href={url}>{url}</ExternalLink> + </ModalHeader> + <ModalBody> + <div className="text-center"> + <p className="preview-modal__loader">Loading...</p> + <img src={`${url}/preview`} className="preview-modal__img" alt="Preview" /> + </div> + </ModalBody> + </Modal> +); PreviewModal.propTypes = propTypes; diff --git a/src/short-urls/helpers/QrCodeModal.js b/src/short-urls/helpers/QrCodeModal.js index d98696ef..3a8cd43f 100644 --- a/src/short-urls/helpers/QrCodeModal.js +++ b/src/short-urls/helpers/QrCodeModal.js @@ -10,20 +10,18 @@ const propTypes = { isOpen: PropTypes.bool, }; -const QrCodeModal = ({ url, toggle, isOpen }) => { - return ( - <Modal isOpen={isOpen} toggle={toggle} centered> - <ModalHeader toggle={toggle}> - QR code for <ExternalLink href={url}>{url}</ExternalLink> - </ModalHeader> - <ModalBody> - <div className="text-center"> - <img src={`${url}/qr-code`} className="qr-code-modal__img" alt="QR code" /> - </div> - </ModalBody> - </Modal> - ); -}; +const QrCodeModal = ({ url, toggle, isOpen }) => ( + <Modal isOpen={isOpen} toggle={toggle} centered> + <ModalHeader toggle={toggle}> + QR code for <ExternalLink href={url}>{url}</ExternalLink> + </ModalHeader> + <ModalBody> + <div className="text-center"> + <img src={`${url}/qr-code`} className="qr-code-modal__img" alt="QR code" /> + </div> + </ModalBody> + </Modal> +); QrCodeModal.propTypes = propTypes; diff --git a/test/short-urls/CreateShortUrl.test.js b/test/short-urls/CreateShortUrl.test.js new file mode 100644 index 00000000..041fb1c3 --- /dev/null +++ b/test/short-urls/CreateShortUrl.test.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import moment from 'moment'; +import * as sinon from 'sinon'; +import { identity } from 'ramda'; +import { CreateShortUrlComponent as CreateShortUrl } from '../../src/short-urls/CreateShortUrl'; +import TagsSelector from '../../src/tags/helpers/TagsSelector'; +import DateInput from '../../src/utils/DateInput'; + +describe('<CreateShortUrl />', () => { + let wrapper; + const shortUrlCreationResult = { + loading: false, + }; + const createShortUrl = sinon.spy(); + + beforeEach(() => { + wrapper = shallow( + <CreateShortUrl shortUrlCreationResult={shortUrlCreationResult} createShortUrl={createShortUrl} /> + ); + }); + afterEach(() => { + wrapper.unmount(); + createShortUrl.resetHistory(); + }); + + it('saves short URL with data set in form controls', (done) => { + const validSince = moment('2017-01-01'); + const validUntil = moment('2017-01-06'); + + const urlInput = wrapper.find('.form-control-lg'); + const tagsInput = wrapper.find(TagsSelector); + const customSlugInput = wrapper.find('#customSlug'); + const maxVisitsInput = wrapper.find('#maxVisits'); + const dateInputs = wrapper.find(DateInput); + const validSinceInput = dateInputs.at(0); + const validUntilInput = dateInputs.at(1); + + urlInput.simulate('change', { target: { value: 'https://long-domain.com/foo/bar' } }); + tagsInput.simulate('change', [ 'tag_foo', 'tag_bar' ]); + customSlugInput.simulate('change', { target: { value: 'my-slug' } }); + maxVisitsInput.simulate('change', { target: { value: '20' } }); + validSinceInput.simulate('change', validSince); + validUntilInput.simulate('change', validUntil); + + setImmediate(() => { + const form = wrapper.find('form'); + + form.simulate('submit', { preventDefault: identity }); + expect(createShortUrl.callCount).toEqual(1); + expect(createShortUrl.getCall(0).args).toEqual( + [ + { + longUrl: 'https://long-domain.com/foo/bar', + tags: [ 'tag_foo', 'tag_bar' ], + customSlug: 'my-slug', + validSince: validSince.format(), + validUntil: validUntil.format(), + maxVisits: '20', + }, + ] + ); + done(); + }); + }); +});