From 28ca54547ee403c65064a2264a7ef4de8b79e33a Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Apr 2019 12:41:59 +0200 Subject: [PATCH] Removed remaining usages of sinon --- package-lock.json | 103 ------------------ package.json | 1 - test/short-urls/CreateShortUrl.test.js | 9 +- test/short-urls/SearchBar.test.js | 18 ++- .../helpers/CreateShortUrlResult.test.js | 9 +- .../helpers/DeleteShortUrlModal.test.js | 9 +- test/short-urls/helpers/EditTagsModal.test.js | 33 +++--- test/short-urls/helpers/ShortUrlsRow.test.js | 7 +- .../helpers/ShortUrlsRowMenu.test.js | 3 +- .../reducers/shortUrlCreation.test.js | 27 +++-- .../reducers/shortUrlDeletion.test.js | 35 +++--- .../short-urls/reducers/shortUrlsList.test.js | 31 +++--- test/tags/TagsList.test.js | 9 +- .../helpers/DeleteTagConfirmModal.test.js | 19 ++-- test/tags/reducers/tagDelete.test.js | 27 +++-- test/tags/reducers/tagEdit.test.js | 38 +++---- test/utils/SortingDropdown.test.js | 19 ++-- test/utils/services/ColorGenerator.test.js | 21 ++-- test/utils/services/ShlinkApiClient.test.js | 79 ++++++-------- test/utils/services/Storage.test.js | 18 ++- test/utils/utils.test.js | 16 ++- test/visits/ShortUrlVisits.test.js | 13 +-- test/visits/reducers/shortUrlDetail.test.js | 38 ++----- test/visits/reducers/shortUrlVisits.test.js | 51 +++------ 24 files changed, 231 insertions(+), 402 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fec2639..4eb8bad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -921,43 +921,6 @@ "integrity": "sha1-K1o6s/kYzKSKjHVMCBaOPwPrphs=", "dev": true }, - "@sinonjs/commons": { - "version": "1.3.0", - "resolved": "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.3.0.tgz", - "integrity": "sha1-UKJ1QBa28wqZTO2m2aCow2rdqEk=", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "3.1.0", - "resolved": "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.1.0.tgz", - "integrity": "sha1-asnR6xghmE2ExJlnJuRdFkbYzOU=", - "dev": true, - "requires": { - "@sinonjs/samsam": "^2 || ^3" - }, - "dependencies": { - "@sinonjs/samsam": { - "version": "3.0.2", - "resolved": "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.0.2.tgz", - "integrity": "sha1-ME+zO9VYWgst+KTIAfy0f6hNjkM=", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.0.2", - "array-from": "^2.1.1", - "lodash.get": "^4.4.2" - } - } - } - }, - "@sinonjs/samsam": { - "version": "2.1.3", - "resolved": "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.3.tgz", - "integrity": "sha1-Ys8qm2JO3HlRNBNf43/Cro6ja+M=", - "dev": true - }, "@svgr/core": { "version": "2.4.1", "resolved": "https://registry.yarnpkg.com/@svgr/core/-/core-2.4.1.tgz", @@ -1468,12 +1431,6 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "array-includes": { "version": "3.0.3", "resolved": "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz", @@ -10255,12 +10212,6 @@ "resolved": "https://registry.yarnpkg.com/just-curry-it/-/just-curry-it-3.1.0.tgz", "integrity": "sha1-q1na7TCKWLhHraFm7dCi1Adm+8U=" }, - "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha1-8/R/ffyg+YnFVBCn68iFSwcQivw=", - "dev": true - }, "killable": { "version": "1.0.1", "resolved": "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz", @@ -10496,12 +10447,6 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -10587,12 +10532,6 @@ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", "dev": true }, - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha1-ETAB1Wv8fgLVbjYpHMXEE9GqBzM=", - "dev": true - }, "longest-streak": { "version": "2.0.2", "resolved": "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz", @@ -11256,19 +11195,6 @@ "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, - "nise": { - "version": "1.4.8", - "resolved": "https://registry.yarnpkg.com/nise/-/nise-1.4.8.tgz", - "integrity": "sha1-zpHDHobPmyxMrEnX/Nf1Z3m/1rA=", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.1.0", - "just-extend": "^4.0.2", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" - } - }, "no-case": { "version": "2.3.2", "resolved": "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz", @@ -15199,23 +15125,6 @@ } } }, - "sinon": { - "version": "6.3.5", - "resolved": "https://registry.yarnpkg.com/sinon/-/sinon-6.3.5.tgz", - "integrity": "sha1-D21qW066rR9ujgGTlVQtHQLBRKA=", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.0.2", - "@sinonjs/formatio": "^3.0.0", - "@sinonjs/samsam": "^2.1.2", - "diff": "^3.5.0", - "lodash.get": "^4.4.2", - "lolex": "^2.7.5", - "nise": "^1.4.5", - "supports-color": "^5.5.0", - "type-detect": "^4.0.8" - } - }, "sisteransi": { "version": "0.1.1", "resolved": "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz", @@ -16374,12 +16283,6 @@ } } }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz", @@ -16668,12 +16571,6 @@ "prelude-ls": "~1.1.2" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha1-dkb7XxiHHPu3dJ5pvTmmOI63RQw=", - "dev": true - }, "type-is": { "version": "1.6.16", "resolved": "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz", diff --git a/package.json b/package.json index 3f5b5768..99f33260 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,6 @@ "resolve": "^1.8.1", "sass-loader": "^7.1.0", "serve": "^10.0.0", - "sinon": "^6.1.5", "style-loader": "^0.23.0", "stylelint": "^9.9.0", "stylelint-config-adidas": "^1.2.1", diff --git a/test/short-urls/CreateShortUrl.test.js b/test/short-urls/CreateShortUrl.test.js index 55592106..b78a550f 100644 --- a/test/short-urls/CreateShortUrl.test.js +++ b/test/short-urls/CreateShortUrl.test.js @@ -1,7 +1,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import moment from 'moment'; -import * as sinon from 'sinon'; import { identity } from 'ramda'; import createShortUrlsCreator from '../../src/short-urls/CreateShortUrl'; import DateInput from '../../src/utils/DateInput'; @@ -12,7 +11,7 @@ describe('', () => { const shortUrlCreationResult = { loading: false, }; - const createShortUrl = sinon.spy(); + const createShortUrl = jest.fn(); beforeEach(() => { const CreateShortUrl = createShortUrlsCreator(TagsSelector, () => ''); @@ -23,7 +22,7 @@ describe('', () => { }); afterEach(() => { wrapper.unmount(); - createShortUrl.resetHistory(); + createShortUrl.mockReset(); }); it('saves short URL with data set in form controls', (done) => { @@ -49,8 +48,8 @@ describe('', () => { const form = wrapper.find('form'); form.simulate('submit', { preventDefault: identity }); - expect(createShortUrl.callCount).toEqual(1); - expect(createShortUrl.getCall(0).args).toEqual( + expect(createShortUrl).toHaveBeenCalledTimes(1); + expect(createShortUrl.mock.calls[0]).toEqual( [ { longUrl: 'https://long-domain.com/foo/bar', diff --git a/test/short-urls/SearchBar.test.js b/test/short-urls/SearchBar.test.js index d3a3869e..54bb8781 100644 --- a/test/short-urls/SearchBar.test.js +++ b/test/short-urls/SearchBar.test.js @@ -1,21 +1,17 @@ import React from 'react'; import { shallow } from 'enzyme'; -import sinon from 'sinon'; import searchBarCreator from '../../src/short-urls/SearchBar'; import SearchField from '../../src/utils/SearchField'; import Tag from '../../src/tags/helpers/Tag'; describe('', () => { let wrapper; - const listShortUrlsMock = sinon.spy(); + const listShortUrlsMock = jest.fn(); const SearchBar = searchBarCreator({}); afterEach(() => { - listShortUrlsMock.resetHistory(); - - if (wrapper) { - wrapper.unmount(); - } + listShortUrlsMock.mockReset(); + wrapper && wrapper.unmount(); }); it('renders a SearchField', () => { @@ -42,9 +38,9 @@ describe('', () => { wrapper = shallow(); const searchField = wrapper.find(SearchField); - expect(listShortUrlsMock.callCount).toEqual(0); + expect(listShortUrlsMock).not.toHaveBeenCalled(); searchField.simulate('change'); - expect(listShortUrlsMock.callCount).toEqual(1); + expect(listShortUrlsMock).toHaveBeenCalledTimes(1); }); it('updates short URLs list when a tag is removed', () => { @@ -53,8 +49,8 @@ describe('', () => { ); const tag = wrapper.find(Tag).first(); - expect(listShortUrlsMock.callCount).toEqual(0); + expect(listShortUrlsMock).not.toHaveBeenCalled(); tag.simulate('close'); - expect(listShortUrlsMock.callCount).toEqual(1); + expect(listShortUrlsMock).toHaveBeenCalledTimes(1); }); }); diff --git a/test/short-urls/helpers/CreateShortUrlResult.test.js b/test/short-urls/helpers/CreateShortUrlResult.test.js index e256969f..ece6bbdf 100644 --- a/test/short-urls/helpers/CreateShortUrlResult.test.js +++ b/test/short-urls/helpers/CreateShortUrlResult.test.js @@ -3,12 +3,11 @@ import { shallow } from 'enzyme'; import { identity } from 'ramda'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import { Tooltip } from 'reactstrap'; -import * as sinon from 'sinon'; import createCreateShortUrlResult from '../../../src/short-urls/helpers/CreateShortUrlResult'; describe('', () => { let wrapper; - const stateFlagTimeout = sinon.spy(); + const stateFlagTimeout = jest.fn(); const createWrapper = (result, error = false) => { const CreateShortUrlResult = createCreateShortUrlResult(stateFlagTimeout); @@ -18,7 +17,7 @@ describe('', () => { }; afterEach(() => { - stateFlagTimeout.resetHistory(); + stateFlagTimeout.mockReset(); wrapper && wrapper.unmount(); }); @@ -48,8 +47,8 @@ describe('', () => { const wrapper = createWrapper({ shortUrl: 'https://doma.in/abc123' }); const copyBtn = wrapper.find(CopyToClipboard); - expect(stateFlagTimeout.callCount).toEqual(0); + expect(stateFlagTimeout).not.toHaveBeenCalled(); copyBtn.simulate('copy'); - expect(stateFlagTimeout.callCount).toEqual(1); + expect(stateFlagTimeout).toHaveBeenCalledTimes(1); }); }); diff --git a/test/short-urls/helpers/DeleteShortUrlModal.test.js b/test/short-urls/helpers/DeleteShortUrlModal.test.js index 6118f732..204a2779 100644 --- a/test/short-urls/helpers/DeleteShortUrlModal.test.js +++ b/test/short-urls/helpers/DeleteShortUrlModal.test.js @@ -1,7 +1,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { identity } from 'ramda'; -import * as sinon from 'sinon'; import DeleteShortUrlModal from '../../../src/short-urls/helpers/DeleteShortUrlModal'; describe('', () => { @@ -11,7 +10,7 @@ describe('', () => { shortCode: 'abc123', originalUrl: 'https://long-domain.com/foo/bar', }; - const deleteShortUrl = sinon.fake.returns(Promise.resolve()); + const deleteShortUrl = jest.fn(() => Promise.resolve()); const createWrapper = (shortUrlDeletion) => { wrapper = shallow( ', () => { afterEach(() => { wrapper && wrapper.unmount(); - deleteShortUrl.resetHistory(); + deleteShortUrl.mockClear(); }); it('shows threshold error message when threshold error occurs', () => { @@ -106,9 +105,9 @@ describe('', () => { setImmediate(() => { const form = wrapper.find('form'); - expect(deleteShortUrl.callCount).toEqual(0); + expect(deleteShortUrl).not.toHaveBeenCalled(); form.simulate('submit', { preventDefault: identity }); - expect(deleteShortUrl.callCount).toEqual(1); + expect(deleteShortUrl).toHaveBeenCalledTimes(1); done(); }); }); diff --git a/test/short-urls/helpers/EditTagsModal.test.js b/test/short-urls/helpers/EditTagsModal.test.js index 3c307eba..43df3feb 100644 --- a/test/short-urls/helpers/EditTagsModal.test.js +++ b/test/short-urls/helpers/EditTagsModal.test.js @@ -1,6 +1,5 @@ import React from 'react'; import { shallow } from 'enzyme'; -import * as sinon from 'sinon'; import { Modal } from 'reactstrap'; import createEditTagsModal from '../../../src/short-urls/helpers/EditTagsModal'; @@ -8,10 +7,10 @@ describe('', () => { let wrapper; const shortCode = 'abc123'; const TagsSelector = () => ''; - const editShortUrlTags = sinon.fake.resolves(); - const shortUrlTagsEdited = sinon.fake(); - const resetShortUrlsTags = sinon.fake(); - const toggle = sinon.fake(); + const editShortUrlTags = jest.fn(() => Promise.resolve()); + const shortUrlTagsEdited = jest.fn(); + const resetShortUrlsTags = jest.fn(); + const toggle = jest.fn(); const createWrapper = (shortUrlTags) => { const EditTagsModal = createEditTagsModal(TagsSelector); @@ -37,10 +36,10 @@ describe('', () => { afterEach(() => { wrapper && wrapper.unmount(); - editShortUrlTags.resetHistory(); - shortUrlTagsEdited.resetHistory(); - resetShortUrlsTags.resetHistory(); - toggle.resetHistory(); + editShortUrlTags.mockClear(); + shortUrlTagsEdited.mockReset(); + resetShortUrlsTags.mockReset(); + toggle.mockReset(); }); it('resets tags when component is mounted', () => { @@ -51,7 +50,7 @@ describe('', () => { error: false, }); - expect(resetShortUrlsTags.callCount).toEqual(1); + expect(resetShortUrlsTags).toHaveBeenCalledTimes(1); }); it('renders tags selector and save button when loaded', () => { @@ -92,12 +91,12 @@ describe('', () => { saveBtn.simulate('click'); - expect(editShortUrlTags.callCount).toEqual(1); - expect(editShortUrlTags.getCall(0).args).toEqual([ shortCode, []]); + expect(editShortUrlTags).toHaveBeenCalledTimes(1); + expect(editShortUrlTags.mock.calls[0]).toEqual([ shortCode, []]); // Wrap this expect in a setImmediate since it is called as a result of an inner promise setImmediate(() => { - expect(toggle.callCount).toEqual(1); + expect(toggle).toHaveBeenCalledTimes(1); done(); }); }); @@ -112,7 +111,7 @@ describe('', () => { const modal = wrapper.find(Modal); modal.simulate('closed'); - expect(shortUrlTagsEdited.callCount).toEqual(0); + expect(shortUrlTagsEdited).not.toHaveBeenCalled(); }); it('notifies tags have been edited when window is closed after saving', (done) => { @@ -130,8 +129,8 @@ describe('', () => { // Wrap this expect in a setImmediate since it is called as a result of an inner promise setImmediate(() => { modal.simulate('closed'); - expect(shortUrlTagsEdited.callCount).toEqual(1); - expect(shortUrlTagsEdited.getCall(0).args).toEqual([ shortCode, []]); + expect(shortUrlTagsEdited).toHaveBeenCalledTimes(1); + expect(shortUrlTagsEdited.mock.calls[0]).toEqual([ shortCode, []]); done(); }); }); @@ -146,6 +145,6 @@ describe('', () => { const cancelBtn = wrapper.find('.btn-link'); cancelBtn.simulate('click'); - expect(toggle.callCount).toEqual(1); + expect(toggle).toHaveBeenCalledTimes(1); }); }); diff --git a/test/short-urls/helpers/ShortUrlsRow.test.js b/test/short-urls/helpers/ShortUrlsRow.test.js index 48c32b5e..4c1d3be8 100644 --- a/test/short-urls/helpers/ShortUrlsRow.test.js +++ b/test/short-urls/helpers/ShortUrlsRow.test.js @@ -3,7 +3,6 @@ import { shallow } from 'enzyme'; import moment from 'moment'; import Moment from 'react-moment'; import { assoc, toString } from 'ramda'; -import * as sinon from 'sinon'; import createShortUrlsRow from '../../../src/short-urls/helpers/ShortUrlsRow'; import ExternalLink from '../../../src/utils/ExternalLink'; import Tag from '../../../src/tags/helpers/Tag'; @@ -12,7 +11,7 @@ describe('', () => { let wrapper; const mockFunction = () => ''; const ShortUrlsRowMenu = mockFunction; - const stateFlagTimeout = sinon.spy(); + const stateFlagTimeout = jest.fn(); const colorGenerator = { getColorForKey: mockFunction, setColorForKey: mockFunction, @@ -92,9 +91,9 @@ describe('', () => { const menu = col.find(ShortUrlsRowMenu); expect(menu).toHaveLength(1); - expect(stateFlagTimeout.called).toEqual(false); + expect(stateFlagTimeout).not.toHaveBeenCalled(); menu.simulate('copyToClipboard'); - expect(stateFlagTimeout.calledOnce).toEqual(true); + expect(stateFlagTimeout).toHaveBeenCalledTimes(1); }); it('shows copy hint when state prop is true', () => { diff --git a/test/short-urls/helpers/ShortUrlsRowMenu.test.js b/test/short-urls/helpers/ShortUrlsRowMenu.test.js index 6e73da0f..a17744d2 100644 --- a/test/short-urls/helpers/ShortUrlsRowMenu.test.js +++ b/test/short-urls/helpers/ShortUrlsRowMenu.test.js @@ -1,6 +1,5 @@ import React from 'react'; import { shallow } from 'enzyme'; -import * as sinon from 'sinon'; import { ButtonDropdown, DropdownItem } from 'reactstrap'; import createShortUrlsRowMenu from '../../../src/short-urls/helpers/ShortUrlsRowMenu'; import PreviewModal from '../../../src/short-urls/helpers/PreviewModal'; @@ -10,7 +9,7 @@ describe('', () => { let wrapper; const DeleteShortUrlModal = () => ''; const EditTagsModal = () => ''; - const onCopyToClipboard = sinon.spy(); + const onCopyToClipboard = jest.fn(); const selectedServer = { id: 'abc123' }; const shortUrl = { shortCode: 'abc123', diff --git a/test/short-urls/reducers/shortUrlCreation.test.js b/test/short-urls/reducers/shortUrlCreation.test.js index c9a4e0e3..c976721c 100644 --- a/test/short-urls/reducers/shortUrlCreation.test.js +++ b/test/short-urls/reducers/shortUrlCreation.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { CREATE_SHORT_URL_START, CREATE_SHORT_URL_ERROR, @@ -48,12 +47,12 @@ describe('shortUrlCreationReducer', () => { describe('createShortUrl', () => { const createApiClientMock = (result) => ({ - createShortUrl: sinon.fake.returns(result), + createShortUrl: jest.fn(() => result), }); - const dispatch = sinon.spy(); + const dispatch = jest.fn(); const getState = () => ({}); - afterEach(() => dispatch.resetHistory()); + afterEach(() => dispatch.mockReset()); it('calls API on success', async () => { const expectedDispatchCalls = 2; @@ -62,12 +61,12 @@ describe('shortUrlCreationReducer', () => { const dispatchable = createShortUrl(() => apiClientMock)({}); await dispatchable(dispatch, getState); + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(apiClientMock.createShortUrl.callCount).toEqual(1); - - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: CREATE_SHORT_URL_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: CREATE_SHORT_URL, result }]); + expect(apiClientMock.createShortUrl).toHaveBeenCalledTimes(1); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: CREATE_SHORT_URL_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: CREATE_SHORT_URL, result }]); }); it('throws on error', async () => { @@ -81,12 +80,12 @@ describe('shortUrlCreationReducer', () => { } catch (e) { expect(e).toEqual(error); } + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(apiClientMock.createShortUrl.callCount).toEqual(1); - - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: CREATE_SHORT_URL_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: CREATE_SHORT_URL_ERROR }]); + expect(apiClientMock.createShortUrl).toHaveBeenCalledTimes(1); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: CREATE_SHORT_URL_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: CREATE_SHORT_URL_ERROR }]); }); }); }); diff --git a/test/short-urls/reducers/shortUrlDeletion.test.js b/test/short-urls/reducers/shortUrlDeletion.test.js index b0289614..037d1cc6 100644 --- a/test/short-urls/reducers/shortUrlDeletion.test.js +++ b/test/short-urls/reducers/shortUrlDeletion.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { DELETE_SHORT_URL, DELETE_SHORT_URL_ERROR, DELETE_SHORT_URL_START, @@ -58,36 +57,37 @@ describe('shortUrlDeletionReducer', () => { }); describe('deleteShortUrl', () => { - const dispatch = sinon.spy(); - const getState = sinon.fake.returns({ selectedServer: {} }); + const dispatch = jest.fn(); + const getState = jest.fn().mockReturnValue({ selectedServer: {} }); afterEach(() => { - dispatch.resetHistory(); - getState.resetHistory(); + dispatch.mockReset(); + getState.mockClear(); }); it('dispatches proper actions if API client request succeeds', async () => { const apiClientMock = { - deleteShortUrl: sinon.fake.resolves(''), + deleteShortUrl: jest.fn(() => ''), }; const shortCode = 'abc123'; const expectedDispatchCalls = 2; await deleteShortUrl(() => apiClientMock)(shortCode)(dispatch, getState); + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: DELETE_SHORT_URL_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: DELETE_SHORT_URL, shortCode }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: DELETE_SHORT_URL_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: DELETE_SHORT_URL, shortCode }]); - expect(apiClientMock.deleteShortUrl.callCount).toEqual(1); - expect(apiClientMock.deleteShortUrl.getCall(0).args).toEqual([ shortCode ]); + expect(apiClientMock.deleteShortUrl).toHaveBeenCalledTimes(1); + expect(apiClientMock.deleteShortUrl.mock.calls[0]).toEqual([ shortCode ]); }); it('dispatches proper actions if API client request fails', async () => { const data = { foo: 'bar' }; const error = { response: { data } }; const apiClientMock = { - deleteShortUrl: sinon.fake.returns(Promise.reject(error)), + deleteShortUrl: jest.fn(() => Promise.reject(error)), }; const shortCode = 'abc123'; const expectedDispatchCalls = 2; @@ -97,13 +97,14 @@ describe('shortUrlDeletionReducer', () => { } catch (e) { expect(e).toEqual(error); } + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: DELETE_SHORT_URL_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: DELETE_SHORT_URL_ERROR, errorData: data }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: DELETE_SHORT_URL_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: DELETE_SHORT_URL_ERROR, errorData: data }]); - expect(apiClientMock.deleteShortUrl.callCount).toEqual(1); - expect(apiClientMock.deleteShortUrl.getCall(0).args).toEqual([ shortCode ]); + expect(apiClientMock.deleteShortUrl).toHaveBeenCalledTimes(1); + expect(apiClientMock.deleteShortUrl.mock.calls[0]).toEqual([ shortCode ]); }); }); }); diff --git a/test/short-urls/reducers/shortUrlsList.test.js b/test/short-urls/reducers/shortUrlsList.test.js index a3055a04..b7f5578a 100644 --- a/test/short-urls/reducers/shortUrlsList.test.js +++ b/test/short-urls/reducers/shortUrlsList.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { LIST_SHORT_URLS, LIST_SHORT_URLS_ERROR, @@ -73,42 +72,44 @@ describe('shortUrlsListReducer', () => { }); describe('listShortUrls', () => { - const dispatch = sinon.spy(); - const getState = sinon.fake.returns({ selectedServer: {} }); + const dispatch = jest.fn(); + const getState = jest.fn().mockReturnValue({ selectedServer: {} }); afterEach(() => { - dispatch.resetHistory(); - getState.resetHistory(); + dispatch.mockReset(); + getState.mockClear(); }); it('dispatches proper actions if API client request succeeds', async () => { const apiClientMock = { - listShortUrls: sinon.fake.resolves([]), + listShortUrls: jest.fn().mockResolvedValue([]), }; const expectedDispatchCalls = 2; await listShortUrls(() => apiClientMock)()(dispatch, getState); + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: LIST_SHORT_URLS_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: LIST_SHORT_URLS, shortUrls: [], params: {} }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: LIST_SHORT_URLS_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: LIST_SHORT_URLS, shortUrls: [], params: {} }]); - expect(apiClientMock.listShortUrls.callCount).toEqual(1); + expect(apiClientMock.listShortUrls).toHaveBeenCalledTimes(1); }); it('dispatches proper actions if API client request fails', async () => { const apiClientMock = { - listShortUrls: sinon.fake.rejects(), + listShortUrls: jest.fn().mockRejectedValue(), }; const expectedDispatchCalls = 2; await listShortUrls(() => apiClientMock)()(dispatch, getState); + const [ firstDispatchCallArgs, secondDispatchCallArgs ] = dispatch.mock.calls; - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: LIST_SHORT_URLS_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: LIST_SHORT_URLS_ERROR, params: {} }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(firstDispatchCallArgs).toEqual([{ type: LIST_SHORT_URLS_START }]); + expect(secondDispatchCallArgs).toEqual([{ type: LIST_SHORT_URLS_ERROR, params: {} }]); - expect(apiClientMock.listShortUrls.callCount).toEqual(1); + expect(apiClientMock.listShortUrls).toHaveBeenCalledTimes(1); }); }); }); diff --git a/test/tags/TagsList.test.js b/test/tags/TagsList.test.js index df914f7b..60c9b234 100644 --- a/test/tags/TagsList.test.js +++ b/test/tags/TagsList.test.js @@ -1,7 +1,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { identity } from 'ramda'; -import * as sinon from 'sinon'; import createTagsList from '../../src/tags/TagsList'; import MuttedMessage from '../../src/utils/MuttedMessage'; import SearchField from '../../src/utils/SearchField'; @@ -9,7 +8,7 @@ import { rangeOf } from '../../src/utils/utils'; describe('', () => { let wrapper; - const filterTags = sinon.spy(); + const filterTags = jest.fn(); const TagCard = () => ''; const createWrapper = (tagsList) => { const params = { serverId: '1' }; @@ -24,7 +23,7 @@ describe('', () => { afterEach(() => { wrapper && wrapper.unmount(); - filterTags.resetHistory(); + filterTags.mockReset(); }); it('shows a loading message when tags are being loaded', () => { @@ -67,11 +66,11 @@ describe('', () => { const searchField = wrapper.find(SearchField); expect(searchField).toHaveLength(1); - expect(filterTags.callCount).toEqual(0); + expect(filterTags).not.toHaveBeenCalled(); searchField.simulate('change'); setImmediate(() => { - expect(filterTags.callCount).toEqual(1); + expect(filterTags).toHaveBeenCalledTimes(1); done(); }); }); diff --git a/test/tags/helpers/DeleteTagConfirmModal.test.js b/test/tags/helpers/DeleteTagConfirmModal.test.js index 3c1edc88..73d6de29 100644 --- a/test/tags/helpers/DeleteTagConfirmModal.test.js +++ b/test/tags/helpers/DeleteTagConfirmModal.test.js @@ -1,14 +1,13 @@ import React from 'react'; import { shallow } from 'enzyme'; -import * as sinon from 'sinon'; import { Modal, ModalBody, ModalFooter } from 'reactstrap'; import DeleteTagConfirmModal from '../../../src/tags/helpers/DeleteTagConfirmModal'; describe('', () => { let wrapper; const tag = 'nodejs'; - const deleteTag = sinon.spy(); - const tagDeleted = sinon.spy(); + const deleteTag = jest.fn(); + const tagDeleted = jest.fn(); const createWrapper = (tagDelete) => { wrapper = shallow( ', () => { afterEach(() => { wrapper && wrapper.unmount(); - deleteTag.resetHistory(); - tagDeleted.resetHistory(); + deleteTag.mockReset(); + tagDeleted.mockReset(); }); it('asks confirmation for provided tag to be deleted', () => { @@ -63,8 +62,8 @@ describe('', () => { const delBtn = footer.find('.btn-danger'); delBtn.simulate('click'); - expect(deleteTag.calledOnce).toEqual(true); - expect(deleteTag.calledWith(tag)).toEqual(true); + expect(deleteTag).toHaveBeenCalledTimes(1); + expect(deleteTag).toHaveBeenCalledWith(tag); }); it('does no further actions when modal is closed without deleting tag', () => { @@ -72,7 +71,7 @@ describe('', () => { const modal = wrapper.find(Modal); modal.simulate('closed'); - expect(tagDeleted.called).toEqual(false); + expect(tagDeleted).not.toHaveBeenCalled(); }); it('notifies tag to be deleted when modal is closed after deleting tag', () => { @@ -81,7 +80,7 @@ describe('', () => { wrapper.instance().tagWasDeleted = true; modal.simulate('closed'); - expect(tagDeleted.calledOnce).toEqual(true); - expect(tagDeleted.calledWith(tag)).toEqual(true); + expect(tagDeleted).toHaveBeenCalledTimes(1); + expect(tagDeleted).toHaveBeenCalledWith(tag); }); }); diff --git a/test/tags/reducers/tagDelete.test.js b/test/tags/reducers/tagDelete.test.js index 8915b14f..d12ec481 100644 --- a/test/tags/reducers/tagDelete.test.js +++ b/test/tags/reducers/tagDelete.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { DELETE_TAG_START, DELETE_TAG_ERROR, @@ -42,12 +41,12 @@ describe('tagDeleteReducer', () => { describe('deleteTag', () => { const createApiClientMock = (result) => ({ - deleteTags: sinon.fake.returns(result), + deleteTags: jest.fn(() => result), }); - const dispatch = sinon.spy(); + const dispatch = jest.fn(); const getState = () => ({}); - afterEach(() => dispatch.resetHistory()); + afterEach(() => dispatch.mockReset()); it('calls API on success', async () => { const expectedDispatchCalls = 2; @@ -57,12 +56,12 @@ describe('tagDeleteReducer', () => { await dispatchable(dispatch, getState); - expect(apiClientMock.deleteTags.callCount).toEqual(1); - expect(apiClientMock.deleteTags.getCall(0).args).toEqual([[ tag ]]); + expect(apiClientMock.deleteTags).toHaveBeenCalledTimes(1); + expect(apiClientMock.deleteTags).toHaveBeenNthCalledWith(1, [ tag ]); - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: DELETE_TAG_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: DELETE_TAG }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_TAG_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: DELETE_TAG }); }); it('throws on error', async () => { @@ -78,12 +77,12 @@ describe('tagDeleteReducer', () => { expect(e).toEqual(error); } - expect(apiClientMock.deleteTags.callCount).toEqual(1); - expect(apiClientMock.deleteTags.getCall(0).args).toEqual([[ tag ]]); + expect(apiClientMock.deleteTags).toHaveBeenCalledTimes(1); + expect(apiClientMock.deleteTags).toHaveBeenNthCalledWith(1, [ tag ]); - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: DELETE_TAG_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: DELETE_TAG_ERROR }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_TAG_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: DELETE_TAG_ERROR }); }); }); }); diff --git a/test/tags/reducers/tagEdit.test.js b/test/tags/reducers/tagEdit.test.js index 14a63353..035ced44 100644 --- a/test/tags/reducers/tagEdit.test.js +++ b/test/tags/reducers/tagEdit.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { EDIT_TAG_START, EDIT_TAG_ERROR, @@ -46,17 +45,17 @@ describe('tagEditReducer', () => { describe('editTag', () => { const createApiClientMock = (result) => ({ - editTag: sinon.fake.returns(result), + editTag: jest.fn(() => result), }); const colorGenerator = { - setColorForKey: sinon.spy(), + setColorForKey: jest.fn(), }; - const dispatch = sinon.spy(); + const dispatch = jest.fn(); const getState = () => ({}); afterEach(() => { - colorGenerator.setColorForKey.resetHistory(); - dispatch.resetHistory(); + colorGenerator.setColorForKey.mockReset(); + dispatch.mockReset(); }); it('calls API on success', async () => { @@ -69,19 +68,18 @@ describe('tagEditReducer', () => { await dispatchable(dispatch, getState); - expect(apiClientMock.editTag.callCount).toEqual(1); - expect(apiClientMock.editTag.getCall(0).args).toEqual([ oldName, newName ]); + expect(apiClientMock.editTag).toHaveBeenCalledTimes(1); + expect(apiClientMock.editTag).toHaveBeenCalledWith(oldName, newName); - expect(colorGenerator.setColorForKey.callCount).toEqual(1); - expect(colorGenerator.setColorForKey.getCall(0).args).toEqual([ newName, color ]); + expect(colorGenerator.setColorForKey).toHaveBeenCalledTimes(1); + expect(colorGenerator.setColorForKey).toHaveBeenCalledWith(newName, color); - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: EDIT_TAG_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: EDIT_TAG, oldName, newName }]); + expect(dispatch).toHaveBeenCalledTimes(expectedDispatchCalls); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_TAG_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_TAG, oldName, newName }); }); it('throws on error', async () => { - const expectedDispatchCalls = 2; const error = 'Error'; const oldName = 'foo'; const newName = 'bar'; @@ -95,14 +93,14 @@ describe('tagEditReducer', () => { expect(e).toEqual(error); } - expect(apiClientMock.editTag.callCount).toEqual(1); - expect(apiClientMock.editTag.getCall(0).args).toEqual([ oldName, newName ]); + expect(apiClientMock.editTag).toHaveBeenCalledTimes(1); + expect(apiClientMock.editTag).toHaveBeenCalledWith(oldName, newName); - expect(colorGenerator.setColorForKey.callCount).toEqual(0); + expect(colorGenerator.setColorForKey).not.toHaveBeenCalled(); - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.getCall(0).args).toEqual([{ type: EDIT_TAG_START }]); - expect(dispatch.getCall(1).args).toEqual([{ type: EDIT_TAG_ERROR }]); + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_TAG_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_TAG_ERROR }); }); }); }); diff --git a/test/utils/SortingDropdown.test.js b/test/utils/SortingDropdown.test.js index 2722225c..1547d31c 100644 --- a/test/utils/SortingDropdown.test.js +++ b/test/utils/SortingDropdown.test.js @@ -4,7 +4,6 @@ import { DropdownItem } from 'reactstrap'; import { identity, values } from 'ramda'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons'; -import * as sinon from 'sinon'; import SortingDropdown from '../../src/utils/SortingDropdown'; describe('', () => { @@ -44,35 +43,35 @@ describe('', () => { }); it('triggers change function when item is clicked and no order field was provided', () => { - const onChange = sinon.spy(); + const onChange = jest.fn(); const wrapper = createWrapper({ onChange }); const firstItem = wrapper.find(DropdownItem).first(); firstItem.simulate('click'); - expect(onChange.callCount).toEqual(1); - expect(onChange.calledWith('foo', 'ASC')).toEqual(true); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith('foo', 'ASC'); }); it('triggers change function when item is clicked and an order field was provided', () => { - const onChange = sinon.spy(); + const onChange = jest.fn(); const wrapper = createWrapper({ onChange, orderField: 'baz', orderDir: 'ASC' }); const firstItem = wrapper.find(DropdownItem).first(); firstItem.simulate('click'); - expect(onChange.callCount).toEqual(1); - expect(onChange.calledWith('foo', 'ASC')).toEqual(true); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith('foo', 'ASC'); }); it('updates order dir when already selected item is clicked', () => { - const onChange = sinon.spy(); + const onChange = jest.fn(); const wrapper = createWrapper({ onChange, orderField: 'foo', orderDir: 'ASC' }); const firstItem = wrapper.find(DropdownItem).first(); firstItem.simulate('click'); - expect(onChange.callCount).toEqual(1); - expect(onChange.calledWith('foo', 'DESC')).toEqual(true); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith('foo', 'DESC'); }); }); diff --git a/test/utils/services/ColorGenerator.test.js b/test/utils/services/ColorGenerator.test.js index 937c3b2a..71546d17 100644 --- a/test/utils/services/ColorGenerator.test.js +++ b/test/utils/services/ColorGenerator.test.js @@ -1,16 +1,15 @@ -import * as sinon from 'sinon'; import ColorGenerator from '../../../src/utils/services/ColorGenerator'; describe('ColorGenerator', () => { let colorGenerator; const storageMock = { - set: sinon.fake(), - get: sinon.fake.returns(undefined), + set: jest.fn(), + get: jest.fn(), }; beforeEach(() => { - storageMock.set.resetHistory(); - storageMock.get.resetHistory(); + storageMock.set.mockReset(); + storageMock.get.mockReset(); colorGenerator = new ColorGenerator(storageMock); }); @@ -21,14 +20,14 @@ describe('ColorGenerator', () => { colorGenerator.setColorForKey('foo', color); expect(colorGenerator.getColorForKey('foo')).toEqual(color); - expect(storageMock.set.callCount).toEqual(1); - expect(storageMock.get.callCount).toEqual(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.get).toHaveBeenCalledTimes(1); }); it('generates a random color when none is available for requested key', () => { expect(colorGenerator.getColorForKey('bar')).toEqual(expect.stringMatching(/^#(?:[0-9a-fA-F]{6})$/)); - expect(storageMock.set.callCount).toEqual(1); - expect(storageMock.get.callCount).toEqual(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.get).toHaveBeenCalledTimes(1); }); it('trims and lower cases keys before trying to match', () => { @@ -42,7 +41,7 @@ describe('ColorGenerator', () => { expect(colorGenerator.getColorForKey('FOO')).toEqual(color); expect(colorGenerator.getColorForKey('FOO ')).toEqual(color); expect(colorGenerator.getColorForKey(' FoO ')).toEqual(color); - expect(storageMock.set.callCount).toEqual(1); - expect(storageMock.get.callCount).toEqual(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.get).toHaveBeenCalledTimes(1); }); }); diff --git a/test/utils/services/ShlinkApiClient.test.js b/test/utils/services/ShlinkApiClient.test.js index f41d324b..024febf3 100644 --- a/test/utils/services/ShlinkApiClient.test.js +++ b/test/utils/services/ShlinkApiClient.test.js @@ -1,5 +1,3 @@ -import sinon from 'sinon'; -import { head, last } from 'ramda'; import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient'; describe('ShlinkApiClient', () => { @@ -35,23 +33,21 @@ describe('ShlinkApiClient', () => { }); it('removes all empty options', async () => { - const axiosSpy = sinon.spy(createAxiosMock({ data: shortUrl })); + const axiosSpy = jest.fn(createAxiosMock({ data: shortUrl })); const { createShortUrl } = new ShlinkApiClient(axiosSpy); await createShortUrl( { foo: 'bar', empty: undefined, anotherEmpty: null } ); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); - expect(axiosArgs.data).toEqual({ foo: 'bar' }); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ data: { foo: 'bar' } })); }); }); describe('getShortUrlVisits', () => { it('properly returns short URL visits', async () => { const expectedVisits = [ 'foo', 'bar' ]; - const axiosSpy = sinon.spy(createAxiosMock({ + const axiosSpy = jest.fn(createAxiosMock({ data: { visits: { data: expectedVisits, @@ -61,55 +57,55 @@ describe('ShlinkApiClient', () => { const { getShortUrlVisits } = new ShlinkApiClient(axiosSpy); const actualVisits = await getShortUrlVisits('abc123', {}); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); expect({ data: expectedVisits }).toEqual(actualVisits); - expect(axiosArgs.url).toContain('/short-urls/abc123/visits'); - expect(axiosArgs.method).toEqual('GET'); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/short-urls/abc123/visits', + method: 'GET', + })); }); }); describe('getShortUrl', () => { it('properly returns short URL', async () => { const expectedShortUrl = { foo: 'bar' }; - const axiosSpy = sinon.spy(createAxiosMock({ + const axiosSpy = jest.fn(createAxiosMock({ data: expectedShortUrl, })); const { getShortUrl } = new ShlinkApiClient(axiosSpy); const result = await getShortUrl('abc123'); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); expect(expectedShortUrl).toEqual(result); - expect(axiosArgs.url).toContain('/short-urls/abc123'); - expect(axiosArgs.method).toEqual('GET'); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/short-urls/abc123', + method: 'GET', + })); }); }); describe('updateShortUrlTags', () => { it('properly updates short URL tags', async () => { const expectedTags = [ 'foo', 'bar' ]; - const axiosSpy = sinon.spy(createAxiosMock({ + const axiosSpy = jest.fn(createAxiosMock({ data: { tags: expectedTags }, })); const { updateShortUrlTags } = new ShlinkApiClient(axiosSpy); const result = await updateShortUrlTags('abc123', expectedTags); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); expect(expectedTags).toEqual(result); - expect(axiosArgs.url).toContain('/short-urls/abc123/tags'); - expect(axiosArgs.method).toEqual('PUT'); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/short-urls/abc123/tags', + method: 'PUT', + })); }); }); describe('listTags', () => { it('properly returns list of tags', async () => { const expectedTags = [ 'foo', 'bar' ]; - const axiosSpy = sinon.spy(createAxiosMock({ + const axiosSpy = jest.fn(createAxiosMock({ data: { tags: { data: expectedTags }, }, @@ -117,28 +113,25 @@ describe('ShlinkApiClient', () => { const { listTags } = new ShlinkApiClient(axiosSpy); const result = await listTags(); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); expect(expectedTags).toEqual(result); - expect(axiosArgs.url).toContain('/tags'); - expect(axiosArgs.method).toEqual('GET'); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ url: '/tags', method: 'GET' })); }); }); describe('deleteTags', () => { it('properly deletes provided tags', async () => { const tags = [ 'foo', 'bar' ]; - const axiosSpy = sinon.spy(createAxiosMock({})); + const axiosSpy = jest.fn(createAxiosMock({})); const { deleteTags } = new ShlinkApiClient(axiosSpy); await deleteTags(tags); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); - expect(axiosArgs.url).toContain('/tags'); - expect(axiosArgs.method).toEqual('DELETE'); - expect(axiosArgs.params).toEqual({ tags }); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/tags', + method: 'DELETE', + params: { tags }, + })); }); }); @@ -146,30 +139,30 @@ describe('ShlinkApiClient', () => { it('properly edits provided tag', async () => { const oldName = 'foo'; const newName = 'bar'; - const axiosSpy = sinon.spy(createAxiosMock({})); + const axiosSpy = jest.fn(createAxiosMock({})); const { editTag } = new ShlinkApiClient(axiosSpy); await editTag(oldName, newName); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); - expect(axiosArgs.url).toContain('/tags'); - expect(axiosArgs.method).toEqual('PUT'); - expect(axiosArgs.data).toEqual({ oldName, newName }); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/tags', + method: 'PUT', + data: { oldName, newName }, + })); }); }); describe('deleteShortUrl', () => { it('properly deletes provided short URL', async () => { - const axiosSpy = sinon.spy(createAxiosMock({})); + const axiosSpy = jest.fn(createAxiosMock({})); const { deleteShortUrl } = new ShlinkApiClient(axiosSpy); await deleteShortUrl('abc123'); - const lastAxiosCall = last(axiosSpy.getCalls()); - const axiosArgs = head(lastAxiosCall.args); - expect(axiosArgs.url).toContain('/short-urls/abc123'); - expect(axiosArgs.method).toEqual('DELETE'); + expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({ + url: '/short-urls/abc123', + method: 'DELETE', + })); }); }); }); diff --git a/test/utils/services/Storage.test.js b/test/utils/services/Storage.test.js index b244ffcb..290a5c09 100644 --- a/test/utils/services/Storage.test.js +++ b/test/utils/services/Storage.test.js @@ -1,16 +1,15 @@ -import * as sinon from 'sinon'; import Storage from '../../../src/utils/services/Storage'; describe('Storage', () => { const localStorageMock = { - getItem: sinon.fake((key) => key === 'shlink.foo' ? JSON.stringify({ foo: 'bar' }) : null), - setItem: sinon.spy(), + getItem: jest.fn((key) => key === 'shlink.foo' ? JSON.stringify({ foo: 'bar' }) : null), + setItem: jest.fn(), }; let storage; beforeEach(() => { - localStorageMock.getItem.resetHistory(); - localStorageMock.setItem.resetHistory(); + localStorageMock.getItem.mockClear(); + localStorageMock.setItem.mockReset(); storage = new Storage(localStorageMock); }); @@ -21,18 +20,15 @@ describe('Storage', () => { storage.set('foo', value); - expect(localStorageMock.setItem.callCount).toEqual(1); - expect(localStorageMock.setItem.getCall(0).args).toEqual([ - 'shlink.foo', - JSON.stringify(value), - ]); + expect(localStorageMock.setItem).toHaveBeenCalledTimes(1); + expect(localStorageMock.setItem).toHaveBeenCalledWith('shlink.foo', JSON.stringify(value)); }); }); describe('get', () => { it('fetches item from local storage', () => { storage.get('foo'); - expect(localStorageMock.getItem.callCount).toEqual(1); + expect(localStorageMock.getItem).toHaveBeenCalledTimes(1); }); it('returns parsed value when requested value is found in local storage', () => { diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index 3a9c507a..94ac847c 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import L from 'leaflet'; import marker2x from 'leaflet/dist/images/marker-icon-2x.png'; import marker from 'leaflet/dist/images/marker-icon.png'; @@ -14,19 +13,18 @@ import { describe('utils', () => { describe('stateFlagTimeout', () => { it('sets state and initializes timeout with provided delay', () => { - const setTimeout = sinon.fake((callback) => callback()); - const setState = sinon.spy(); + const setTimeout = jest.fn((callback) => callback()); + const setState = jest.fn(); const stateFlagTimeout = stateFlagTimeoutFactory(setTimeout); const delay = 5000; - const expectedSetStateCalls = 2; stateFlagTimeout(setState, 'foo', false, delay); - expect(setState.callCount).toEqual(expectedSetStateCalls); - expect(setState.getCall(0).args).toEqual([{ foo: false }]); - expect(setState.getCall(1).args).toEqual([{ foo: true }]); - expect(setTimeout.callCount).toEqual(1); - expect(setTimeout.getCall(0).args[1]).toEqual(delay); + expect(setState).toHaveBeenCalledTimes(2); + expect(setState).toHaveBeenNthCalledWith(1, { foo: false }); + expect(setState).toHaveBeenNthCalledWith(2, { foo: true }); + expect(setTimeout).toHaveBeenCalledTimes(1); + expect(setTimeout).toHaveBeenCalledWith(expect.anything(), delay); }); }); diff --git a/test/visits/ShortUrlVisits.test.js b/test/visits/ShortUrlVisits.test.js index 29e1e932..82930585 100644 --- a/test/visits/ShortUrlVisits.test.js +++ b/test/visits/ShortUrlVisits.test.js @@ -2,7 +2,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { identity } from 'ramda'; import { Card } from 'reactstrap'; -import * as sinon from 'sinon'; import createShortUrlVisits from '../../src/visits/ShortUrlVisits'; import MutedMessage from '../../src/utils/MuttedMessage'; import GraphCard from '../../src/visits/GraphCard'; @@ -14,7 +13,7 @@ describe('', () => { const processStatsFromVisits = () => ( { os: {}, browsers: {}, referrers: {}, countries: {}, cities: {}, citiesForMap: {} } ); - const getShortUrlVisitsMock = sinon.spy(); + const getShortUrlVisitsMock = jest.fn(); const match = { params: { shortCode: 'abc123' }, }; @@ -37,11 +36,8 @@ describe('', () => { }; afterEach(() => { - getShortUrlVisitsMock.resetHistory(); - - if (wrapper) { - wrapper.unmount(); - } + getShortUrlVisitsMock.mockReset(); + wrapper && wrapper.unmount(); }); it('renders a preloader when visits are loading', () => { @@ -88,13 +84,12 @@ describe('', () => { it('reloads visits when selected dates change', () => { const wrapper = createComponent({ loading: false, error: false, visits: [{}, {}, {}] }); const dateInput = wrapper.find(DateInput).first(); - const expectedGetShortUrlVisitsCalls = 4; dateInput.simulate('change', '2016-01-01T00:00:00+01:00'); dateInput.simulate('change', '2016-01-02T00:00:00+01:00'); dateInput.simulate('change', '2016-01-03T00:00:00+01:00'); - expect(getShortUrlVisitsMock.callCount).toEqual(expectedGetShortUrlVisitsCalls); + expect(getShortUrlVisitsMock).toHaveBeenCalledTimes(4); expect(wrapper.state('startDate')).toEqual('2016-01-03T00:00:00+01:00'); }); diff --git a/test/visits/reducers/shortUrlDetail.test.js b/test/visits/reducers/shortUrlDetail.test.js index 218ca1c3..fb0555c9 100644 --- a/test/visits/reducers/shortUrlDetail.test.js +++ b/test/visits/reducers/shortUrlDetail.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { getShortUrlDetail, GET_SHORT_URL_DETAIL_START, @@ -36,49 +35,34 @@ describe('shortUrlDetailReducer', () => { describe('getShortUrlDetail', () => { const buildApiClientMock = (returned) => ({ - getShortUrl: sinon.fake.returns(returned), + getShortUrl: jest.fn(() => returned), }); - const dispatchMock = sinon.spy(); + const dispatchMock = jest.fn(); const getState = () => ({}); - beforeEach(() => dispatchMock.resetHistory()); + beforeEach(() => dispatchMock.mockReset()); it('dispatches start and error when promise is rejected', async () => { const ShlinkApiClient = buildApiClientMock(Promise.reject()); - const expectedDispatchCalls = 2; await getShortUrlDetail(() => ShlinkApiClient)('abc123')(dispatchMock, getState); - const [ firstCallArg ] = dispatchMock.getCall(0).args; - const { type: firstCallType } = firstCallArg; - - const [ secondCallArg ] = dispatchMock.getCall(1).args; - const { type: secondCallType } = secondCallArg; - - expect(dispatchMock.callCount).toEqual(expectedDispatchCalls); - expect(ShlinkApiClient.getShortUrl.callCount).toEqual(1); - expect(firstCallType).toEqual(GET_SHORT_URL_DETAIL_START); - expect(secondCallType).toEqual(GET_SHORT_URL_DETAIL_ERROR); + expect(dispatchMock).toHaveBeenCalledTimes(2); + expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START }); + expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_DETAIL_ERROR }); + expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1); }); it('dispatches start and success when promise is resolved', async () => { const resolvedShortUrl = { longUrl: 'foo', shortCode: 'bar' }; const ShlinkApiClient = buildApiClientMock(Promise.resolve(resolvedShortUrl)); - const expectedDispatchCalls = 2; await getShortUrlDetail(() => ShlinkApiClient)('abc123')(dispatchMock, getState); - const [ firstCallArg ] = dispatchMock.getCall(0).args; - const { type: firstCallType } = firstCallArg; - - const [ secondCallArg ] = dispatchMock.getCall(1).args; - const { type: secondCallType, shortUrl } = secondCallArg; - - expect(dispatchMock.callCount).toEqual(expectedDispatchCalls); - expect(ShlinkApiClient.getShortUrl.callCount).toEqual(1); - expect(firstCallType).toEqual(GET_SHORT_URL_DETAIL_START); - expect(secondCallType).toEqual(GET_SHORT_URL_DETAIL); - expect(shortUrl).toEqual(resolvedShortUrl); + expect(dispatchMock).toHaveBeenCalledTimes(2); + expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START }); + expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_DETAIL, shortUrl: resolvedShortUrl }); + expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1); }); }); }); diff --git a/test/visits/reducers/shortUrlVisits.test.js b/test/visits/reducers/shortUrlVisits.test.js index 0f714675..9bc285ca 100644 --- a/test/visits/reducers/shortUrlVisits.test.js +++ b/test/visits/reducers/shortUrlVisits.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { getShortUrlVisits, cancelGetShortUrlVisits, @@ -53,57 +52,42 @@ describe('shortUrlVisitsReducer', () => { describe('getShortUrlVisits', () => { const buildApiClientMock = (returned) => ({ - getShortUrlVisits: typeof returned === 'function' ? sinon.fake(returned) : sinon.fake.returns(returned), + getShortUrlVisits: jest.fn(typeof returned === 'function' ? returned : () => returned), }); - const dispatchMock = sinon.spy(); + const dispatchMock = jest.fn(); const getState = () => ({ shortUrlVisits: { cancelVisits: false }, }); - beforeEach(() => dispatchMock.resetHistory()); + beforeEach(() => dispatchMock.mockReset()); it('dispatches start and error when promise is rejected', async () => { const ShlinkApiClient = buildApiClientMock(Promise.reject()); - const expectedDispatchCalls = 2; await getShortUrlVisits(() => ShlinkApiClient)('abc123')(dispatchMock, getState); - const [ firstCallArg ] = dispatchMock.getCall(0).args; - const { type: firstCallType } = firstCallArg; - - const [ secondCallArg ] = dispatchMock.getCall(1).args; - const { type: secondCallType } = secondCallArg; - - expect(dispatchMock.callCount).toEqual(expectedDispatchCalls); - expect(ShlinkApiClient.getShortUrlVisits.callCount).toEqual(1); - expect(firstCallType).toEqual(GET_SHORT_URL_VISITS_START); - expect(secondCallType).toEqual(GET_SHORT_URL_VISITS_ERROR); + expect(dispatchMock).toHaveBeenCalledTimes(2); + expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_VISITS_START }); + expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_VISITS_ERROR }); + expect(ShlinkApiClient.getShortUrlVisits).toHaveBeenCalledTimes(1); }); it('dispatches start and success when promise is resolved', async () => { - const resolvedVisits = [{}, {}]; + const visits = [{}, {}]; const ShlinkApiClient = buildApiClientMock(Promise.resolve({ - data: resolvedVisits, + data: visits, pagination: { currentPage: 1, pagesCount: 1, }, })); - const expectedDispatchCalls = 2; await getShortUrlVisits(() => ShlinkApiClient)('abc123')(dispatchMock, getState); - const [ firstCallArg ] = dispatchMock.getCall(0).args; - const { type: firstCallType } = firstCallArg; - - const [ secondCallArg ] = dispatchMock.getCall(1).args; - const { type: secondCallType, visits } = secondCallArg; - - expect(dispatchMock.callCount).toEqual(expectedDispatchCalls); - expect(ShlinkApiClient.getShortUrlVisits.callCount).toEqual(1); - expect(firstCallType).toEqual(GET_SHORT_URL_VISITS_START); - expect(secondCallType).toEqual(GET_SHORT_URL_VISITS); - expect(visits).toEqual(resolvedVisits); + expect(dispatchMock).toHaveBeenCalledTimes(2); + expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_VISITS_START }); + expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_VISITS, visits }); + expect(ShlinkApiClient.getShortUrlVisits).toHaveBeenCalledTimes(1); }); it('performs multiple API requests when response contains more pages', async () => { @@ -119,11 +103,10 @@ describe('shortUrlVisitsReducer', () => { await getShortUrlVisits(() => ShlinkApiClient)('abc123')(dispatchMock, getState); - const [ secondCallArg ] = dispatchMock.getCall(1).args; - const { visits } = secondCallArg; - - expect(ShlinkApiClient.getShortUrlVisits.callCount).toEqual(expectedRequests); - expect(visits).toEqual([{}, {}, {}, {}, {}, {}]); + expect(ShlinkApiClient.getShortUrlVisits).toHaveBeenCalledTimes(expectedRequests); + expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ + visits: [{}, {}, {}, {}, {}, {}], + })); }); });