diff --git a/CHANGELOG.md b/CHANGELOG.md index df4e8429..dd0a497c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), #### Changed * [#125](https://github.com/shlinkio/shlink-web-client/issues/125) Refactored reducers to replace `switch` statements by `handleActions` from [redux-actions](https://github.com/redux-utilities/redux-actions). +* [#116](https://github.com/shlinkio/shlink-web-client/issues/116) Removed sinon in favor of jest mocks. #### Deprecated diff --git a/package-lock.json b/package-lock.json index c1a0565e..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", @@ -4296,6 +4253,7 @@ "anymatch": "^2.0.0", "async-each": "^1.0.0", "braces": "^2.3.0", + "fsevents": "^1.2.2", "glob-parent": "^3.1.0", "inherits": "^2.0.1", "is-binary-path": "^1.0.0", @@ -7042,6 +7000,535 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", + "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, "fstream": { "version": "1.0.11", "resolved": "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz", @@ -9725,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", @@ -9966,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", @@ -10057,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", @@ -10726,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", @@ -14185,6 +14641,7 @@ "capture-exit": "^1.2.0", "exec-sh": "^0.2.0", "fb-watchman": "^2.0.0", + "fsevents": "^1.2.3", "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5", @@ -14668,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", @@ -15843,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", @@ -16137,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/App.test.js b/test/App.test.js index c9df2d9c..428f3b1d 100644 --- a/test/App.test.js +++ b/test/App.test.js @@ -19,7 +19,6 @@ describe('', () => { it('renders app main routes', () => { const routes = wrapper.find(Route); - const expectedRoutesCount = 4; const expectedPaths = [ '/server/create', '/', @@ -27,7 +26,7 @@ describe('', () => { ]; expect.assertions(expectedPaths.length + 1); - expect(routes).toHaveLength(expectedRoutesCount); + expect(routes).toHaveLength(4); expectedPaths.forEach((path, index) => { expect(routes.at(index).prop('path')).toEqual(path); }); diff --git a/test/common/AsideMenu.test.js b/test/common/AsideMenu.test.js index 75500821..4541b00d 100644 --- a/test/common/AsideMenu.test.js +++ b/test/common/AsideMenu.test.js @@ -16,9 +16,8 @@ describe('', () => { it('contains links to different sections', () => { const links = wrapped.find(NavLink); - const expectedLength = 3; - expect(links).toHaveLength(expectedLength); + expect(links).toHaveLength(3); links.forEach((link) => expect(link.prop('to')).toContain('abc123')); }); diff --git a/test/common/Home.test.js b/test/common/Home.test.js index 83e01ba2..6fb094a5 100644 --- a/test/common/Home.test.js +++ b/test/common/Home.test.js @@ -1,7 +1,6 @@ import { shallow } from 'enzyme'; import { values } from 'ramda'; import React from 'react'; -import * as sinon from 'sinon'; import Home from '../../src/common/Home'; describe('', () => { @@ -28,11 +27,11 @@ describe('', () => { }); it('resets selected server when mounted', () => { - const resetSelectedServer = sinon.spy(); + const resetSelectedServer = jest.fn(); - expect(resetSelectedServer.called).toEqual(false); + expect(resetSelectedServer).not.toHaveBeenCalled(); createComponent({ resetSelectedServer }); - expect(resetSelectedServer.called).toEqual(true); + expect(resetSelectedServer).toHaveBeenCalled(); }); it('shows link to create server when no servers exist', () => { diff --git a/test/common/ScrollToTop.test.js b/test/common/ScrollToTop.test.js index 77d428e5..b49450db 100644 --- a/test/common/ScrollToTop.test.js +++ b/test/common/ScrollToTop.test.js @@ -1,12 +1,11 @@ import React from 'react'; import { shallow } from 'enzyme'; -import * as sinon from 'sinon'; import createScrollToTop from '../../src/common/ScrollToTop'; describe('', () => { let wrapper; const window = { - scrollTo: sinon.spy(), + scrollTo: jest.fn(), }; beforeEach(() => { @@ -17,13 +16,13 @@ describe('', () => { afterEach(() => { wrapper.unmount(); - window.scrollTo.resetHistory(); + window.scrollTo.mockReset(); }); it('just renders children', () => expect(wrapper.text()).toEqual('Foobar')); it('scrolls to top when location changes', () => { wrapper.instance().componentDidUpdate({ location: { href: 'bar' } }); - expect(window.scrollTo.calledOnce).toEqual(true); + expect(window.scrollTo).toHaveBeenCalledTimes(1); }); }); diff --git a/test/servers/CreateServer.test.js b/test/servers/CreateServer.test.js index ed6aff67..69a7888e 100644 --- a/test/servers/CreateServer.test.js +++ b/test/servers/CreateServer.test.js @@ -1,20 +1,18 @@ import React from 'react'; import { shallow } from 'enzyme'; import { identity } from 'ramda'; -import sinon from 'sinon'; import createServerConstruct from '../../src/servers/CreateServer'; describe('', () => { let wrapper; const ImportServersBtn = () => ''; - const createServerMock = sinon.fake(); + const createServerMock = jest.fn(); const historyMock = { - push: sinon.fake(), + push: jest.fn(), }; beforeEach(() => { - createServerMock.resetHistory(); - historyMock.push.resetHistory(); + createServerMock.mockReset(); const CreateServer = createServerConstruct(ImportServersBtn); @@ -44,8 +42,8 @@ describe('', () => { return ''; } }); - expect(createServerMock.callCount).toEqual(1); - expect(historyMock.push.callCount).toEqual(1); + expect(createServerMock).toHaveBeenCalledTimes(1); + expect(historyMock.push).toHaveBeenCalledTimes(1); }); it('updates state when inputs are changed', () => { diff --git a/test/servers/DeleteServerModal.test.js b/test/servers/DeleteServerModal.test.js index ab06004d..21f938c7 100644 --- a/test/servers/DeleteServerModal.test.js +++ b/test/servers/DeleteServerModal.test.js @@ -1,20 +1,19 @@ import React from 'react'; import { shallow } from 'enzyme'; -import sinon from 'sinon'; import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import DeleteServerModal from '../../src/servers/DeleteServerModal'; describe('', () => { let wrapper; - const deleteServerMock = sinon.fake(); - const historyMock = { push: sinon.fake() }; - const toggleMock = sinon.fake(); + const deleteServerMock = jest.fn(); + const historyMock = { push: jest.fn() }; + const toggleMock = jest.fn(); const serverName = 'the_server_name'; beforeEach(() => { - toggleMock.resetHistory(); - deleteServerMock.resetHistory(); - historyMock.push.resetHistory(); + deleteServerMock.mockReset(); + toggleMock.mockReset(); + historyMock.push.mockReset(); wrapper = shallow( ', () => { cancelBtn.simulate('click'); - expect(toggleMock.callCount).toEqual(1); - expect(deleteServerMock.callCount).toEqual(0); - expect(historyMock.push.callCount).toEqual(0); + expect(toggleMock).toHaveBeenCalledTimes(1); + expect(deleteServerMock).not.toHaveBeenCalled(); + expect(historyMock.push).not.toHaveBeenCalled(); }); it('deletes server when clicking accept button', () => { @@ -58,8 +57,8 @@ describe('', () => { acceptBtn.simulate('click'); - expect(toggleMock.callCount).toEqual(1); - expect(deleteServerMock.callCount).toEqual(1); - expect(historyMock.push.callCount).toEqual(1); + expect(toggleMock).toHaveBeenCalledTimes(1); + expect(deleteServerMock).toHaveBeenCalledTimes(1); + expect(historyMock.push).toHaveBeenCalledTimes(1); }); }); diff --git a/test/servers/helpers/ImportServersBtn.test.js b/test/servers/helpers/ImportServersBtn.test.js index 269cd300..d4c68186 100644 --- a/test/servers/helpers/ImportServersBtn.test.js +++ b/test/servers/helpers/ImportServersBtn.test.js @@ -1,25 +1,24 @@ import React from 'react'; -import sinon from 'sinon'; import { shallow } from 'enzyme'; import { UncontrolledTooltip } from 'reactstrap'; import importServersBtnConstruct from '../../../src/servers/helpers/ImportServersBtn'; describe('', () => { let wrapper; - const onImportMock = sinon.fake(); - const createServersMock = sinon.fake(); + const onImportMock = jest.fn(); + const createServersMock = jest.fn(); const serversImporterMock = { - importServersFromFile: sinon.fake.returns(Promise.resolve([])), + importServersFromFile: jest.fn().mockResolvedValue([]), }; const fileRef = { - current: { click: sinon.fake() }, + current: { click: jest.fn() }, }; beforeEach(() => { - onImportMock.resetHistory(); - createServersMock.resetHistory(); - serversImporterMock.importServersFromFile.resetHistory(); - fileRef.current.click.resetHistory(); + onImportMock.mockReset(); + createServersMock.mockReset(); + serversImporterMock.importServersFromFile.mockClear(); + fileRef.current.click.mockReset(); const ImportServersBtn = importServersBtnConstruct(serversImporterMock); @@ -40,7 +39,7 @@ describe('', () => { btn.simulate('click'); - expect(fileRef.current.click.callCount).toEqual(1); + expect(fileRef.current.click).toHaveBeenCalledTimes(1); }); it('imports servers when file input changes', (done) => { @@ -49,9 +48,9 @@ describe('', () => { file.simulate('change', { target: { files: [ '' ] } }); setImmediate(() => { - expect(serversImporterMock.importServersFromFile.callCount).toEqual(1); - expect(createServersMock.callCount).toEqual(1); - expect(onImportMock.callCount).toEqual(1); + expect(serversImporterMock.importServersFromFile).toHaveBeenCalledTimes(1); + expect(createServersMock).toHaveBeenCalledTimes(1); + expect(onImportMock).toHaveBeenCalledTimes(1); done(); }); }); diff --git a/test/servers/reducers/selectedServer.test.js b/test/servers/reducers/selectedServer.test.js index 423637d6..dcab78e6 100644 --- a/test/servers/reducers/selectedServer.test.js +++ b/test/servers/reducers/selectedServer.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import reducer, { selectServer, resetSelectedServer, @@ -31,31 +30,27 @@ describe('selectedServerReducer', () => { id: serverId, }; const ServersServiceMock = { - findServerById: sinon.fake.returns(selectedServer), + findServerById: jest.fn(() => selectedServer), }; afterEach(() => { - ServersServiceMock.findServerById.resetHistory(); + ServersServiceMock.findServerById.mockClear(); }); it('dispatches proper actions', () => { - const dispatch = sinon.spy(); - const expectedDispatchCalls = 2; + const dispatch = jest.fn(); selectServer(ServersServiceMock)(serverId)(dispatch); - expect(dispatch.callCount).toEqual(expectedDispatchCalls); - expect(dispatch.firstCall.calledWith({ type: RESET_SHORT_URL_PARAMS })).toEqual(true); - expect(dispatch.secondCall.calledWith({ - type: SELECT_SERVER, - selectedServer, - })).toEqual(true); + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: RESET_SHORT_URL_PARAMS }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: SELECT_SERVER, selectedServer }); }); it('invokes dependencies', () => { selectServer(ServersServiceMock)(serverId)(() => {}); - expect(ServersServiceMock.findServerById.callCount).toEqual(1); + expect(ServersServiceMock.findServerById).toHaveBeenCalledTimes(1); }); }); }); diff --git a/test/servers/reducers/server.test.js b/test/servers/reducers/server.test.js index cbf81d00..6645f9e0 100644 --- a/test/servers/reducers/server.test.js +++ b/test/servers/reducers/server.test.js @@ -1,4 +1,3 @@ -import * as sinon from 'sinon'; import { values } from 'ramda'; import reducer, { createServer, @@ -15,10 +14,10 @@ describe('serverReducer', () => { }; const expectedFetchServersResult = { type: FETCH_SERVERS, payload }; const ServersServiceMock = { - listServers: sinon.fake.returns(payload), - createServer: sinon.fake(), - deleteServer: sinon.fake(), - createServers: sinon.fake(), + listServers: jest.fn(() => payload), + createServer: jest.fn(), + deleteServer: jest.fn(), + createServers: jest.fn(), }; describe('reducer', () => { @@ -28,10 +27,10 @@ describe('serverReducer', () => { describe('action creators', () => { beforeEach(() => { - ServersServiceMock.listServers.resetHistory(); - ServersServiceMock.createServer.resetHistory(); - ServersServiceMock.deleteServer.resetHistory(); - ServersServiceMock.createServers.resetHistory(); + ServersServiceMock.listServers.mockClear(); + ServersServiceMock.createServer.mockReset(); + ServersServiceMock.deleteServer.mockReset(); + ServersServiceMock.createServers.mockReset(); }); describe('listServers', () => { @@ -39,10 +38,10 @@ describe('serverReducer', () => { const result = listServers(ServersServiceMock)(); expect(result).toEqual(expectedFetchServersResult); - expect(ServersServiceMock.listServers.calledOnce).toEqual(true); - expect(ServersServiceMock.createServer.called).toEqual(false); - expect(ServersServiceMock.deleteServer.called).toEqual(false); - expect(ServersServiceMock.createServers.called).toEqual(false); + expect(ServersServiceMock.listServers).toHaveBeenCalledTimes(1); + expect(ServersServiceMock.createServer).not.toHaveBeenCalled(); + expect(ServersServiceMock.deleteServer).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServers).not.toHaveBeenCalled(); }); }); @@ -52,11 +51,11 @@ describe('serverReducer', () => { const result = createServer(ServersServiceMock, () => expectedFetchServersResult)(serverToCreate); expect(result).toEqual(expectedFetchServersResult); - expect(ServersServiceMock.createServer.calledOnce).toEqual(true); - expect(ServersServiceMock.createServer.firstCall.calledWith(serverToCreate)).toEqual(true); - expect(ServersServiceMock.listServers.called).toEqual(false); - expect(ServersServiceMock.deleteServer.called).toEqual(false); - expect(ServersServiceMock.createServers.called).toEqual(false); + expect(ServersServiceMock.createServer).toHaveBeenCalledTimes(1); + expect(ServersServiceMock.createServer).toHaveBeenCalledWith(serverToCreate); + expect(ServersServiceMock.listServers).not.toHaveBeenCalled(); + expect(ServersServiceMock.deleteServer).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServers).not.toHaveBeenCalled(); }); }); @@ -66,11 +65,11 @@ describe('serverReducer', () => { const result = deleteServer(ServersServiceMock, () => expectedFetchServersResult)(serverToDelete); expect(result).toEqual(expectedFetchServersResult); - expect(ServersServiceMock.listServers.called).toEqual(false); - expect(ServersServiceMock.createServer.called).toEqual(false); - expect(ServersServiceMock.createServers.called).toEqual(false); - expect(ServersServiceMock.deleteServer.calledOnce).toEqual(true); - expect(ServersServiceMock.deleteServer.firstCall.calledWith(serverToDelete)).toEqual(true); + expect(ServersServiceMock.listServers).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServer).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServers).not.toHaveBeenCalled(); + expect(ServersServiceMock.deleteServer).toHaveBeenCalledTimes(1); + expect(ServersServiceMock.deleteServer).toHaveBeenCalledWith(serverToDelete); }); }); @@ -80,11 +79,11 @@ describe('serverReducer', () => { const result = createServers(ServersServiceMock, () => expectedFetchServersResult)(serversToCreate); expect(result).toEqual(expectedFetchServersResult); - expect(ServersServiceMock.listServers.called).toEqual(false); - expect(ServersServiceMock.createServer.called).toEqual(false); - expect(ServersServiceMock.createServers.calledOnce).toEqual(true); - expect(ServersServiceMock.createServers.firstCall.calledWith(serversToCreate)).toEqual(true); - expect(ServersServiceMock.deleteServer.called).toEqual(false); + expect(ServersServiceMock.listServers).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServer).not.toHaveBeenCalled(); + expect(ServersServiceMock.createServers).toHaveBeenCalledTimes(1); + expect(ServersServiceMock.createServers).toHaveBeenCalledWith(serversToCreate); + expect(ServersServiceMock.deleteServer).not.toHaveBeenCalled(); }); }); }); diff --git a/test/servers/services/ServersExporter.test.js b/test/servers/services/ServersExporter.test.js index 173292e3..772df918 100644 --- a/test/servers/services/ServersExporter.test.js +++ b/test/servers/services/ServersExporter.test.js @@ -1,26 +1,25 @@ -import sinon from 'sinon'; import ServersExporter from '../../../src/servers/services/ServersExporter'; describe('ServersExporter', () => { const createLinkMock = () => ({ - setAttribute: sinon.fake(), - click: sinon.fake(), + setAttribute: jest.fn(), + click: jest.fn(), style: {}, }); const createWindowMock = (isIe10 = true) => ({ navigator: { - msSaveBlob: isIe10 ? sinon.fake() : undefined, + msSaveBlob: isIe10 ? jest.fn() : undefined, }, document: { - createElement: sinon.fake.returns(createLinkMock()), + createElement: jest.fn(() => createLinkMock()), body: { - appendChild: sinon.fake(), - removeChild: sinon.fake(), + appendChild: jest.fn(), + removeChild: jest.fn(), }, }, }); const serversServiceMock = { - listServers: sinon.fake.returns({ + listServers: jest.fn(() => ({ abc123: { id: 'abc123', name: 'foo', @@ -29,10 +28,16 @@ describe('ServersExporter', () => { id: 'def456', name: 'bar', }, - }), + })), }; const createCsvjsonMock = (throwError = false) => ({ - toCSV: throwError ? sinon.fake.throws('') : sinon.fake.returns(''), + toCSV: jest.fn(() => { + if (throwError) { + throw new Error(''); + } + + return ''; + }), }); describe('exportServers', () => { @@ -40,10 +45,10 @@ describe('ServersExporter', () => { beforeEach(() => { originalConsole = global.console; - global.console = { error: sinon.fake() }; + global.console = { error: jest.fn() }; global.Blob = class Blob {}; global.URL = { createObjectURL: () => '' }; - serversServiceMock.listServers.resetHistory(); + serversServiceMock.listServers.mockReset(); }); afterEach(() => { global.console = originalConsole; @@ -59,8 +64,8 @@ describe('ServersExporter', () => { exporter.exportServers(); - expect(global.console.error.callCount).toEqual(1); - expect(csvjsonMock.toCSV.callCount).toEqual(1); + expect(global.console.error).toHaveBeenCalledTimes(1); + expect(csvjsonMock.toCSV).toHaveBeenCalledTimes(1); }); it('makes use of msSaveBlob API when available', () => { @@ -73,9 +78,9 @@ describe('ServersExporter', () => { exporter.exportServers(); - expect(serversServiceMock.listServers.callCount).toEqual(1); - expect(windowMock.navigator.msSaveBlob.callCount).toEqual(1); - expect(windowMock.document.createElement.callCount).toEqual(0); + expect(serversServiceMock.listServers).toHaveBeenCalledTimes(1); + expect(windowMock.navigator.msSaveBlob).toHaveBeenCalledTimes(1); + expect(windowMock.document.createElement).not.toHaveBeenCalled(); }); it('makes use of download link API when available', () => { @@ -88,10 +93,10 @@ describe('ServersExporter', () => { exporter.exportServers(); - expect(serversServiceMock.listServers.callCount).toEqual(1); - expect(windowMock.document.createElement.callCount).toEqual(1); - expect(windowMock.document.body.appendChild.callCount).toEqual(1); - expect(windowMock.document.body.removeChild.callCount).toEqual(1); + expect(serversServiceMock.listServers).toHaveBeenCalledTimes(1); + expect(windowMock.document.createElement).toHaveBeenCalledTimes(1); + expect(windowMock.document.body.appendChild).toHaveBeenCalledTimes(1); + expect(windowMock.document.body.removeChild).toHaveBeenCalledTimes(1); }); }); }); diff --git a/test/servers/services/ServersImporter.test.js b/test/servers/services/ServersImporter.test.js index ef79d790..217cc0bf 100644 --- a/test/servers/services/ServersImporter.test.js +++ b/test/servers/services/ServersImporter.test.js @@ -1,14 +1,13 @@ -import sinon from 'sinon'; import ServersImporter from '../../../src/servers/services/ServersImporter'; describe('ServersImporter', () => { const servers = [{ name: 'foo' }, { name: 'bar' }]; const csvjsonMock = { - toObject: sinon.fake.returns(servers), + toObject: jest.fn(() => servers), }; const importer = new ServersImporter(csvjsonMock); - beforeEach(() => csvjsonMock.toObject.resetHistory()); + beforeEach(() => csvjsonMock.toObject.mockClear()); describe('importServersFromFile', () => { it('rejects with error if no file was provided', async () => { @@ -28,7 +27,7 @@ describe('ServersImporter', () => { }); it('reads file when a CSV is provided', async () => { - const readAsText = sinon.fake.returns(''); + const readAsText = jest.fn(() => ''); global.FileReader = class FileReader { constructor() { @@ -40,8 +39,8 @@ describe('ServersImporter', () => { await importer.importServersFromFile({ type: 'text/csv' }); - expect(readAsText.callCount).toEqual(1); - expect(csvjsonMock.toObject.callCount).toEqual(1); + expect(readAsText).toHaveBeenCalledTimes(1); + expect(csvjsonMock.toObject).toHaveBeenCalledTimes(1); }); }); }); diff --git a/test/servers/services/ServersService.test.js b/test/servers/services/ServersService.test.js index b8c60d9e..87346c82 100644 --- a/test/servers/services/ServersService.test.js +++ b/test/servers/services/ServersService.test.js @@ -1,5 +1,3 @@ -import sinon from 'sinon'; -import { last } from 'ramda'; import ServersService from '../../../src/servers/services/ServersService'; describe('ServersService', () => { @@ -8,8 +6,8 @@ describe('ServersService', () => { def456: { id: 'def456' }, }; const createStorageMock = (returnValue) => ({ - set: sinon.fake(), - get: sinon.fake.returns(returnValue), + set: jest.fn(), + get: jest.fn(() => returnValue), }); describe('listServers', () => { @@ -20,8 +18,8 @@ describe('ServersService', () => { const result = service.listServers(); expect(result).toEqual({}); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(0); + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).not.toHaveBeenCalled(); }); it('returns value from storage when found', () => { @@ -31,8 +29,8 @@ describe('ServersService', () => { const result = service.listServers(); expect(result).toEqual(servers); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(0); + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).not.toHaveBeenCalled(); }); }); @@ -44,8 +42,8 @@ describe('ServersService', () => { const result = service.findServerById('ghi789'); expect(result).toBeUndefined(); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(0); + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).not.toHaveBeenCalled(); }); it('returns server from list when found', () => { @@ -55,8 +53,8 @@ describe('ServersService', () => { const result = service.findServerById('abc123'); expect(result).toEqual({ id: 'abc123' }); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(0); + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).not.toHaveBeenCalled(); }); }); @@ -67,9 +65,9 @@ describe('ServersService', () => { service.createServer({ id: 'ghi789' }); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(1); - expect(last(storageMock.set.lastCall.args)).toEqual({ + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledWith(expect.anything(), { abc123: { id: 'abc123' }, def456: { id: 'def456' }, ghi789: { id: 'ghi789' }, @@ -84,9 +82,9 @@ describe('ServersService', () => { service.createServers([{ id: 'ghi789' }, { id: 'jkl123' }]); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(1); - expect(last(storageMock.set.lastCall.args)).toEqual({ + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledWith(expect.anything(), { abc123: { id: 'abc123' }, def456: { id: 'def456' }, ghi789: { id: 'ghi789' }, @@ -102,9 +100,9 @@ describe('ServersService', () => { service.deleteServer({ id: 'abc123' }); - expect(storageMock.get.callCount).toEqual(1); - expect(storageMock.set.callCount).toEqual(1); - expect(last(storageMock.set.lastCall.args)).toEqual({ + expect(storageMock.get).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledTimes(1); + expect(storageMock.set).toHaveBeenCalledWith(expect.anything(), { def456: { id: 'def456' }, }); }); diff --git a/test/short-urls/CreateShortUrl.test.js b/test/short-urls/CreateShortUrl.test.js index 55592106..f7b08096 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,20 +48,16 @@ describe('', () => { const form = wrapper.find('form'); form.simulate('submit', { preventDefault: identity }); - expect(createShortUrl.callCount).toEqual(1); - expect(createShortUrl.getCall(0).args).toEqual( - [ - { - longUrl: 'https://long-domain.com/foo/bar', - tags: [ 'tag_foo', 'tag_bar' ], - customSlug: 'my-slug', - validSince: validSince.format(), - validUntil: validUntil.format(), - maxVisits: '20', - findIfExists: false, - }, - ] - ); + expect(createShortUrl).toHaveBeenCalledTimes(1); + expect(createShortUrl).toHaveBeenCalledWith({ + longUrl: 'https://long-domain.com/foo/bar', + tags: [ 'tag_foo', 'tag_bar' ], + customSlug: 'my-slug', + validSince: validSince.format(), + validUntil: validUntil.format(), + maxVisits: '20', + findIfExists: false, + }); done(); }); }); 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..99c3106f 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).toHaveBeenCalledWith(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).toHaveBeenCalledWith(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..3e1ee367 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,30 +47,27 @@ 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; const result = 'foo'; const apiClientMock = createApiClientMock(Promise.resolve(result)); const dispatchable = createShortUrl(() => apiClientMock)({}); await dispatchable(dispatch, getState); - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: CREATE_SHORT_URL_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: CREATE_SHORT_URL, result }); }); it('throws on error', async () => { - const expectedDispatchCalls = 2; const error = 'Error'; const apiClientMock = createApiClientMock(Promise.reject(error)); const dispatchable = createShortUrl(() => apiClientMock)({}); @@ -82,11 +78,10 @@ describe('shortUrlCreationReducer', () => { expect(e).toEqual(error); } - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: CREATE_SHORT_URL_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { 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..63f18f30 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,39 +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); - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_SHORT_URL_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { 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).toHaveBeenCalledWith(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; try { await deleteShortUrl(() => apiClientMock)(shortCode)(dispatch, getState); @@ -98,12 +95,12 @@ describe('shortUrlDeletionReducer', () => { expect(e).toEqual(error); } - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_SHORT_URL_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { 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).toHaveBeenCalledWith(shortCode); }); }); }); diff --git a/test/short-urls/reducers/shortUrlsList.test.js b/test/short-urls/reducers/shortUrlsList.test.js index a3055a04..5af7b9cd 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,40 @@ 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); - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: LIST_SHORT_URLS_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { 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); - 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: LIST_SHORT_URLS_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { 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..1978917a 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,31 +41,29 @@ 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; const tag = 'foo'; const apiClientMock = createApiClientMock(Promise.resolve()); const dispatchable = deleteTag(() => apiClientMock)(tag); 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(2); + expect(dispatch).toHaveBeenNthCalledWith(1, { type: DELETE_TAG_START }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: DELETE_TAG }); }); it('throws on error', async () => { - const expectedDispatchCalls = 2; const error = 'Error'; const tag = 'foo'; const apiClientMock = createApiClientMock(Promise.reject(error)); @@ -78,12 +75,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(2); + 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..f70a3fa7 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,21 +45,20 @@ 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 () => { - const expectedDispatchCalls = 2; const oldName = 'foo'; const newName = 'bar'; const color = '#ff0000'; @@ -69,19 +67,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(2); + 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 +92,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..e7006c75 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', () => { @@ -80,21 +76,19 @@ describe('', () => { const wrapper = createComponent({ loading: false, error: false, visits: [{}, {}, {}] }); const graphs = wrapper.find(GraphCard); const sortableBarGraphs = wrapper.find(SortableBarGraph); - const expectedGraphsCount = 5; - expect(graphs.length + sortableBarGraphs.length).toEqual(expectedGraphsCount); + expect(graphs.length + sortableBarGraphs.length).toEqual(5); }); 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: [{}, {}, {}, {}, {}, {}], + })); }); });