diff --git a/.gitignore b/.gitignore
index c47bebc..9f83dc1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,10 @@
 .DS_Store
+.vscode
 
 node_modules
 npm-debug.log
 
-.nyc_output
-coverage
+.coverage
 *.lcov
 
 example/build
diff --git a/jest/config.json b/jest/config.json
new file mode 100644
index 0000000..ccd6bcd
--- /dev/null
+++ b/jest/config.json
@@ -0,0 +1,25 @@
+{
+  "collectCoverageFrom": [
+    "!**/*.d.ts",
+    "!**/node_modules/**",
+    "src/*.{ts,js}"
+  ],
+  "coverageDirectory": "<rootDir>/.coverage",
+  "coverageReporters": [
+    "lcov"
+  ],
+  "moduleFileExtensions": [
+    "js",
+    "ts"
+  ],
+  "modulePaths": [
+    "<rootDir>/src"
+  ],
+  "resetModules": true,
+  "rootDir": "../",
+  "testRegex": "/src/(.+\\.)?spec\\.ts$",
+  "transform": {
+    "^.+\\.(ts|js)$": "<rootDir>/jest/tsTransform.js"
+  },
+  "verbose": true
+}
\ No newline at end of file
diff --git a/jest/tsTransform.js b/jest/tsTransform.js
new file mode 100644
index 0000000..dbc0c18
--- /dev/null
+++ b/jest/tsTransform.js
@@ -0,0 +1,17 @@
+const tsc = require('typescript');
+
+const tsconfig = require('../tsconfig.json');
+
+const compilerOptions = Object.assign({}, tsconfig.compilerOptions, {
+  module: 'commonjs',
+});
+
+module.exports = {
+  process(source, path) {
+    return (
+      path.endsWith('.ts') || path.endsWith('.js')
+        ? tsc.transpile(source, compilerOptions, path, [])
+        : source
+    );
+  },
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index 7a6e129..f868f83 100644
--- a/package.json
+++ b/package.json
@@ -5,12 +5,11 @@
   "main": "dist/redux-logger.js",
   "scripts": {
     "lint": "eslint src",
-    "test": "npm run lint && npm run spec",
-    "spec": "nyc --all --silent --require babel-core/register mocha --plugins transform-inline-environment-variables --recursive spec/*.spec.js",
-    "spec:watch": "npm run spec -- --watch",
-    "coverage": "nyc report",
-    "coverage:html": "nyc report --reporter=html && http-server -p 8077 ./coverage -o",
-    "coverage:production": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
+    "test": "jest --config jest/config.json",
+    "test:coverage": "npm test -- --coverage",
+    "test:coverage:show": "http-server -o -p 1335 .coverage/lcov-report",
+    "test:production": "npm test:coverage -- --ci",
+    "test:watch": "npm test -- --watch",
     "clean": "rimraf dist",
     "build": "rollup -c",
     "precommit": "npm test && npm run lint",
@@ -22,21 +21,9 @@
       "no-console": "off"
     },
     "env": {
-      "browser": true,
-      "mocha": true
+      "browser": true
     }
   },
-  "nyc": {
-    "exclude": [
-      "node_modules",
-      "spec",
-      "example",
-      "lib",
-      "dist",
-      "coverage",
-      "rollup.config.js"
-    ]
-  },
   "files": [
     "dist",
     "src"
@@ -58,11 +45,11 @@
   },
   "homepage": "https://github.com/theaqua/redux-logger#readme",
   "devDependencies": {
+    "@types/jest": "^22.2.0",
     "babel-core": "^6.24.0",
     "babel-plugin-external-helpers": "^6.22.0",
     "babel-plugin-transform-inline-environment-variables": "6.8.0",
     "babel-preset-es2015": "^6.24.0",
-    "chai": "3.5.0",
     "codecov": "1.0.1",
     "eslint": "^3.19.0",
     "eslint-config-airbnb": "^14.1.0",
@@ -71,8 +58,7 @@
     "eslint-plugin-react": "^6.10.3",
     "http-server": "0.9.0",
     "husky": "^0.13.2",
-    "mocha": "3.1.2",
-    "nyc": "9.0.1",
+    "jest": "^22.4.2",
     "redux": "^3.6.0",
     "rimraf": "^2.6.1",
     "rollup": "^0.41.6",
@@ -80,7 +66,8 @@
     "rollup-plugin-commonjs": "^8.0.2",
     "rollup-plugin-node-resolve": "^3.0.0",
     "rollup-plugin-uglify": "^1.0.2",
-    "sinon": "^1.17.7"
+    "tslib": "^1.9.0",
+    "typescript": "^2.8.0-rc"
   },
   "dependencies": {
     "deep-diff": "^0.3.5"
diff --git a/spec/diff.spec.js b/spec/diff.spec.js
deleted file mode 100644
index a89b4bf..0000000
--- a/spec/diff.spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import sinon from 'sinon';
-import { expect } from 'chai';
-import { style, render, default as diffLogger } from '../src/diff';
-
-context('Diff', () => {
-  describe('style', () => {
-    it('return css rules for the given kind of diff changes', () => {
-      expect(style('E')).to.equal('color: #2196F3; font-weight: bold');
-      expect(style('N')).to.equal('color: #4CAF50; font-weight: bold');
-      expect(style('D')).to.equal('color: #F44336; font-weight: bold');
-      expect(style('A')).to.equal('color: #2196F3; font-weight: bold');
-    });
-  });
-
-  describe('render', () => {
-    it('should return an array indicating the changes', () => {
-      expect(render({
-        kind: 'E',
-        path: ['capitain', 'name'],
-        lhs: 'kirk',
-        rhs: 'picard',
-      })).to.eql(['capitain.name', 'kirk', '→', 'picard']);
-    });
-
-    it('should return an array indicating an added property/element', () => {
-      expect(render({
-        kind: 'N',
-        path: ['crew', 'engineer'],
-        rhs: 'geordi',
-      })).to.eql(['crew.engineer', 'geordi']);
-    });
-
-    it('should return an array indicating a removed property/element', () => {
-      expect(render({
-        kind: 'D',
-        path: ['crew', 'security'],
-      })).to.eql(['crew.security']);
-    });
-
-    it('should return an array indicating a changed index', () => {
-      expect(render({
-        kind: 'A',
-        path: ['crew'],
-        index: 2,
-        item: {
-          kind: 'N',
-          rhs: 'after',
-        },
-      })).to.eql(['crew[2]', {
-        kind: 'N',
-        rhs: 'after',
-      }]);
-    });
-
-    it('should return an empty array', () => {
-      expect(render({})).to.eql([]);
-    });
-  });
-
-  describe('diffLogger', () => {
-    let logger
-
-    beforeEach(() => {
-      logger = {
-        log: sinon.spy(),
-        groupCollapsed: sinon.spy(),
-        groupEnd: sinon.spy(),
-        group: sinon.spy(),
-      };
-    });
-
-    it('should show no diff with group collapsed', () => {
-      diffLogger({}, {}, logger, true);
-
-      expect(logger.group.calledOnce).to.be.false;
-      expect(logger.groupCollapsed.calledOnce).to.be.true;
-      expect(logger.groupEnd.calledOnce).to.be.true;
-      expect(logger.log.calledOnce).to.be.true;
-      expect(logger.log.calledWith('—— no diff ——')).to.be.true;
-    });
-
-    it('should show no diff with group not collapsed', () => {
-      diffLogger({}, {}, logger, false);
-
-      expect(logger.group.calledOnce).to.be.true;
-      expect(logger.groupCollapsed.calledOnce).to.be.false;
-      expect(logger.groupEnd.calledOnce).to.be.true;
-      expect(logger.log.calledOnce).to.be.true;
-      expect(logger.log.calledWith('—— no diff ——')).to.be.true;
-    });
-
-    it('should log no diff without group', () => {
-      const loggerWithNoGroupCollapsed = Object.assign({}, logger, {
-        groupCollapsed: () => {
-          throw new Error()
-        },
-        groupEnd: () => {
-          throw new Error()
-        },
-      });
-
-      diffLogger({}, {}, loggerWithNoGroupCollapsed, true);
-
-      expect(loggerWithNoGroupCollapsed.log.calledWith('diff')).to.be.true;
-      expect(loggerWithNoGroupCollapsed.log.calledWith('—— no diff ——')).to.be.true;
-      expect(loggerWithNoGroupCollapsed.log.calledWith('—— diff end —— ')).to.be.true;
-    });
-
-    it('should log the diffs', () => {
-      diffLogger({name: 'kirk'}, {name: 'picard'}, logger, false);
-
-      expect(logger.log.calledWithExactly('%c CHANGED:', 'color: #2196F3; font-weight: bold', 'name', 'kirk', '→', 'picard')).to.be.true;
-    });
-  });
-});
diff --git a/src/diff.spec.ts b/src/diff.spec.ts
new file mode 100644
index 0000000..883a390
--- /dev/null
+++ b/src/diff.spec.ts
@@ -0,0 +1,111 @@
+import diffLogger, { style, render } from './diff';
+
+describe('Diff', () => {
+  describe('style', () => {
+    it('return css rules for the given kind of diff changes', () => {
+      expect(style('E')).toEqual('color: #2196F3; font-weight: bold');
+      expect(style('N')).toEqual('color: #4CAF50; font-weight: bold');
+      expect(style('D')).toEqual('color: #F44336; font-weight: bold');
+      expect(style('A')).toEqual('color: #2196F3; font-weight: bold');
+    });
+  });
+
+  describe('render', () => {
+    it('should return an array indicating the changes', () => {
+      expect(render({
+        kind: 'E',
+        path: ['capitain', 'name'],
+        lhs: 'kirk',
+        rhs: 'picard',
+      })).toEqual(['capitain.name', 'kirk', '→', 'picard']);
+    });
+
+    it('should return an array indicating an added property/element', () => {
+      expect(render({
+        kind: 'N',
+        path: ['crew', 'engineer'],
+        rhs: 'geordi',
+      })).toEqual(['crew.engineer', 'geordi']);
+    });
+
+    it('should return an array indicating a removed property/element', () => {
+      expect(render({
+        kind: 'D',
+        path: ['crew', 'security'],
+      })).toEqual(['crew.security']);
+    });
+
+    it('should return an array indicating a changed index', () => {
+      expect(render({
+        kind: 'A',
+        path: ['crew'],
+        index: 2,
+        item: {
+          kind: 'N',
+          rhs: 'after',
+        },
+      })).toEqual(['crew[2]', {
+        kind: 'N',
+        rhs: 'after',
+      }]);
+    });
+
+    it('should return an empty array', () => {
+      expect(render({})).toEqual([]);
+    });
+  });
+
+  describe('diffLogger', () => {
+    let logger: any;
+
+    beforeEach(() => {
+      logger = {
+        log: jest.fn(),
+        groupCollapsed: jest.fn(),
+        groupEnd: jest.fn(),
+        group: jest.fn(),
+      };
+    });
+
+    it('should show no diff with group collapsed', () => {
+      diffLogger({}, {}, logger, true);
+
+      expect(logger.group).not.toHaveBeenCalled();
+      expect(logger.groupCollapsed).toHaveBeenCalled();
+      expect(logger.groupEnd).toHaveBeenCalled();
+      expect(logger.log).toHaveBeenCalledWith('—— no diff ——');
+    });
+
+    it('should show no diff with group not collapsed', () => {
+      diffLogger({}, {}, logger, false);
+
+      expect(logger.group).toHaveBeenCalled();
+      expect(logger.groupCollapsed).not.toHaveBeenCalled();
+      expect(logger.groupEnd).toHaveBeenCalled();
+      expect(logger.log).toHaveBeenCalledWith('—— no diff ——');
+    });
+
+    it('should log no diff without group', () => {
+      const loggerWithNoGroupCollapsed = Object.assign({}, logger, {
+        groupCollapsed: () => {
+          throw new Error()
+        },
+        groupEnd: () => {
+          throw new Error()
+        },
+      });
+
+      diffLogger({}, {}, loggerWithNoGroupCollapsed, true);
+
+      expect(loggerWithNoGroupCollapsed.log).toHaveBeenCalledWith('diff');
+      expect(loggerWithNoGroupCollapsed.log).toHaveBeenCalledWith('—— no diff ——');
+      expect(loggerWithNoGroupCollapsed.log).toHaveBeenCalledWith('—— diff end —— ');
+    });
+
+    it('should log the diffs', () => {
+      diffLogger({ name: 'kirk' }, { name: 'picard' }, logger, false);
+
+      expect(logger.log).toHaveBeenCalledWith('%c CHANGED:', 'color: #2196F3; font-weight: bold', 'name', 'kirk', '→', 'picard');
+    });
+  });
+});
\ No newline at end of file
diff --git a/spec/helpers.spec.js b/src/helpers.spec.ts
similarity index 58%
rename from spec/helpers.spec.js
rename to src/helpers.spec.ts
index 7d8d4c2..ed1f03e 100644
--- a/spec/helpers.spec.js
+++ b/src/helpers.spec.ts
@@ -1,23 +1,22 @@
-import { expect } from 'chai';
-import { repeat, pad, formatTime } from '../src/helpers';
+import { repeat, pad, formatTime } from './helpers';
 
-context('Helpers', () => {
+describe('Helpers', () => {
   describe('repeat', () => {
     it('should repeat a string the number of indicated times', () => {
-      expect(repeat('teacher', 3)).to.equal('teacherteacherteacher');
+      expect(repeat('teacher', 3)).toEqual('teacherteacherteacher');
     });
   });
 
   describe('pad', () => {
     it('should add leading zeros to a number given a maximun length', () => {
-      expect(pad(56, 4)).to.equal('0056');
+      expect(pad(56, 4)).toEqual('0056');
     });
   });
 
   describe('formatTime', () => {
     it('should format a time given a Date object', () => {
       const time = new Date('December 25, 1995 23:15:30');
-      expect(formatTime(time)).to.equal('23:15:30.000');
+      expect(formatTime(time)).toEqual('23:15:30.000');
     });
   });
 });
diff --git a/spec/index.spec.js b/src/index.spec.ts
similarity index 61%
rename from spec/index.spec.js
rename to src/index.spec.ts
index 0373ef6..91934ac 100644
--- a/spec/index.spec.js
+++ b/src/index.spec.ts
@@ -1,48 +1,48 @@
-import sinon from 'sinon';
 import { applyMiddleware, createStore } from 'redux';
-import { default as logger, createLogger } from '../src';
 
-context('default logger', () => {
+import logger, { createLogger } from './';
+
+describe('default logger', () => {
   describe('init', () => {
     beforeEach(() => {
-      sinon.spy(console, 'error');
+      jest.spyOn(console, 'error');
     });
 
     afterEach(() => {
-      console.error.restore();
+      jest.restoreAllMocks();
     });
 
     it('should be ok', () => {
       const store = createStore(() => ({}), applyMiddleware(logger));
 
       store.dispatch({ type: 'foo' });
-      sinon.assert.notCalled(console.error);
+      expect(console.error).not.toHaveBeenCalled();
     });
   });
 });
 
-context('createLogger', () => {
+describe('createLogger', () => {
   describe('init', () => {
     beforeEach(() => {
-      sinon.spy(console, 'error');
+      jest.spyOn(console, 'error');
     });
 
     afterEach(() => {
-      console.error.restore();
+      jest.restoreAllMocks();
     });
 
     it('should throw error if passed direct to applyMiddleware', () => {
-      const store = createStore(() => ({}), applyMiddleware(createLogger));
+      const store = createStore(() => ({}), applyMiddleware(createLogger as any));
 
       store.dispatch({ type: 'foo' });
-      sinon.assert.calledOnce(console.error);
+      expect(console.error).toHaveBeenCalled();
     });
 
     it('should be ok', () => {
-      const store = createStore(() => ({}), applyMiddleware(createLogger()));
+      const store = createStore(() => ({}), applyMiddleware(createLogger() as any));
 
       store.dispatch({ type: 'foo' });
-      sinon.assert.notCalled(console.error);
+      expect(console.error).not.toHaveBeenCalled();
     });
   });
-});
+});
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..936babe
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,29 @@
+{
+  "buildOnSave": false,
+  "compileOnSave": false,
+  "compilerOptions": {
+    "allowJs": true,
+    "esModuleInterop": true,
+    "importHelpers": true,
+    "lib": [
+      "es5",
+      "es6"
+    ],
+    "module": "esnext",
+    "moduleResolution": "node",
+    "noEmitHelpers": true,
+    "noImplicitAny": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "removeComments": true,
+    "sourceMap": true,
+    "strictNullChecks": true,
+    "target": "es5",
+    "types": [
+      "jest"
+    ]
+  },
+  "include": [
+    "src"
+  ]
+}
\ No newline at end of file