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