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 = {}) => (
- this.setState({ [id]: e.target.value })}
- {...props}
- />
+
+ this.setState({ [id]: e.target.value })}
+ {...props}
+ />
+
);
- const createDateInput = (id, placeholder, props = {}) => (
- this.setState({ [id]: date })}
- {...props}
- />
+ const renderDateInput = (id, placeholder, props = {}) => (
+
+ this.setState({ [id]: date })}
+ {...props}
+ />
+
);
- 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 {
-
- {renderOptionalInput('customSlug', 'Custom slug')}
-
-
- {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
-
+ {renderOptionalInput('customSlug', 'Custom slug')}
+ {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
-
- {createDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })}
-
-
- {createDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })}
-
+ {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })}
+ {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })}
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 (
-
-
- Preview for {url}
-
-
-
-
Loading...
-

-
-
-
- );
-};
+const PreviewModal = ({ url, toggle, isOpen }) => (
+
+
+ Preview for {url}
+
+
+
+
Loading...
+

+
+
+
+);
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 (
-
-
- QR code for {url}
-
-
-
-

-
-
-
- );
-};
+const QrCodeModal = ({ url, toggle, isOpen }) => (
+
+
+ QR code for {url}
+
+
+
+

+
+
+
+);
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('', () => {
+ let wrapper;
+ const shortUrlCreationResult = {
+ loading: false,
+ };
+ const createShortUrl = sinon.spy();
+
+ beforeEach(() => {
+ wrapper = shallow(
+
+ );
+ });
+ 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();
+ });
+ });
+});