mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Merge pull request #237 from acelaya-forks/feature/short-code-length
Feature/short code length
This commit is contained in:
commit
ab2f311bb7
9 changed files with 143 additions and 118 deletions
|
@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
* [#213](https://github.com/shlinkio/shlink-web-client/issues/213) The versions of both shlink-web-client and currently consumed Shlink server are now displayed in the footer.
|
* [#213](https://github.com/shlinkio/shlink-web-client/issues/213) The versions of both shlink-web-client and currently consumed Shlink server are now displayed in the footer.
|
||||||
* [#221](https://github.com/shlinkio/shlink-web-client/issues/221) Improved how servers are handled, displaying meaningful errors when a not-found or a not-reachable server is tried to be loaded.
|
* [#221](https://github.com/shlinkio/shlink-web-client/issues/221) Improved how servers are handled, displaying meaningful errors when a not-found or a not-reachable server is tried to be loaded.
|
||||||
* [#226](https://github.com/shlinkio/shlink-web-client/issues/226) Created servers can now be edited.
|
* [#226](https://github.com/shlinkio/shlink-web-client/issues/226) Created servers can now be edited.
|
||||||
|
* [#234](https://github.com/shlinkio/shlink-web-client/issues/234) Allowed short code length to be edited on any new short RUL when suing Shlink 2.1 or higher.
|
||||||
|
|
||||||
#### Changed
|
#### Changed
|
||||||
|
|
||||||
|
|
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -13894,9 +13894,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"version": "16.10.2",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
|
||||||
"integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==",
|
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
|
@ -14106,14 +14106,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.10.2",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
|
||||||
"integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==",
|
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"scheduler": "^0.16.2"
|
"scheduler": "^0.19.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-error-overlay": {
|
"react-error-overlay": {
|
||||||
|
@ -15279,9 +15279,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
|
||||||
"integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==",
|
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
|
|
|
@ -43,13 +43,13 @@
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"qs": "^6.9.0",
|
"qs": "^6.9.0",
|
||||||
"ramda": "^0.26.1",
|
"ramda": "^0.26.1",
|
||||||
"react": "^16.10.2",
|
"react": "^16.13.1",
|
||||||
"react-autosuggest": "^9.4.3",
|
"react-autosuggest": "^9.4.3",
|
||||||
"react-chartjs-2": "^2.8.0",
|
"react-chartjs-2": "^2.8.0",
|
||||||
"react-color": "^2.17.3",
|
"react-color": "^2.17.3",
|
||||||
"react-copy-to-clipboard": "^5.0.1",
|
"react-copy-to-clipboard": "^5.0.1",
|
||||||
"react-datepicker": "~1.5.0",
|
"react-datepicker": "~1.5.0",
|
||||||
"react-dom": "^16.10.2",
|
"react-dom": "^16.13.1",
|
||||||
"react-external-link": "^1.0.0",
|
"react-external-link": "^1.0.0",
|
||||||
"react-leaflet": "^2.4.0",
|
"react-leaflet": "^2.4.0",
|
||||||
"react-moment": "^0.9.5",
|
"react-moment": "^0.9.5",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { serverType } from '../prop-types';
|
import { serverType } from '../prop-types';
|
||||||
import { compareVersions } from '../../utils/helpers/version';
|
import { versionMatch } from '../../utils/helpers/version';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
minVersion: PropTypes.string,
|
minVersion: PropTypes.string,
|
||||||
|
@ -16,10 +16,9 @@ const ForServerVersion = ({ minVersion, maxVersion, selectedServer, children })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { version } = selectedServer;
|
const { version } = selectedServer;
|
||||||
const matchesMinVersion = !minVersion || compareVersions(version, '>=', minVersion);
|
const matchesVersion = versionMatch(version, { maxVersion, minVersion });
|
||||||
const matchesMaxVersion = !maxVersion || compareVersions(version, '<=', maxVersion);
|
|
||||||
|
|
||||||
if (!matchesMinVersion || !matchesMaxVersion) {
|
if (!matchesVersion) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,52 @@
|
||||||
import { faAngleDoubleDown as downIcon, faAngleDoubleUp as upIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faAngleDoubleDown as downIcon, faAngleDoubleUp as upIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { assoc, dissoc, isEmpty, isNil, pipe, replace, trim } from 'ramda';
|
import { isEmpty, isNil, pipe, replace, trim } from 'ramda';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Collapse, FormGroup, Input } from 'reactstrap';
|
import { Collapse, FormGroup, Input } from 'reactstrap';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import DateInput from '../utils/DateInput';
|
import DateInput from '../utils/DateInput';
|
||||||
import Checkbox from '../utils/Checkbox';
|
import Checkbox from '../utils/Checkbox';
|
||||||
import { serverType } from '../servers/prop-types';
|
import { serverType } from '../servers/prop-types';
|
||||||
import { compareVersions } from '../utils/helpers/version';
|
import { versionMatch } from '../utils/helpers/version';
|
||||||
|
import { hasValue } from '../utils/utils';
|
||||||
|
import { useToggle } from '../utils/helpers/hooks';
|
||||||
import { createShortUrlResultType } from './reducers/shortUrlCreation';
|
import { createShortUrlResultType } from './reducers/shortUrlCreation';
|
||||||
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
||||||
|
|
||||||
const normalizeTag = pipe(trim, replace(/ /g, '-'));
|
const normalizeTag = pipe(trim, replace(/ /g, '-'));
|
||||||
const formatDate = (date) => isNil(date) ? date : date.format();
|
const formatDate = (date) => isNil(date) ? date : date.format();
|
||||||
|
|
||||||
const CreateShortUrl = (
|
const propTypes = {
|
||||||
TagsSelector,
|
createShortUrl: PropTypes.func,
|
||||||
CreateShortUrlResult,
|
shortUrlCreationResult: createShortUrlResultType,
|
||||||
ForServerVersion
|
resetCreateShortUrl: PropTypes.func,
|
||||||
) => class CreateShortUrl extends React.Component {
|
selectedServer: serverType,
|
||||||
static propTypes = {
|
};
|
||||||
createShortUrl: PropTypes.func,
|
|
||||||
shortUrlCreationResult: createShortUrlResultType,
|
|
||||||
resetCreateShortUrl: PropTypes.func,
|
|
||||||
selectedServer: serverType,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
const CreateShortUrl = (TagsSelector, CreateShortUrlResult, ForServerVersion) => {
|
||||||
longUrl: '',
|
const CreateShortUrlComp = ({ createShortUrl, shortUrlCreationResult, resetCreateShortUrl, selectedServer }) => {
|
||||||
tags: [],
|
const [ shortUrlCreation, setShortUrlCreation ] = useState({
|
||||||
customSlug: undefined,
|
longUrl: '',
|
||||||
domain: undefined,
|
tags: [],
|
||||||
validSince: undefined,
|
customSlug: undefined,
|
||||||
validUntil: undefined,
|
shortCodeLength: undefined,
|
||||||
maxVisits: undefined,
|
domain: undefined,
|
||||||
findIfExists: false,
|
validSince: undefined,
|
||||||
moreOptionsVisible: false,
|
validUntil: undefined,
|
||||||
};
|
maxVisits: undefined,
|
||||||
|
findIfExists: false,
|
||||||
|
});
|
||||||
|
const [ moreOptionsVisible, toggleMoreOptionsVisible ] = useToggle(false);
|
||||||
|
|
||||||
render() {
|
const changeTags = (tags) => setShortUrlCreation({ ...shortUrlCreation, tags: tags.map(normalizeTag) });
|
||||||
const { createShortUrl, shortUrlCreationResult, resetCreateShortUrl } = this.props;
|
|
||||||
|
|
||||||
const changeTags = (tags) => this.setState({ tags: tags.map(normalizeTag) });
|
|
||||||
const renderOptionalInput = (id, placeholder, type = 'text', props = {}) => (
|
const renderOptionalInput = (id, placeholder, type = 'text', props = {}) => (
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Input
|
<Input
|
||||||
id={id}
|
id={id}
|
||||||
type={type}
|
type={type}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
value={this.state[id]}
|
value={shortUrlCreation[id]}
|
||||||
onChange={(e) => this.setState({ [id]: e.target.value })}
|
onChange={(e) => setShortUrlCreation({ ...shortUrlCreation, [id]: e.target.value })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
@ -57,24 +54,25 @@ const CreateShortUrl = (
|
||||||
const renderDateInput = (id, placeholder, props = {}) => (
|
const renderDateInput = (id, placeholder, props = {}) => (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<DateInput
|
<DateInput
|
||||||
selected={this.state[id]}
|
selected={shortUrlCreation[id]}
|
||||||
placeholderText={placeholder}
|
placeholderText={placeholder}
|
||||||
isClearable
|
isClearable
|
||||||
onChange={(date) => this.setState({ [id]: date })}
|
onChange={(date) => setShortUrlCreation({ ...shortUrlCreation, [id]: date })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const save = (e) => {
|
const save = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
createShortUrl(pipe(
|
createShortUrl({
|
||||||
dissoc('moreOptionsVisible'),
|
...shortUrlCreation,
|
||||||
assoc('validSince', formatDate(this.state.validSince)),
|
validSince: formatDate(shortUrlCreation.validSince),
|
||||||
assoc('validUntil', formatDate(this.state.validUntil))
|
validUntil: formatDate(shortUrlCreation.validUntil),
|
||||||
)(this.state));
|
});
|
||||||
};
|
};
|
||||||
const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : '';
|
const currentServerVersion = selectedServer && selectedServer.version;
|
||||||
const disableDomain = isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', '1.19.0-beta.1');
|
const disableDomain = !versionMatch(currentServerVersion, { minVersion: '1.19.0-beta.1' });
|
||||||
|
const disableShortCodeLength = !versionMatch(currentServerVersion, { minVersion: '2.1.0' });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={save}>
|
<form onSubmit={save}>
|
||||||
|
@ -84,21 +82,30 @@ const CreateShortUrl = (
|
||||||
type="url"
|
type="url"
|
||||||
placeholder="Insert the URL to be shortened"
|
placeholder="Insert the URL to be shortened"
|
||||||
required
|
required
|
||||||
value={this.state.longUrl}
|
value={shortUrlCreation.longUrl}
|
||||||
onChange={(e) => this.setState({ longUrl: e.target.value })}
|
onChange={(e) => setShortUrlCreation({ ...shortUrlCreation, longUrl: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Collapse isOpen={this.state.moreOptionsVisible}>
|
<Collapse isOpen={moreOptionsVisible}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<TagsSelector tags={this.state.tags} onChange={changeTags} />
|
<TagsSelector tags={shortUrlCreation.tags} onChange={changeTags} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-sm-6">
|
<div className="col-sm-4">
|
||||||
{renderOptionalInput('customSlug', 'Custom slug')}
|
{renderOptionalInput('customSlug', 'Custom slug')}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-sm-6">
|
<div className="col-sm-4">
|
||||||
|
{renderOptionalInput('shortCodeLength', 'Short code length', 'number', {
|
||||||
|
min: 4,
|
||||||
|
disabled: disableShortCodeLength || hasValue(shortUrlCreation.customSlug),
|
||||||
|
...disableShortCodeLength && {
|
||||||
|
title: 'Shlink 2.1.0 or higher is required to be able to provide the short code length',
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="col-sm-4">
|
||||||
{renderOptionalInput('domain', 'Domain', 'text', {
|
{renderOptionalInput('domain', 'Domain', 'text', {
|
||||||
disabled: disableDomain,
|
disabled: disableDomain,
|
||||||
...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' },
|
...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' },
|
||||||
|
@ -107,14 +114,14 @@ const CreateShortUrl = (
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-sm-6">
|
<div className="col-sm-4">
|
||||||
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
|
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-sm-3">
|
<div className="col-sm-4">
|
||||||
{renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })}
|
{renderDateInput('validSince', 'Enabled since...', { maxDate: shortUrlCreation.validUntil })}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-sm-3">
|
<div className="col-sm-4">
|
||||||
{renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })}
|
{renderDateInput('validUntil', 'Enabled until...', { minDate: shortUrlCreation.validSince })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -122,8 +129,8 @@ const CreateShortUrl = (
|
||||||
<div className="mb-4 text-right">
|
<div className="mb-4 text-right">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
checked={this.state.findIfExists}
|
checked={shortUrlCreation.findIfExists}
|
||||||
onChange={(findIfExists) => this.setState({ findIfExists })}
|
onChange={(findIfExists) => setShortUrlCreation({ ...shortUrlCreation, findIfExists })}
|
||||||
>
|
>
|
||||||
Use existing URL if found
|
Use existing URL if found
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
@ -133,18 +140,14 @@ const CreateShortUrl = (
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button type="button" className="btn btn-outline-secondary" onClick={toggleMoreOptionsVisible}>
|
||||||
type="button"
|
<FontAwesomeIcon icon={moreOptionsVisible ? upIcon : downIcon} />
|
||||||
className="btn btn-outline-secondary"
|
|
||||||
onClick={() => this.setState(({ moreOptionsVisible }) => ({ moreOptionsVisible: !moreOptionsVisible }))}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={this.state.moreOptionsVisible ? upIcon : downIcon} />
|
|
||||||
|
|
||||||
{this.state.moreOptionsVisible ? 'Less' : 'More'} options
|
{moreOptionsVisible ? 'Less' : 'More'} options
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline-primary float-right"
|
className="btn btn-outline-primary float-right"
|
||||||
disabled={shortUrlCreationResult.loading || isEmpty(this.state.longUrl)}
|
disabled={shortUrlCreationResult.loading || isEmpty(shortUrlCreation.longUrl)}
|
||||||
>
|
>
|
||||||
{shortUrlCreationResult.loading ? 'Creating...' : 'Create'}
|
{shortUrlCreationResult.loading ? 'Creating...' : 'Create'}
|
||||||
</button>
|
</button>
|
||||||
|
@ -153,7 +156,11 @@ const CreateShortUrl = (
|
||||||
<CreateShortUrlResult {...shortUrlCreationResult} resetCreateShortUrl={resetCreateShortUrl} />
|
<CreateShortUrlResult {...shortUrlCreationResult} resetCreateShortUrl={resetCreateShortUrl} />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
CreateShortUrlComp.propTypes = propTypes;
|
||||||
|
|
||||||
|
return CreateShortUrlComp;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CreateShortUrl;
|
export default CreateShortUrl;
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
import { compare } from 'compare-versions';
|
import { compare } from 'compare-versions';
|
||||||
import { identity, memoizeWith } from 'ramda';
|
import { identity, memoizeWith } from 'ramda';
|
||||||
|
import { hasValue } from '../utils';
|
||||||
|
|
||||||
export const compareVersions = (firstVersion, operator, secondVersion) => compare(
|
export const versionMatch = (versionToMatch, { maxVersion, minVersion }) => {
|
||||||
firstVersion,
|
if (!hasValue(versionToMatch)) {
|
||||||
secondVersion,
|
return false;
|
||||||
operator,
|
}
|
||||||
);
|
|
||||||
|
const matchesMinVersion = !minVersion || compare(versionToMatch, minVersion, '>=');
|
||||||
|
const matchesMaxVersion = !maxVersion || compare(versionToMatch, maxVersion, '<=');
|
||||||
|
|
||||||
|
return !!(matchesMaxVersion && matchesMinVersion);
|
||||||
|
};
|
||||||
|
|
||||||
const versionIsValidSemVer = memoizeWith(identity, (version) => {
|
const versionIsValidSemVer = memoizeWith(identity, (version) => {
|
||||||
try {
|
try {
|
||||||
return compareVersions(version, '=', version);
|
return compare(version, version, '=');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import L from 'leaflet';
|
||||||
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
|
||||||
import marker from 'leaflet/dist/images/marker-icon.png';
|
import marker from 'leaflet/dist/images/marker-icon.png';
|
||||||
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
||||||
import { range } from 'ramda';
|
import { isEmpty, isNil, range } from 'ramda';
|
||||||
|
|
||||||
const TEN_ROUNDING_NUMBER = 10;
|
const TEN_ROUNDING_NUMBER = 10;
|
||||||
const DEFAULT_TIMEOUT_DELAY = 2000;
|
const DEFAULT_TIMEOUT_DELAY = 2000;
|
||||||
|
@ -45,3 +45,4 @@ export const rangeOf = (size, mappingFn, startAt = 1) => range(startAt, size + 1
|
||||||
|
|
||||||
export const roundTen = (number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER;
|
export const roundTen = (number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER;
|
||||||
|
|
||||||
|
export const hasValue = (value) => !isNil(value) && !isEmpty(value);
|
||||||
|
|
|
@ -25,43 +25,31 @@ describe('<CreateShortUrl />', () => {
|
||||||
createShortUrl.mockReset();
|
createShortUrl.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('saves short URL with data set in form controls', (done) => {
|
it('saves short URL with data set in form controls', () => {
|
||||||
const validSince = moment('2017-01-01');
|
const validSince = moment('2017-01-01');
|
||||||
const validUntil = moment('2017-01-06');
|
const validUntil = moment('2017-01-06');
|
||||||
|
|
||||||
const urlInput = wrapper.find('.form-control-lg');
|
wrapper.find('.form-control-lg').simulate('change', { target: { value: 'https://long-domain.com/foo/bar' } });
|
||||||
const tagsInput = wrapper.find(TagsSelector);
|
wrapper.find('TagsSelector').simulate('change', [ 'tag_foo', 'tag_bar' ]);
|
||||||
const customSlugInput = wrapper.find('#customSlug');
|
wrapper.find('#customSlug').simulate('change', { target: { value: 'my-slug' } });
|
||||||
const domain = wrapper.find('#domain');
|
wrapper.find('#domain').simulate('change', { target: { value: 'example.com' } });
|
||||||
const maxVisitsInput = wrapper.find('#maxVisits');
|
wrapper.find('#maxVisits').simulate('change', { target: { value: '20' } });
|
||||||
const dateInputs = wrapper.find(DateInput);
|
wrapper.find('#shortCodeLength').simulate('change', { target: { value: 15 } });
|
||||||
const validSinceInput = dateInputs.at(0);
|
wrapper.find(DateInput).at(0).simulate('change', validSince);
|
||||||
const validUntilInput = dateInputs.at(1);
|
wrapper.find(DateInput).at(1).simulate('change', validUntil);
|
||||||
|
wrapper.find('form').simulate('submit', { preventDefault: identity });
|
||||||
|
|
||||||
urlInput.simulate('change', { target: { value: 'https://long-domain.com/foo/bar' } });
|
expect(createShortUrl).toHaveBeenCalledTimes(1);
|
||||||
tagsInput.simulate('change', [ 'tag_foo', 'tag_bar' ]);
|
expect(createShortUrl).toHaveBeenCalledWith({
|
||||||
customSlugInput.simulate('change', { target: { value: 'my-slug' } });
|
longUrl: 'https://long-domain.com/foo/bar',
|
||||||
domain.simulate('change', { target: { value: 'example.com' } });
|
tags: [ 'tag_foo', 'tag_bar' ],
|
||||||
maxVisitsInput.simulate('change', { target: { value: '20' } });
|
customSlug: 'my-slug',
|
||||||
validSinceInput.simulate('change', validSince);
|
domain: 'example.com',
|
||||||
validUntilInput.simulate('change', validUntil);
|
validSince: validSince.format(),
|
||||||
|
validUntil: validUntil.format(),
|
||||||
setImmediate(() => {
|
maxVisits: '20',
|
||||||
const form = wrapper.find('form');
|
findIfExists: false,
|
||||||
|
shortCodeLength: 15,
|
||||||
form.simulate('submit', { preventDefault: identity });
|
|
||||||
expect(createShortUrl).toHaveBeenCalledTimes(1);
|
|
||||||
expect(createShortUrl).toHaveBeenCalledWith({
|
|
||||||
longUrl: 'https://long-domain.com/foo/bar',
|
|
||||||
tags: [ 'tag_foo', 'tag_bar' ],
|
|
||||||
customSlug: 'my-slug',
|
|
||||||
domain: 'example.com',
|
|
||||||
validSince: validSince.format(),
|
|
||||||
validUntil: validUntil.format(),
|
|
||||||
maxVisits: '20',
|
|
||||||
findIfExists: false,
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
23
test/utils/helpers/version.test.js
Normal file
23
test/utils/helpers/version.test.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { versionMatch } from '../../../src/utils/helpers/version';
|
||||||
|
|
||||||
|
describe('version', () => {
|
||||||
|
describe('versionMatch', () => {
|
||||||
|
it.each([
|
||||||
|
[ undefined, {}, false ],
|
||||||
|
[ null, {}, false ],
|
||||||
|
[ '', {}, false ],
|
||||||
|
[[], {}, false ],
|
||||||
|
[ '2.8.3', {}, true ],
|
||||||
|
[ '2.8.3', { minVersion: '2.0.0' }, true ],
|
||||||
|
[ '2.0.0', { minVersion: '2.0.0' }, true ],
|
||||||
|
[ '1.8.0', { maxVersion: '1.8.0' }, true ],
|
||||||
|
[ '1.7.1', { maxVersion: '1.8.0' }, true ],
|
||||||
|
[ '1.7.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, true ],
|
||||||
|
[ '1.8.3', { minVersion: '2.0.0' }, false ],
|
||||||
|
[ '1.8.3', { maxVersion: '1.8.0' }, false ],
|
||||||
|
[ '1.8.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, false ],
|
||||||
|
])('properly matches versions based on what is provided', (version, versionConstraints, expected) => {
|
||||||
|
expect(versionMatch(version, versionConstraints)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue