diff --git a/src/short-urls/helpers/ShortUrlsRow.js b/src/short-urls/helpers/ShortUrlsRow.js
index 43d4205f..a0cbf4a8 100644
--- a/src/short-urls/helpers/ShortUrlsRow.js
+++ b/src/short-urls/helpers/ShortUrlsRow.js
@@ -58,7 +58,11 @@ const ShortUrlsRow = (
| {this.renderTags(shortUrl.tags)} |
-
+
|
-
+
Visit stats
diff --git a/src/short-urls/helpers/VisitStatsLink.js b/src/short-urls/helpers/VisitStatsLink.js
new file mode 100644
index 00000000..f500e580
--- /dev/null
+++ b/src/short-urls/helpers/VisitStatsLink.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Link } from 'react-router-dom';
+import { serverType } from '../../servers/prop-types';
+import { shortUrlType } from '../reducers/shortUrlsList';
+
+const propTypes = {
+ shortUrl: shortUrlType,
+ selectedServer: serverType,
+ children: PropTypes.node.isRequired,
+};
+
+const buildVisitsUrl = ({ id }, { shortCode, domain }) => {
+ const query = domain ? `?domain=${domain}` : '';
+
+ return `/server/${id}/short-code/${shortCode}/visits${query}`;
+};
+
+const VisitStatsLink = ({ selectedServer, shortUrl, children, ...rest }) => {
+ if (!selectedServer || !shortUrl) {
+ return {children};
+ }
+
+ return {children};
+};
+
+VisitStatsLink.propTypes = propTypes;
+
+export default VisitStatsLink;
diff --git a/src/short-urls/reducers/shortUrlDeletion.js b/src/short-urls/reducers/shortUrlDeletion.js
index 9d86ccf1..6754527a 100644
--- a/src/short-urls/reducers/shortUrlDeletion.js
+++ b/src/short-urls/reducers/shortUrlDeletion.js
@@ -30,13 +30,13 @@ export default handleActions({
[RESET_DELETE_SHORT_URL]: () => initialState,
}, initialState);
-export const deleteShortUrl = (buildShlinkApiClient) => (shortCode) => async (dispatch, getState) => {
+export const deleteShortUrl = (buildShlinkApiClient) => (shortCode, domain) => async (dispatch, getState) => {
dispatch({ type: DELETE_SHORT_URL_START });
const { deleteShortUrl } = await buildShlinkApiClient(getState);
try {
- await deleteShortUrl(shortCode);
+ await deleteShortUrl(shortCode, domain);
dispatch({ type: SHORT_URL_DELETED, shortCode });
} catch (e) {
dispatch({ type: DELETE_SHORT_URL_ERROR, errorData: e.response.data });
diff --git a/src/short-urls/reducers/shortUrlMeta.js b/src/short-urls/reducers/shortUrlMeta.js
index 2c799e13..c7891cb8 100644
--- a/src/short-urls/reducers/shortUrlMeta.js
+++ b/src/short-urls/reducers/shortUrlMeta.js
@@ -35,12 +35,12 @@ export default handleActions({
[RESET_EDIT_SHORT_URL_META]: () => initialState,
}, initialState);
-export const editShortUrlMeta = (buildShlinkApiClient) => (shortCode, meta) => async (dispatch, getState) => {
+export const editShortUrlMeta = (buildShlinkApiClient) => (shortCode, domain, meta) => async (dispatch, getState) => {
dispatch({ type: EDIT_SHORT_URL_META_START });
const { updateShortUrlMeta } = await buildShlinkApiClient(getState);
try {
- await updateShortUrlMeta(shortCode, meta);
+ await updateShortUrlMeta(shortCode, domain, meta);
dispatch({ shortCode, meta, type: SHORT_URL_META_EDITED });
} catch (e) {
dispatch({ type: EDIT_SHORT_URL_META_ERROR });
diff --git a/src/short-urls/reducers/shortUrlTags.js b/src/short-urls/reducers/shortUrlTags.js
index 8530e954..77302d76 100644
--- a/src/short-urls/reducers/shortUrlTags.js
+++ b/src/short-urls/reducers/shortUrlTags.js
@@ -29,12 +29,12 @@ export default handleActions({
[RESET_EDIT_SHORT_URL_TAGS]: () => initialState,
}, initialState);
-export const editShortUrlTags = (buildShlinkApiClient) => (shortCode, tags) => async (dispatch, getState) => {
+export const editShortUrlTags = (buildShlinkApiClient) => (shortCode, domain, tags) => async (dispatch, getState) => {
dispatch({ type: EDIT_SHORT_URL_TAGS_START });
const { updateShortUrlTags } = await buildShlinkApiClient(getState);
try {
- const normalizedTags = await updateShortUrlTags(shortCode, tags);
+ const normalizedTags = await updateShortUrlTags(shortCode, domain, tags);
dispatch({ tags: normalizedTags, shortCode, type: SHORT_URL_TAGS_EDITED });
} catch (e) {
diff --git a/src/short-urls/reducers/shortUrlsList.js b/src/short-urls/reducers/shortUrlsList.js
index 6eadeca2..f43c264b 100644
--- a/src/short-urls/reducers/shortUrlsList.js
+++ b/src/short-urls/reducers/shortUrlsList.js
@@ -18,6 +18,7 @@ export const shortUrlType = PropTypes.shape({
visitsCount: PropTypes.number,
meta: shortUrlMetaType,
tags: PropTypes.arrayOf(PropTypes.string),
+ domain: PropTypes.string,
});
const initialState = {
diff --git a/src/utils/services/ShlinkApiClient.js b/src/utils/services/ShlinkApiClient.js
index fe9ce69a..fc9d5652 100644
--- a/src/utils/services/ShlinkApiClient.js
+++ b/src/utils/services/ShlinkApiClient.js
@@ -1,5 +1,5 @@
import qs from 'qs';
-import { isEmpty, isNil, pipe, reject } from 'ramda';
+import { isEmpty, isNil, reject } from 'ramda';
import PropTypes from 'prop-types';
export const apiErrorType = PropTypes.shape({
@@ -12,6 +12,7 @@ export const apiErrorType = PropTypes.shape({
});
const buildShlinkBaseUrl = (url, apiVersion) => url ? `${url}/rest/v${apiVersion}` : '';
+const rejectNilProps = reject(isNil);
export default class ShlinkApiClient {
constructor(axios, baseUrl, apiKey) {
@@ -21,10 +22,8 @@ export default class ShlinkApiClient {
this._apiKey = apiKey || '';
}
- listShortUrls = pipe(
- (options = {}) => reject(isNil, options),
- (options) => this._performRequest('/short-urls', 'GET', options).then((resp) => resp.data.shortUrls)
- );
+ listShortUrls = (options = {}) =>
+ this._performRequest('/short-urls', 'GET', options).then((resp) => resp.data.shortUrls);
createShortUrl = (options) => {
const filteredOptions = reject((value) => isEmpty(value) || isNil(value), options);
@@ -37,20 +36,20 @@ export default class ShlinkApiClient {
this._performRequest(`/short-urls/${shortCode}/visits`, 'GET', query)
.then((resp) => resp.data.visits);
- getShortUrl = (shortCode) =>
- this._performRequest(`/short-urls/${shortCode}`, 'GET')
+ getShortUrl = (shortCode, domain) =>
+ this._performRequest(`/short-urls/${shortCode}`, 'GET', { domain })
.then((resp) => resp.data);
- deleteShortUrl = (shortCode) =>
- this._performRequest(`/short-urls/${shortCode}`, 'DELETE')
+ deleteShortUrl = (shortCode, domain) =>
+ this._performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
.then(() => ({}));
- updateShortUrlTags = (shortCode, tags) =>
- this._performRequest(`/short-urls/${shortCode}/tags`, 'PUT', {}, { tags })
+ updateShortUrlTags = (shortCode, domain, tags) =>
+ this._performRequest(`/short-urls/${shortCode}/tags`, 'PUT', { domain }, { tags })
.then((resp) => resp.data.tags);
- updateShortUrlMeta = (shortCode, meta) =>
- this._performRequest(`/short-urls/${shortCode}`, 'PATCH', {}, meta)
+ updateShortUrlMeta = (shortCode, domain, meta) =>
+ this._performRequest(`/short-urls/${shortCode}`, 'PATCH', { domain }, meta)
.then(() => meta);
listTags = () =>
@@ -73,7 +72,7 @@ export default class ShlinkApiClient {
method,
url: `${buildShlinkBaseUrl(this._baseUrl, this._apiVersion)}${url}`,
headers: { 'X-Api-Key': this._apiKey },
- params: query,
+ params: rejectNilProps(query),
data: body,
paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'brackets' }),
});
diff --git a/src/visits/ShortUrlVisits.js b/src/visits/ShortUrlVisits.js
index d78fd876..7b1ae484 100644
--- a/src/visits/ShortUrlVisits.js
+++ b/src/visits/ShortUrlVisits.js
@@ -4,6 +4,7 @@ import { isEmpty, mapObjIndexed, values } from 'ramda';
import React from 'react';
import { Card } from 'reactstrap';
import PropTypes from 'prop-types';
+import qs from 'qs';
import DateRangeRow from '../utils/DateRangeRow';
import MutedMessage from '../utils/MuttedMessage';
import { formatDate } from '../utils/utils';
@@ -21,6 +22,9 @@ const ShortUrlVisits = (
match: PropTypes.shape({
params: PropTypes.object,
}),
+ location: PropTypes.shape({
+ search: PropTypes.string,
+ }),
getShortUrlVisits: PropTypes.func,
shortUrlVisits: shortUrlVisitsType,
getShortUrlDetail: PropTypes.func,
@@ -29,24 +33,24 @@ const ShortUrlVisits = (
};
state = { startDate: undefined, endDate: undefined };
- loadVisits = () => {
- const { match: { params }, getShortUrlVisits } = this.props;
+ loadVisits = (loadDetail = false) => {
+ const { match: { params }, location: { search }, getShortUrlVisits, getShortUrlDetail } = this.props;
const { shortCode } = params;
- const dates = mapObjIndexed(formatDate(), this.state);
- const { startDate, endDate } = dates;
+ const { startDate, endDate } = mapObjIndexed(formatDate(), this.state);
+ const { domain } = qs.parse(search, { ignoreQueryPrefix: true });
- // While the "page" is loaded, use the timestamp + filtering dates as memoization IDs for stats calcs
+ // While the "page" is loaded, use the timestamp + filtering dates as memoization IDs for stats calculations
this.memoizationId = `${this.timeWhenMounted}_${shortCode}_${startDate}_${endDate}`;
- getShortUrlVisits(shortCode, dates);
+ getShortUrlVisits(shortCode, { startDate, endDate, domain });
+
+ if (loadDetail) {
+ getShortUrlDetail(shortCode, domain);
+ }
};
componentDidMount() {
- const { match: { params }, getShortUrlDetail } = this.props;
- const { shortCode } = params;
-
this.timeWhenMounted = new Date().getTime();
- this.loadVisits();
- getShortUrlDetail(shortCode);
+ this.loadVisits(true);
}
componentWillUnmount() {
diff --git a/src/visits/VisitsHeader.js b/src/visits/VisitsHeader.js
index 439f2063..e13398dc 100644
--- a/src/visits/VisitsHeader.js
+++ b/src/visits/VisitsHeader.js
@@ -33,7 +33,7 @@ export default function VisitsHeader({ shortUrlDetail, shortUrlVisits }) {
Visits:{' '}
-
+
Visit stats for
diff --git a/src/visits/reducers/shortUrlDetail.js b/src/visits/reducers/shortUrlDetail.js
index 893c12ac..2f22243c 100644
--- a/src/visits/reducers/shortUrlDetail.js
+++ b/src/visits/reducers/shortUrlDetail.js
@@ -26,13 +26,13 @@ export default handleActions({
[GET_SHORT_URL_DETAIL]: (state, { shortUrl }) => ({ shortUrl, loading: false, error: false }),
}, initialState);
-export const getShortUrlDetail = (buildShlinkApiClient) => (shortCode) => async (dispatch, getState) => {
+export const getShortUrlDetail = (buildShlinkApiClient) => (shortCode, domain) => async (dispatch, getState) => {
dispatch({ type: GET_SHORT_URL_DETAIL_START });
const { getShortUrl } = await buildShlinkApiClient(getState);
try {
- const shortUrl = await getShortUrl(shortCode);
+ const shortUrl = await getShortUrl(shortCode, domain);
dispatch({ shortUrl, type: GET_SHORT_URL_DETAIL });
} catch (e) {
diff --git a/src/visits/reducers/shortUrlVisits.js b/src/visits/reducers/shortUrlVisits.js
index faefcc5b..8b23125f 100644
--- a/src/visits/reducers/shortUrlVisits.js
+++ b/src/visits/reducers/shortUrlVisits.js
@@ -49,7 +49,7 @@ export default handleActions({
[GET_SHORT_URL_VISITS_CANCEL]: (state) => ({ ...state, cancelLoad: true }),
}, initialState);
-export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, dates) => async (dispatch, getState) => {
+export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, query) => async (dispatch, getState) => {
dispatch({ type: GET_SHORT_URL_VISITS_START });
const { getShortUrlVisits } = await buildShlinkApiClient(getState);
@@ -57,7 +57,7 @@ export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, dates) =>
const isLastPage = ({ currentPage, pagesCount }) => currentPage >= pagesCount;
const loadVisits = async (page = 1) => {
- const { pagination, data } = await getShortUrlVisits(shortCode, { ...dates, page, itemsPerPage });
+ const { pagination, data } = await getShortUrlVisits(shortCode, { ...query, page, itemsPerPage });
// If pagination was not returned, then this is an older shlink version. Just return data
if (!pagination || isLastPage(pagination)) {
@@ -96,7 +96,7 @@ export const getShortUrlVisits = (buildShlinkApiClient) => (shortCode, dates) =>
const loadVisitsInParallel = (pages) =>
Promise.all(pages.map(
(page) =>
- getShortUrlVisits(shortCode, { ...dates, page, itemsPerPage })
+ getShortUrlVisits(shortCode, { ...query, page, itemsPerPage })
.then(prop('data'))
)).then(flatten);
diff --git a/test/short-urls/helpers/EditTagsModal.test.js b/test/short-urls/helpers/EditTagsModal.test.js
index f6e96a5e..07243442 100644
--- a/test/short-urls/helpers/EditTagsModal.test.js
+++ b/test/short-urls/helpers/EditTagsModal.test.js
@@ -1,6 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Modal } from 'reactstrap';
+import each from 'jest-each';
import createEditTagsModal from '../../../src/short-urls/helpers/EditTagsModal';
describe('', () => {
@@ -10,7 +11,7 @@ describe('', () => {
const editShortUrlTags = jest.fn(() => Promise.resolve());
const resetShortUrlsTags = jest.fn();
const toggle = jest.fn();
- const createWrapper = (shortUrlTags) => {
+ const createWrapper = (shortUrlTags, domain) => {
const EditTagsModal = createEditTagsModal(TagsSelector);
wrapper = shallow(
@@ -19,6 +20,7 @@ describe('', () => {
shortUrl={{
tags: [],
shortCode,
+ domain,
originalUrl: 'https://long-domain.com/foo/bar',
}}
shortUrlTags={shortUrlTags}
@@ -74,19 +76,19 @@ describe('', () => {
expect(saveBtn.text()).toEqual('Saving tags...');
});
- it('saves tags when save button is clicked', (done) => {
+ each([[ undefined ], [ null ], [ 'example.com' ]]).it('saves tags when save button is clicked', (domain, done) => {
const wrapper = createWrapper({
shortCode,
tags: [],
saving: true,
error: false,
- });
+ }, domain);
const saveBtn = wrapper.find('.btn-primary');
saveBtn.simulate('click');
expect(editShortUrlTags).toHaveBeenCalledTimes(1);
- expect(editShortUrlTags).toHaveBeenCalledWith(shortCode, []);
+ expect(editShortUrlTags).toHaveBeenCalledWith(shortCode, domain, []);
// Wrap this expect in a setImmediate since it is called as a result of an inner promise
setImmediate(() => {
diff --git a/test/short-urls/helpers/ShortUrlVisitsCount.test.js b/test/short-urls/helpers/ShortUrlVisitsCount.test.js
index 42508219..3a375fd9 100644
--- a/test/short-urls/helpers/ShortUrlVisitsCount.test.js
+++ b/test/short-urls/helpers/ShortUrlVisitsCount.test.js
@@ -7,8 +7,8 @@ import ShortUrlVisitsCount from '../../../src/short-urls/helpers/ShortUrlVisitsC
describe('', () => {
let wrapper;
- const createWrapper = (visitsCount, meta) => {
- wrapper = shallow();
+ const createWrapper = (visitsCount, shortUrl) => {
+ wrapper = shallow();
return wrapper;
};
@@ -17,11 +17,11 @@ describe('', () => {
each([ undefined, {}]).it('just returns visits when no maxVisits is provided', (meta) => {
const visitsCount = 45;
- const wrapper = createWrapper(visitsCount, meta);
+ const wrapper = createWrapper(visitsCount, { meta });
const maxVisitsHelper = wrapper.find('.short-urls-visits-count__max-visits-control');
const maxVisitsTooltip = wrapper.find(UncontrolledTooltip);
- expect(wrapper.html()).toEqual(`${visitsCount}`);
+ expect(wrapper.html()).toEqual(`${visitsCount}`);
expect(maxVisitsHelper).toHaveLength(0);
expect(maxVisitsTooltip).toHaveLength(0);
});
@@ -30,7 +30,7 @@ describe('', () => {
const visitsCount = 45;
const maxVisits = 500;
const meta = { maxVisits };
- const wrapper = createWrapper(visitsCount, meta);
+ const wrapper = createWrapper(visitsCount, { meta });
const maxVisitsHelper = wrapper.find('.short-urls-visits-count__max-visits-control');
const maxVisitsTooltip = wrapper.find(UncontrolledTooltip);
diff --git a/test/short-urls/helpers/VisitStatsLink.test.js b/test/short-urls/helpers/VisitStatsLink.test.js
new file mode 100644
index 00000000..dd7a2ec6
--- /dev/null
+++ b/test/short-urls/helpers/VisitStatsLink.test.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import each from 'jest-each';
+import { Link } from 'react-router-dom';
+import VisitStatsLink from '../../../src/short-urls/helpers/VisitStatsLink';
+
+describe('', () => {
+ let wrapper;
+
+ afterEach(() => wrapper && wrapper.unmount());
+
+ each([
+ [ undefined, undefined ],
+ [ null, null ],
+ [{}, null ],
+ [{}, undefined ],
+ [ null, {}],
+ [ undefined, {}],
+ ]).it('only renders a plan span when either server or short URL are not set', (selectedServer, shortUrl) => {
+ wrapper = shallow(Something);
+ const link = wrapper.find(Link);
+
+ expect(link).toHaveLength(0);
+ expect(wrapper.html()).toEqual('Something');
+ });
+
+ each([
+ [{ id: '1' }, { shortCode: 'abc123' }, '/server/1/short-code/abc123/visits' ],
+ [
+ { id: '3' },
+ { shortCode: 'def456', domain: 'example.com' },
+ '/server/3/short-code/def456/visits?domain=example.com',
+ ],
+ ]).it('renders link with expected query when', (selectedServer, shortUrl, expectedLink) => {
+ wrapper = shallow(Something);
+ const link = wrapper.find(Link);
+ const to = link.prop('to');
+
+ expect(link).toHaveLength(1);
+ expect(to).toEqual(expectedLink);
+ });
+});
diff --git a/test/short-urls/reducers/shortUrlDeletion.test.js b/test/short-urls/reducers/shortUrlDeletion.test.js
index 4c62133b..be7f3adf 100644
--- a/test/short-urls/reducers/shortUrlDeletion.test.js
+++ b/test/short-urls/reducers/shortUrlDeletion.test.js
@@ -1,3 +1,4 @@
+import each from 'jest-each';
import reducer, {
DELETE_SHORT_URL_ERROR,
DELETE_SHORT_URL_START,
@@ -59,20 +60,22 @@ describe('shortUrlDeletionReducer', () => {
getState.mockClear();
});
- it('dispatches proper actions if API client request succeeds', async () => {
+ each(
+ [[ undefined ], [ null ], [ 'example.com' ]]
+ ).it('dispatches proper actions if API client request succeeds', async (domain) => {
const apiClientMock = {
deleteShortUrl: jest.fn(() => ''),
};
const shortCode = 'abc123';
- await deleteShortUrl(() => apiClientMock)(shortCode)(dispatch, getState);
+ await deleteShortUrl(() => apiClientMock)(shortCode, domain)(dispatch, getState);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_SHORT_URL_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_DELETED, shortCode });
expect(apiClientMock.deleteShortUrl).toHaveBeenCalledTimes(1);
- expect(apiClientMock.deleteShortUrl).toHaveBeenCalledWith(shortCode);
+ expect(apiClientMock.deleteShortUrl).toHaveBeenCalledWith(shortCode, domain);
});
it('dispatches proper actions if API client request fails', async () => {
@@ -94,7 +97,7 @@ describe('shortUrlDeletionReducer', () => {
expect(dispatch).toHaveBeenNthCalledWith(2, { type: DELETE_SHORT_URL_ERROR, errorData: data });
expect(apiClientMock.deleteShortUrl).toHaveBeenCalledTimes(1);
- expect(apiClientMock.deleteShortUrl).toHaveBeenCalledWith(shortCode);
+ expect(apiClientMock.deleteShortUrl).toHaveBeenCalledWith(shortCode, undefined);
});
});
});
diff --git a/test/short-urls/reducers/shortUrlMeta.test.js b/test/short-urls/reducers/shortUrlMeta.test.js
index d1158efd..d3019395 100644
--- a/test/short-urls/reducers/shortUrlMeta.test.js
+++ b/test/short-urls/reducers/shortUrlMeta.test.js
@@ -1,4 +1,5 @@
import moment from 'moment';
+import each from 'jest-each';
import reducer, {
EDIT_SHORT_URL_META_START,
EDIT_SHORT_URL_META_ERROR,
@@ -56,12 +57,12 @@ describe('shortUrlMetaReducer', () => {
afterEach(jest.clearAllMocks);
- it('dispatches metadata on success', async () => {
- await editShortUrlMeta(buildShlinkApiClient)(shortCode, meta)(dispatch);
+ each([[ undefined ], [ null ], [ 'example.com' ]]).it('dispatches metadata on success', async (domain) => {
+ await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch);
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1);
- expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, meta);
+ expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, domain, meta);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, meta, shortCode });
@@ -73,14 +74,14 @@ describe('shortUrlMetaReducer', () => {
updateShortUrlMeta.mockRejectedValue(error);
try {
- await editShortUrlMeta(buildShlinkApiClient)(shortCode, meta)(dispatch);
+ await editShortUrlMeta(buildShlinkApiClient)(shortCode, undefined, meta)(dispatch);
} catch (e) {
expect(e).toBe(error);
}
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1);
- expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, meta);
+ expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, undefined, meta);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_META_ERROR });
diff --git a/test/short-urls/reducers/shortUrlTags.test.js b/test/short-urls/reducers/shortUrlTags.test.js
index 1a80e583..bd43199a 100644
--- a/test/short-urls/reducers/shortUrlTags.test.js
+++ b/test/short-urls/reducers/shortUrlTags.test.js
@@ -1,3 +1,4 @@
+import each from 'jest-each';
import reducer, {
EDIT_SHORT_URL_TAGS_ERROR,
EDIT_SHORT_URL_TAGS_START,
@@ -60,16 +61,16 @@ describe('shortUrlTagsReducer', () => {
dispatch.mockReset();
});
- it('dispatches normalized tags on success', async () => {
+ each([[ undefined ], [ null ], [ 'example.com' ]]).it('dispatches normalized tags on success', async (domain) => {
const normalizedTags = [ 'bar', 'foo' ];
updateShortUrlTags.mockResolvedValue(normalizedTags);
- await editShortUrlTags(buildShlinkApiClient)(shortCode, tags)(dispatch);
+ await editShortUrlTags(buildShlinkApiClient)(shortCode, domain, tags)(dispatch);
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
- expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
+ expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, domain, tags);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_TAGS_EDITED, tags: normalizedTags, shortCode });
@@ -81,14 +82,14 @@ describe('shortUrlTagsReducer', () => {
updateShortUrlTags.mockRejectedValue(error);
try {
- await editShortUrlTags(buildShlinkApiClient)(shortCode, tags)(dispatch);
+ await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(dispatch);
} catch (e) {
expect(e).toBe(error);
}
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
- expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, tags);
+ expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, undefined, tags);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_SHORT_URL_TAGS_ERROR });
diff --git a/test/utils/services/ShlinkApiClient.test.js b/test/utils/services/ShlinkApiClient.test.js
index 09697bd6..8cda6271 100644
--- a/test/utils/services/ShlinkApiClient.test.js
+++ b/test/utils/services/ShlinkApiClient.test.js
@@ -1,8 +1,14 @@
+import each from 'jest-each';
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
describe('ShlinkApiClient', () => {
const createAxiosMock = (data) => () => Promise.resolve(data);
const createApiClient = (data) => new ShlinkApiClient(createAxiosMock(data));
+ const shortCodesWithDomainCombinations = [
+ [ 'abc123', null ],
+ [ 'abc123', undefined ],
+ [ 'abc123', 'example.com' ],
+ ];
describe('listShortUrls', () => {
it('properly returns short URLs list', async () => {
@@ -67,43 +73,45 @@ describe('ShlinkApiClient', () => {
});
describe('getShortUrl', () => {
- it('properly returns short URL', async () => {
+ each(shortCodesWithDomainCombinations).it('properly returns short URL', async (shortCode, domain) => {
const expectedShortUrl = { foo: 'bar' };
const axiosSpy = jest.fn(createAxiosMock({
data: expectedShortUrl,
}));
const { getShortUrl } = new ShlinkApiClient(axiosSpy);
- const result = await getShortUrl('abc123');
+ const result = await getShortUrl(shortCode, domain);
expect(expectedShortUrl).toEqual(result);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({
- url: '/short-urls/abc123',
+ url: `/short-urls/${shortCode}`,
method: 'GET',
+ params: domain ? { domain } : {},
}));
});
});
describe('updateShortUrlTags', () => {
- it('properly updates short URL tags', async () => {
+ each(shortCodesWithDomainCombinations).it('properly updates short URL tags', async (shortCode, domain) => {
const expectedTags = [ 'foo', 'bar' ];
const axiosSpy = jest.fn(createAxiosMock({
data: { tags: expectedTags },
}));
const { updateShortUrlTags } = new ShlinkApiClient(axiosSpy);
- const result = await updateShortUrlTags('abc123', expectedTags);
+ const result = await updateShortUrlTags(shortCode, domain, expectedTags);
expect(expectedTags).toEqual(result);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({
- url: '/short-urls/abc123/tags',
+ url: `/short-urls/${shortCode}/tags`,
method: 'PUT',
+ params: domain ? { domain } : {},
}));
});
});
describe('updateShortUrlMeta', () => {
- it('properly updates short URL meta', async () => {
+ each(shortCodesWithDomainCombinations).it('properly updates short URL meta', async (shortCode, domain) => {
const expectedMeta = {
maxVisits: 50,
validSince: '2025-01-01T10:00:00+01:00',
@@ -111,12 +119,13 @@ describe('ShlinkApiClient', () => {
const axiosSpy = jest.fn(createAxiosMock());
const { updateShortUrlMeta } = new ShlinkApiClient(axiosSpy);
- const result = await updateShortUrlMeta('abc123', expectedMeta);
+ const result = await updateShortUrlMeta(shortCode, domain, expectedMeta);
expect(expectedMeta).toEqual(result);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({
- url: '/short-urls/abc123',
+ url: `/short-urls/${shortCode}`,
method: 'PATCH',
+ params: domain ? { domain } : {},
}));
});
});
@@ -172,15 +181,16 @@ describe('ShlinkApiClient', () => {
});
describe('deleteShortUrl', () => {
- it('properly deletes provided short URL', async () => {
+ each(shortCodesWithDomainCombinations).it('properly deletes provided short URL', async (shortCode, domain) => {
const axiosSpy = jest.fn(createAxiosMock({}));
const { deleteShortUrl } = new ShlinkApiClient(axiosSpy);
- await deleteShortUrl('abc123');
+ await deleteShortUrl(shortCode, domain);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({
- url: '/short-urls/abc123',
+ url: `/short-urls/${shortCode}`,
method: 'DELETE',
+ params: domain ? { domain } : {},
}));
});
});
diff --git a/test/visits/ShortUrlVisits.test.js b/test/visits/ShortUrlVisits.test.js
index 40d634c5..b136efde 100644
--- a/test/visits/ShortUrlVisits.test.js
+++ b/test/visits/ShortUrlVisits.test.js
@@ -17,15 +17,17 @@ describe('', () => {
const match = {
params: { shortCode: 'abc123' },
};
+ const location = { search: '' };
const createComponent = (shortUrlVisits) => {
- const ShortUrlVisits = createShortUrlVisits({ processStatsFromVisits });
+ const ShortUrlVisits = createShortUrlVisits({ processStatsFromVisits }, () => '');
wrapper = shallow(
', () => {
it('shows the amount of visits', () => {
const visitsBadge = wrapper.find('.badge');
- expect(visitsBadge.html()).toContain(`Visits: ${shortUrlVisits.visits.length}`);
+ expect(visitsBadge.html()).toContain(`Visits: ${shortUrlVisits.visits.length}`);
});
it('shows when the URL was created', () => {
|