mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-24 01:48:18 +03:00
Merge pull request #401 from acelaya-forks/feature/feature-improvements
Feature/feature improvements
This commit is contained in:
commit
ad46927750
14 changed files with 107 additions and 79 deletions
|
@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
import { useSwipeable, useToggle } from '../utils/helpers/hooks';
|
||||||
import { versionMatch } from '../utils/helpers/version';
|
import { supportsOrphanVisits, supportsTagVisits } from '../utils/helpers/features';
|
||||||
import { isReachableServer } from '../servers/data';
|
import { isReachableServer } from '../servers/data';
|
||||||
import NotFound from './NotFound';
|
import NotFound from './NotFound';
|
||||||
import { AsideMenuProps } from './AsideMenu';
|
import { AsideMenuProps } from './AsideMenu';
|
||||||
|
@ -30,8 +30,8 @@ const MenuLayout = (
|
||||||
return <ServerError />;
|
return <ServerError />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addTagsVisitsRoute = versionMatch(selectedServer.version, { minVersion: '2.2.0' });
|
const addTagsVisitsRoute = supportsTagVisits(selectedServer);
|
||||||
const addOrphanVisitsRoute = versionMatch(selectedServer.version, { minVersion: '2.6.0' });
|
const addOrphanVisitsRoute = supportsOrphanVisits(selectedServer);
|
||||||
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
||||||
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { SemVer } from '../../utils/helpers/version';
|
||||||
|
|
||||||
export interface ServerData {
|
export interface ServerData {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -9,7 +11,7 @@ export interface ServerWithId extends ServerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReachableServer extends ServerWithId {
|
export interface ReachableServer extends ServerWithId {
|
||||||
version: string;
|
version: SemVer;
|
||||||
printableVersion: string;
|
printableVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ import { InputType } from 'reactstrap/lib/Input';
|
||||||
import * as m from 'moment';
|
import * as m from 'moment';
|
||||||
import DateInput, { DateInputProps } from '../utils/DateInput';
|
import DateInput, { DateInputProps } from '../utils/DateInput';
|
||||||
import Checkbox from '../utils/Checkbox';
|
import Checkbox from '../utils/Checkbox';
|
||||||
import { versionMatch, Versions } from '../utils/helpers/version';
|
import { Versions } from '../utils/helpers/version';
|
||||||
|
import { supportsListingDomains, supportsSettingShortCodeLength } from '../utils/helpers/features';
|
||||||
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
import { handleEventPreventingDefault, hasValue } from '../utils/utils';
|
||||||
import { isReachableServer, SelectedServer } from '../servers/data';
|
import { SelectedServer } from '../servers/data';
|
||||||
import { formatIsoDate } from '../utils/helpers/date';
|
import { formatIsoDate } from '../utils/helpers/date';
|
||||||
import { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
import { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
||||||
import { DomainSelectorProps } from '../domains/DomainSelector';
|
import { DomainSelectorProps } from '../domains/DomainSelector';
|
||||||
|
@ -117,9 +118,8 @@ const CreateShortUrl = (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentServerVersion = isReachableServer(selectedServer) ? selectedServer.version : '';
|
const showDomainSelector = supportsListingDomains(selectedServer);
|
||||||
const showDomainSelector = versionMatch(currentServerVersion, { minVersion: '2.4.0' });
|
const disableShortCodeLength = !supportsSettingShortCodeLength(selectedServer);
|
||||||
const disableShortCodeLength = !versionMatch(currentServerVersion, { minVersion: '2.1.0' });
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="create-short-url" onSubmit={save}>
|
<form className="create-short-url" onSubmit={save}>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { FC, ReactNode } from 'react';
|
||||||
import { isEmpty } from 'ramda';
|
import { isEmpty } from 'ramda';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { SelectedServer } from '../servers/data';
|
import { SelectedServer } from '../servers/data';
|
||||||
import { titleIsSupported } from '../utils/helpers/features';
|
import { supportsShortUrlTitle } from '../utils/helpers/features';
|
||||||
import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
|
import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
|
||||||
import { ShortUrlsRowProps } from './helpers/ShortUrlsRow';
|
import { ShortUrlsRowProps } from './helpers/ShortUrlsRow';
|
||||||
import { OrderableFields } from './reducers/shortUrlsListParams';
|
import { OrderableFields } from './reducers/shortUrlsListParams';
|
||||||
|
@ -29,7 +29,7 @@ export const ShortUrlsTable = (ShortUrlsRow: FC<ShortUrlsRowProps>) => ({
|
||||||
const actionableFieldClasses = classNames({ 'short-urls-table__header-cell--with-action': !!orderByColumn });
|
const actionableFieldClasses = classNames({ 'short-urls-table__header-cell--with-action': !!orderByColumn });
|
||||||
const orderableColumnsClasses = classNames('short-urls-table__header-cell', actionableFieldClasses);
|
const orderableColumnsClasses = classNames('short-urls-table__header-cell', actionableFieldClasses);
|
||||||
const tableClasses = classNames('table table-hover', className);
|
const tableClasses = classNames('table table-hover', className);
|
||||||
const supportsTitle = titleIsSupported(selectedServer);
|
const supportsTitle = supportsShortUrlTitle(selectedServer);
|
||||||
|
|
||||||
const renderShortUrls = () => {
|
const renderShortUrls = () => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
@ -3,15 +3,15 @@ import { Modal, DropdownItem, FormGroup, ModalBody, ModalHeader, Row } from 'rea
|
||||||
import { ExternalLink } from 'react-external-link';
|
import { ExternalLink } from 'react-external-link';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ShortUrlModalProps } from '../data';
|
import { ShortUrlModalProps } from '../data';
|
||||||
import { ReachableServer } from '../../servers/data';
|
import { SelectedServer } from '../../servers/data';
|
||||||
import { versionMatch } from '../../utils/helpers/version';
|
|
||||||
import { DropdownBtn } from '../../utils/DropdownBtn';
|
import { DropdownBtn } from '../../utils/DropdownBtn';
|
||||||
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
|
import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon';
|
||||||
import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat } from '../../utils/helpers/qrCodes';
|
import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat } from '../../utils/helpers/qrCodes';
|
||||||
|
import { supportsQrCodeSizeInQuery, supportsQrCodeSvgFormat, supportsQrCodeMargin } from '../../utils/helpers/features';
|
||||||
import './QrCodeModal.scss';
|
import './QrCodeModal.scss';
|
||||||
|
|
||||||
interface QrCodeModalConnectProps extends ShortUrlModalProps {
|
interface QrCodeModalConnectProps extends ShortUrlModalProps {
|
||||||
selectedServer: ReachableServer;
|
selectedServer: SelectedServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }: QrCodeModalConnectProps) => {
|
const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }: QrCodeModalConnectProps) => {
|
||||||
|
@ -19,9 +19,9 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }:
|
||||||
const [ margin, setMargin ] = useState(0);
|
const [ margin, setMargin ] = useState(0);
|
||||||
const [ format, setFormat ] = useState<QrCodeFormat>('png');
|
const [ format, setFormat ] = useState<QrCodeFormat>('png');
|
||||||
const capabilities: QrCodeCapabilities = useMemo(() => ({
|
const capabilities: QrCodeCapabilities = useMemo(() => ({
|
||||||
useSizeInPath: !versionMatch(selectedServer.version, { minVersion: '2.5.0' }),
|
useSizeInPath: !supportsQrCodeSizeInQuery(selectedServer),
|
||||||
svgIsSupported: versionMatch(selectedServer.version, { minVersion: '2.4.0' }),
|
svgIsSupported: supportsQrCodeSvgFormat(selectedServer),
|
||||||
marginIsSupported: versionMatch(selectedServer.version, { minVersion: '2.6.0' }),
|
marginIsSupported: supportsQrCodeMargin(selectedServer),
|
||||||
}), [ selectedServer ]);
|
}), [ selectedServer ]);
|
||||||
const qrCodeUrl = useMemo(
|
const qrCodeUrl = useMemo(
|
||||||
() => buildQrCodeUrl(shortUrl, { size, format, margin }, capabilities),
|
() => buildQrCodeUrl(shortUrl, { size, format, margin }, capabilities),
|
||||||
|
|
|
@ -7,8 +7,7 @@ import { ShortUrlIdentifier } from '../data';
|
||||||
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
||||||
import { ProblemDetailsError } from '../../api/types';
|
import { ProblemDetailsError } from '../../api/types';
|
||||||
import { parseApiError } from '../../api/utils';
|
import { parseApiError } from '../../api/utils';
|
||||||
import { isReachableServer } from '../../servers/data';
|
import { supportsTagsInPatch } from '../../utils/helpers/features';
|
||||||
import { versionMatch } from '../../utils/helpers/version';
|
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
|
||||||
|
@ -54,15 +53,12 @@ export const editShortUrlTags = (buildShlinkApiClient: ShlinkApiClientBuilder) =
|
||||||
) => async (dispatch: Dispatch, getState: GetState) => {
|
) => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
dispatch({ type: EDIT_SHORT_URL_TAGS_START });
|
dispatch({ type: EDIT_SHORT_URL_TAGS_START });
|
||||||
const { selectedServer } = getState();
|
const { selectedServer } = getState();
|
||||||
const supportsTagsInPatch = isReachableServer(selectedServer) && versionMatch(
|
const tagsInPatch = supportsTagsInPatch(selectedServer);
|
||||||
selectedServer.version,
|
|
||||||
{ minVersion: '2.6.0' },
|
|
||||||
);
|
|
||||||
const { updateShortUrlTags, updateShortUrlMeta } = buildShlinkApiClient(getState);
|
const { updateShortUrlTags, updateShortUrlMeta } = buildShlinkApiClient(getState);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const normalizedTags = await (
|
const normalizedTags = await (
|
||||||
supportsTagsInPatch
|
tagsInPatch
|
||||||
? updateShortUrlMeta(shortCode, domain, { tags }).then(prop('tags'))
|
? updateShortUrlMeta(shortCode, domain, { tags }).then(prop('tags'))
|
||||||
: updateShortUrlTags(shortCode, domain, tags)
|
: updateShortUrlTags(shortCode, domain, tags)
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,4 +4,20 @@ import { versionMatch, Versions } from './version';
|
||||||
const serverMatchesVersions = (versions: Versions) => (selectedServer: SelectedServer): boolean =>
|
const serverMatchesVersions = (versions: Versions) => (selectedServer: SelectedServer): boolean =>
|
||||||
isReachableServer(selectedServer) && versionMatch(selectedServer.version, versions);
|
isReachableServer(selectedServer) && versionMatch(selectedServer.version, versions);
|
||||||
|
|
||||||
export const titleIsSupported = serverMatchesVersions({ minVersion: '2.6.0' });
|
export const supportsSettingShortCodeLength = serverMatchesVersions({ minVersion: '2.1.0' });
|
||||||
|
|
||||||
|
export const supportsTagVisits = serverMatchesVersions({ minVersion: '2.2.0' });
|
||||||
|
|
||||||
|
export const supportsListingDomains = serverMatchesVersions({ minVersion: '2.4.0' });
|
||||||
|
|
||||||
|
export const supportsQrCodeSvgFormat = supportsListingDomains;
|
||||||
|
|
||||||
|
export const supportsQrCodeSizeInQuery = serverMatchesVersions({ minVersion: '2.5.0' });
|
||||||
|
|
||||||
|
export const supportsShortUrlTitle = serverMatchesVersions({ minVersion: '2.6.0' });
|
||||||
|
|
||||||
|
export const supportsOrphanVisits = supportsShortUrlTitle;
|
||||||
|
|
||||||
|
export const supportsQrCodeMargin = supportsShortUrlTitle;
|
||||||
|
|
||||||
|
export const supportsTagsInPatch = supportsShortUrlTitle;
|
||||||
|
|
|
@ -2,12 +2,20 @@ import { compare } from 'compare-versions';
|
||||||
import { identity, memoizeWith } from 'ramda';
|
import { identity, memoizeWith } from 'ramda';
|
||||||
import { Empty, hasValue } from '../utils';
|
import { Empty, hasValue } from '../utils';
|
||||||
|
|
||||||
|
type SemVerPatternFragment = `${bigint | '*'}`;
|
||||||
|
|
||||||
|
export type SemVerPattern = SemVerPatternFragment
|
||||||
|
| `${SemVerPatternFragment}.${SemVerPatternFragment}`
|
||||||
|
| `${SemVerPatternFragment}.${SemVerPatternFragment}.${SemVerPatternFragment}`;
|
||||||
|
|
||||||
export interface Versions {
|
export interface Versions {
|
||||||
maxVersion?: string;
|
maxVersion?: SemVerPattern;
|
||||||
minVersion?: string;
|
minVersion?: SemVerPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const versionMatch = (versionToMatch: string | Empty, { maxVersion, minVersion }: Versions): boolean => {
|
export type SemVer = `${bigint}.${bigint}.${bigint}` | 'latest';
|
||||||
|
|
||||||
|
export const versionMatch = (versionToMatch: SemVer | Empty, { maxVersion, minVersion }: Versions): boolean => {
|
||||||
if (!hasValue(versionToMatch)) {
|
if (!hasValue(versionToMatch)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +26,7 @@ export const versionMatch = (versionToMatch: string | Empty, { maxVersion, minVe
|
||||||
return matchesMaxVersion && matchesMinVersion;
|
return matchesMaxVersion && matchesMinVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
const versionIsValidSemVer = memoizeWith(identity, (version: string) => {
|
const versionIsValidSemVer = memoizeWith(identity, (version: string): version is SemVer => {
|
||||||
try {
|
try {
|
||||||
return compare(version, version, '=');
|
return compare(version, version, '=');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -28,5 +36,5 @@ const versionIsValidSemVer = memoizeWith(identity, (version: string) => {
|
||||||
|
|
||||||
export const versionToPrintable = (version: string) => !versionIsValidSemVer(version) ? version : `v${version}`;
|
export const versionToPrintable = (version: string) => !versionIsValidSemVer(version) ? version : `v${version}`;
|
||||||
|
|
||||||
export const versionToSemVer = (defaultValue = 'latest') =>
|
export const versionToSemVer = (defaultValue: SemVer = 'latest') =>
|
||||||
(version: string) => versionIsValidSemVer(version) ? version : defaultValue;
|
(version: string): SemVer => versionIsValidSemVer(version) ? version : defaultValue;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Mock } from 'ts-mockery';
|
||||||
import createMenuLayout from '../../src/common/MenuLayout';
|
import createMenuLayout from '../../src/common/MenuLayout';
|
||||||
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data';
|
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
import NoMenuLayout from '../../src/common/NoMenuLayout';
|
import NoMenuLayout from '../../src/common/NoMenuLayout';
|
||||||
|
import { SemVer } from '../../src/utils/helpers/version';
|
||||||
|
|
||||||
describe('<MenuLayout />', () => {
|
describe('<MenuLayout />', () => {
|
||||||
const ServerError = jest.fn();
|
const ServerError = jest.fn();
|
||||||
|
@ -48,11 +49,11 @@ describe('<MenuLayout />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.1.0', 6 ],
|
[ '2.1.0' as SemVer, 6 ],
|
||||||
[ '2.2.0', 7 ],
|
[ '2.2.0' as SemVer, 7 ],
|
||||||
[ '2.5.0', 7 ],
|
[ '2.5.0' as SemVer, 7 ],
|
||||||
[ '2.6.0', 8 ],
|
[ '2.6.0' as SemVer, 8 ],
|
||||||
[ '2.7.0', 8 ],
|
[ '2.7.0' as SemVer, 8 ],
|
||||||
])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => {
|
])('has expected amount of routes based on selected server\'s version', (version, expectedAmountOfRoutes) => {
|
||||||
const selectedServer = Mock.of<ReachableServer>({ version });
|
const selectedServer = Mock.of<ReachableServer>({ version });
|
||||||
const wrapper = createWrapper(selectedServer).dive();
|
const wrapper = createWrapper(selectedServer).dive();
|
||||||
|
|
|
@ -14,11 +14,11 @@ describe('<ShlinkVersions />', () => {
|
||||||
afterEach(() => wrapper?.unmount());
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '1.2.3', Mock.of<ReachableServer>({ version: '', printableVersion: 'foo' }), 'v1.2.3', 'foo' ],
|
[ '1.2.3', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'foo' }), 'v1.2.3', 'foo' ],
|
||||||
[ 'foo', Mock.of<ReachableServer>({ version: '', printableVersion: '1.2.3' }), 'latest', '1.2.3' ],
|
[ 'foo', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: '1.2.3' }), 'latest', '1.2.3' ],
|
||||||
[ 'latest', Mock.of<ReachableServer>({ version: '', printableVersion: 'latest' }), 'latest', 'latest' ],
|
[ 'latest', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'latest' }), 'latest', 'latest' ],
|
||||||
[ '5.5.0', Mock.of<ReachableServer>({ version: '', printableVersion: '0.2.8' }), 'v5.5.0', '0.2.8' ],
|
[ '5.5.0', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: '0.2.8' }), 'v5.5.0', '0.2.8' ],
|
||||||
[ 'not-semver', Mock.of<ReachableServer>({ version: '', printableVersion: 'something' }), 'latest', 'something' ],
|
[ 'not-semver', Mock.of<ReachableServer>({ version: '1.0.0', printableVersion: 'some' }), 'latest', 'some' ],
|
||||||
])(
|
])(
|
||||||
'displays expected versions when selected server is reachable',
|
'displays expected versions when selected server is reachable',
|
||||||
(clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => {
|
(clientVersion, selectedServer, expectedClientVersion, expectedServerVersion) => {
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { mount, ReactWrapper } from 'enzyme';
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import ForServerVersion from '../../../src/servers/helpers/ForServerVersion';
|
import ForServerVersion from '../../../src/servers/helpers/ForServerVersion';
|
||||||
import { ReachableServer, SelectedServer } from '../../../src/servers/data';
|
import { ReachableServer, SelectedServer } from '../../../src/servers/data';
|
||||||
|
import { SemVer, SemVerPattern } from '../../../src/utils/helpers/version';
|
||||||
|
|
||||||
describe('<ForServerVersion />', () => {
|
describe('<ForServerVersion />', () => {
|
||||||
let wrapped: ReactWrapper;
|
let wrapped: ReactWrapper;
|
||||||
|
|
||||||
const renderComponent = (selectedServer: SelectedServer, minVersion?: string, maxVersion?: string) => {
|
const renderComponent = (selectedServer: SelectedServer, minVersion?: SemVerPattern, maxVersion?: SemVerPattern) => {
|
||||||
wrapped = mount(
|
wrapped = mount(
|
||||||
<ForServerVersion minVersion={minVersion} maxVersion={maxVersion} selectedServer={selectedServer}>
|
<ForServerVersion minVersion={minVersion} maxVersion={maxVersion} selectedServer={selectedServer}>
|
||||||
<span>Hello</span>
|
<span>Hello</span>
|
||||||
|
@ -19,15 +20,15 @@ describe('<ForServerVersion />', () => {
|
||||||
afterEach(() => wrapped?.unmount());
|
afterEach(() => wrapped?.unmount());
|
||||||
|
|
||||||
it('does not render children when current server is empty', () => {
|
it('does not render children when current server is empty', () => {
|
||||||
const wrapped = renderComponent(null, '1');
|
const wrapped = renderComponent(null, '1.*.*');
|
||||||
|
|
||||||
expect(wrapped.html()).toBeNull();
|
expect(wrapped.html()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.0.0', undefined, '1.8.3' ],
|
[ '2.0.0' as SemVerPattern, undefined, '1.8.3' as SemVer ],
|
||||||
[ undefined, '1.8.0', '1.8.3' ],
|
[ undefined, '1.8.0' as SemVerPattern, '1.8.3' as SemVer ],
|
||||||
[ '1.7.0', '1.8.0', '1.8.3' ],
|
[ '1.7.0' as SemVerPattern, '1.8.0' as SemVerPattern, '1.8.3' as SemVer ],
|
||||||
])('does not render children when current version does not match requirements', (min, max, version) => {
|
])('does not render children when current version does not match requirements', (min, max, version) => {
|
||||||
const wrapped = renderComponent(Mock.of<ReachableServer>({ version, printableVersion: version }), min, max);
|
const wrapped = renderComponent(Mock.of<ReachableServer>({ version, printableVersion: version }), min, max);
|
||||||
|
|
||||||
|
@ -35,11 +36,11 @@ describe('<ForServerVersion />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.0.0', undefined, '2.8.3' ],
|
[ '2.0.0' as SemVerPattern, undefined, '2.8.3' as SemVer ],
|
||||||
[ '2.0.0', undefined, '2.0.0' ],
|
[ '2.0.0' as SemVerPattern, undefined, '2.0.0' as SemVer ],
|
||||||
[ undefined, '1.8.0', '1.8.0' ],
|
[ undefined, '1.8.0' as SemVerPattern, '1.8.0' as SemVer ],
|
||||||
[ undefined, '1.8.0', '1.7.1' ],
|
[ undefined, '1.8.0' as SemVerPattern, '1.7.1' as SemVer ],
|
||||||
[ '1.7.0', '1.8.0', '1.7.3' ],
|
[ '1.7.0' as SemVerPattern, '1.8.0' as SemVerPattern, '1.7.3' as SemVer ],
|
||||||
])('renders children when current version matches requirements', (min, max, version) => {
|
])('renders children when current version matches requirements', (min, max, version) => {
|
||||||
const wrapped = renderComponent(Mock.of<ReachableServer>({ version, printableVersion: version }), min, max);
|
const wrapped = renderComponent(Mock.of<ReachableServer>({ version, printableVersion: version }), min, max);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { ShortUrlsTable as shortUrlsTableCreator } from '../../src/short-urls/Sh
|
||||||
import { OrderableFields, SORTABLE_FIELDS } from '../../src/short-urls/reducers/shortUrlsListParams';
|
import { OrderableFields, SORTABLE_FIELDS } from '../../src/short-urls/reducers/shortUrlsListParams';
|
||||||
import { ShortUrlsList } from '../../src/short-urls/reducers/shortUrlsList';
|
import { ShortUrlsList } from '../../src/short-urls/reducers/shortUrlsList';
|
||||||
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
|
import { SemVer } from '../../src/utils/helpers/version';
|
||||||
|
|
||||||
describe('<ShortUrlsTable />', () => {
|
describe('<ShortUrlsTable />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
|
@ -61,10 +62,10 @@ describe('<ShortUrlsTable />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.6.0' ],
|
[ '2.6.0' as SemVer ],
|
||||||
[ '2.6.1' ],
|
[ '2.6.1' as SemVer ],
|
||||||
[ '2.7.0' ],
|
[ '2.7.0' as SemVer ],
|
||||||
[ '3.0.0' ],
|
[ '3.0.0' as SemVer ],
|
||||||
])('should render composed column when server supports title', (version) => {
|
])('should render composed column when server supports title', (version) => {
|
||||||
const wrapper = createWrapper(Mock.of<ReachableServer>({ version }));
|
const wrapper = createWrapper(Mock.of<ReachableServer>({ version }));
|
||||||
const composedColumn = wrapper.find('table').find('th').at(2);
|
const composedColumn = wrapper.find('table').find('th').at(2);
|
||||||
|
|
|
@ -7,11 +7,12 @@ import { ShortUrl } from '../../../src/short-urls/data';
|
||||||
import { ReachableServer } from '../../../src/servers/data';
|
import { ReachableServer } from '../../../src/servers/data';
|
||||||
import { CopyToClipboardIcon } from '../../../src/utils/CopyToClipboardIcon';
|
import { CopyToClipboardIcon } from '../../../src/utils/CopyToClipboardIcon';
|
||||||
import { DropdownBtn } from '../../../src/utils/DropdownBtn';
|
import { DropdownBtn } from '../../../src/utils/DropdownBtn';
|
||||||
|
import { SemVer } from '../../../src/utils/helpers/version';
|
||||||
|
|
||||||
describe('<QrCodeModal />', () => {
|
describe('<QrCodeModal />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const shortUrl = 'https://doma.in/abc123';
|
const shortUrl = 'https://doma.in/abc123';
|
||||||
const createWrapper = (version = '2.6.0') => {
|
const createWrapper = (version: SemVer = '2.6.0') => {
|
||||||
const selectedServer = Mock.of<ReachableServer>({ version });
|
const selectedServer = Mock.of<ReachableServer>({ version });
|
||||||
|
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
|
@ -37,12 +38,12 @@ describe('<QrCodeModal />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.3.0', 0, '/qr-code/300' ],
|
[ '2.3.0' as SemVer, 0, '/qr-code/300' ],
|
||||||
[ '2.4.0', 0, '/qr-code/300?format=png' ],
|
[ '2.4.0' as SemVer, 0, '/qr-code/300?format=png' ],
|
||||||
[ '2.4.0', 10, '/qr-code/300?format=png' ],
|
[ '2.4.0' as SemVer, 10, '/qr-code/300?format=png' ],
|
||||||
[ '2.5.0', 0, '/qr-code?size=300&format=png' ],
|
[ '2.5.0' as SemVer, 0, '/qr-code?size=300&format=png' ],
|
||||||
[ '2.6.0', 0, '/qr-code?size=300&format=png' ],
|
[ '2.6.0' as SemVer, 0, '/qr-code?size=300&format=png' ],
|
||||||
[ '2.6.0', 10, '/qr-code?size=300&format=png&margin=10' ],
|
[ '2.6.0' as SemVer, 10, '/qr-code?size=300&format=png&margin=10' ],
|
||||||
])('displays an image with the QR code of the URL', (version, margin, expectedUrl) => {
|
])('displays an image with the QR code of the URL', (version, margin, expectedUrl) => {
|
||||||
const wrapper = createWrapper(version);
|
const wrapper = createWrapper(version);
|
||||||
const formControls = wrapper.find('.form-control-range');
|
const formControls = wrapper.find('.form-control-range');
|
||||||
|
@ -84,9 +85,9 @@ describe('<QrCodeModal />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ '2.3.0', 0, 'col-12' ],
|
[ '2.3.0' as SemVer, 0, 'col-12' ],
|
||||||
[ '2.4.0', 1, 'col-md-6' ],
|
[ '2.4.0' as SemVer, 1, 'col-md-6' ],
|
||||||
[ '2.6.0', 1, 'col-md-4' ],
|
[ '2.6.0' as SemVer, 1, 'col-md-4' ],
|
||||||
])('shows expected components based on server version', (version, expectedAmountOfDropdowns, expectedRangeClass) => {
|
])('shows expected components based on server version', (version, expectedAmountOfDropdowns, expectedRangeClass) => {
|
||||||
const wrapper = createWrapper(version);
|
const wrapper = createWrapper(version);
|
||||||
const dropdown = wrapper.find(DropdownBtn);
|
const dropdown = wrapper.find(DropdownBtn);
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import { versionMatch } from '../../../src/utils/helpers/version';
|
import { Mock } from 'ts-mockery';
|
||||||
|
import { SemVer, versionMatch, Versions } from '../../../src/utils/helpers/version';
|
||||||
|
import { Empty } from '../../../src/utils/utils';
|
||||||
|
|
||||||
describe('version', () => {
|
describe('version', () => {
|
||||||
describe('versionMatch', () => {
|
describe('versionMatch', () => {
|
||||||
it.each([
|
it.each([
|
||||||
[ undefined, {}, false ],
|
[ undefined, Mock.all<Versions>(), false ],
|
||||||
[ null, {}, false ],
|
[ null, Mock.all<Versions>(), false ],
|
||||||
[ '', {}, false ],
|
[ '' as Empty, Mock.all<Versions>(), false ],
|
||||||
[[], {}, false ],
|
[[], Mock.all<Versions>(), false ],
|
||||||
[ '2.8.3', {}, true ],
|
[ '2.8.3' as SemVer, Mock.all<Versions>(), true ],
|
||||||
[ '2.8.3', { minVersion: '2.0.0' }, true ],
|
[ '2.8.3' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), true ],
|
||||||
[ '2.0.0', { minVersion: '2.0.0' }, true ],
|
[ '2.0.0' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), true ],
|
||||||
[ '1.8.0', { maxVersion: '1.8.0' }, true ],
|
[ '1.8.0' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), true ],
|
||||||
[ '1.7.1', { maxVersion: '1.8.0' }, true ],
|
[ '1.7.1' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), true ],
|
||||||
[ '1.7.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, true ],
|
[ '1.7.3' as SemVer, Mock.of<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), true ],
|
||||||
[ '1.8.3', { minVersion: '2.0.0' }, false ],
|
[ '1.8.3' as SemVer, Mock.of<Versions>({ minVersion: '2.0.0' }), false ],
|
||||||
[ '1.8.3', { maxVersion: '1.8.0' }, false ],
|
[ '1.8.3' as SemVer, Mock.of<Versions>({ maxVersion: '1.8.0' }), false ],
|
||||||
[ '1.8.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, false ],
|
[ '1.8.3' as SemVer, Mock.of<Versions>({ minVersion: '1.7.0', maxVersion: '1.8.0' }), false ],
|
||||||
])('properly matches versions based on what is provided', (version, versionConstraints, expected) => {
|
])('properly matches versions based on what is provided', (version, versionConstraints, expected) => {
|
||||||
expect(versionMatch(version, versionConstraints)).toEqual(expected);
|
expect(versionMatch(version, versionConstraints)).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue