diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..50964de --- /dev/null +++ b/.eslintrc @@ -0,0 +1,28 @@ +{ + "rules": { + "array-bracket-spacing": [2, "never"], + "indent": [2, 2], + "linebreak-style": [2, "unix"], + "no-console": 1, + "object-curly-spacing": [2, "never"], + "quotes": [2, "double", "avoid-escape"], + "semi": [2,"always"], + "space-infix-ops": 2, + + // react rules + "react/jsx-quotes": [2, "double"], + "react/jsx-uses-react": 2 + }, + "env": { + "browser": true, + "mocha": true, + "node": true + }, + "extends": "eslint:recommended", + "ecmaFeatures": { + "jsx": true + }, + "plugins": [ + "react" + ] +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index e49f99c..0000000 --- a/.jshintrc +++ /dev/null @@ -1,30 +0,0 @@ -{ - "browser": true, - "camelcase": true, - "devel": true, - "eqeqeq": true, - "globalstrict": true, - "immed": false, - "indent": 2, - "jquery": true, - "latedef": true, - "maxlen": 120, - "newcap": false, - "node": true, - "strict": true, - "trailing": false, - "undef": true, - "unused": "vars", - - "predef": [ - "jasmine", - "jest", - "describe", - "it", - "expect", - "before", - "beforeEach", - "after", - "afterEach" - ] -} diff --git a/.jsxhintignore b/.jsxhintignore deleted file mode 100644 index e3fbd98..0000000 --- a/.jsxhintignore +++ /dev/null @@ -1,2 +0,0 @@ -build -node_modules diff --git a/.travis.yml b/.travis.yml index 6e5919d..63f7b35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - "0.10" + - "4" diff --git a/gulpfile.js b/gulpfile.js index c95ce31..3d917b5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,22 +1,21 @@ -/* jshint node:true */ - "use strict"; var gulp = require("gulp"); -var browserify = require('browserify'); -var watchify = require('watchify'); -var source = require('vinyl-source-stream'); -var uglify = require('gulp-uglify'); -var webserver = require("gulp-webserver"); +var browserify = require("browserify"); +var watchify = require("watchify"); +var source = require("vinyl-source-stream"); +var uglify = require("gulp-uglify"); var deploy = require("gulp-gh-pages"); +var extend = require("util")._extend; + var opt = { outputFolder: "build", server: { host: "localhost", port: 4000, - livereload: true, + livereload: 35729, open:true }, @@ -38,12 +37,16 @@ var opt = { ], app: { - src: "src/js/kept.js", + src: "src/js/kept.jsx", dest: "kept.js" }, vendors: "vendors.js" }; +var jsxOpt = { + extensions: [".jsx"] +}; + /** * Assets tasks */ @@ -75,12 +78,14 @@ gulp.task("assets:fonts", function() { gulp.task("js", [ "js:vendors", "js:app" - ]); +]); gulp.task("js:app", ["js:vendors"], function() { - return browserify("./" + opt.app.src) - .transform("reactify") + return browserify(jsxOpt) + .add("./" + opt.app.src) + .transform("reactify", {global: true}) .external("react") + .external("react-dom") .external("react-bootstrap") .external("marked") .bundle() @@ -91,6 +96,7 @@ gulp.task("js:app", ["js:vendors"], function() { gulp.task("js:vendors", function() { return browserify() .require("react") + .require("react-dom") .require("react-bootstrap") .require("marked") .bundle() @@ -102,9 +108,20 @@ gulp.task("js:vendors", function() { /** * Server task */ +var connect = require("connect"); +var livereload = require("connect-livereload"); +var staticFile = require("serve-static"); +var lrServer = require("tiny-lr")(); + gulp.task("server", function() { - return gulp.src(opt.outputFolder) - .pipe(webserver(opt.server)); + + lrServer + .listen(opt.server.livereload); + + connect() + .use(livereload({port: opt.server.livereload})) + .use(staticFile(opt.outputFolder)) + .listen(4000); }); /** @@ -112,28 +129,23 @@ gulp.task("server", function() { */ gulp.task("watchify", function(){ + var args = extend(watchify.args, jsxOpt); - var b = browserify( "./" + opt.app.src , watchify.args) - .transform("reactify") + var b = browserify("./" + opt.app.src, args) + .transform("reactify", {global: true}) .external("react") + .external("react-dom") .external("react-bootstrap") .external("marked"); - - function updateBundle(w){ - - return w.bundle() + return watchify(b).on("update", function(){ + b.bundle() .pipe(source(opt.app.dest)) .pipe(gulp.dest(opt.outputFolder + "/js")); - } - - var watcher= watchify(b); - watcher.on("update", function(){ - updateBundle(watcher); - }); - - return updateBundle(watcher); - + }) + .bundle() + .pipe(source(opt.app.dest)) + .pipe(gulp.dest(opt.outputFolder + "/js")); }); @@ -147,6 +159,9 @@ gulp.task("watch", ["assets","js:vendors", "watchify"], function() { gulp.watch(opt.cssAssets, ["assets:css"]); gulp.watch(opt.fontAssets, ["assets:fonts"]); gulp.watch(opt.htmlAssets, ["assets:html"]); + gulp.watch(opt.outputFolder + "/**/*", function(file){ + lrServer.changed({body : {files : [file.path]}}); + }); }); gulp.task("dist", ["assets", "js"], function() { @@ -163,4 +178,8 @@ gulp.task("deploy", ["dist"], function() { .pipe(deploy("git@github.com:n1k0/kept.git")); }); -gulp.task("default", ["server", "watch"]); +gulp.task("default", ["server", "watch"], function(done){ + console.log("app running at http://" + opt.server.host + ":" + opt.server.port); + require("opn")("http://" + opt.server.host + ":" + opt.server.port); + done(); +}); diff --git a/jest-config-win.json b/jest-config-win.json deleted file mode 100644 index 3be128c..0000000 --- a/jest-config-win.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "collectCoverage": true, - "scriptPreprocessor": "preprocessor.js", - "unmockedModulePathPatterns": [ - "react" - ] -} diff --git a/jest-config.json b/jest-config.json deleted file mode 100644 index f61be74..0000000 --- a/jest-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "collectCoverage": true, - "scriptPreprocessor": "/preprocessor.js", - "unmockedModulePathPatterns": [ - "/node_modules/react" - ] -} diff --git a/package.json b/package.json index a21e88e..0bda5d3 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,10 @@ "scripts": { "deploy": "gulp deploy", "dist": "gulp dist", - "lint": "jsxhint -c .jshintrc src/js", + "lint": "eslint --ext .js --ext .jsx src gulpfile.js", "start": "gulp", - "test": " npm run lint && jest --config jest-config.json", - "test-win": "npm run lint && jest --config jest-config-win.json" + "test": "mocha src/**/__tests__/*", + "tdd": "mocha --watch ./src/**/__tests__/*.*" }, "repository": { "type": "git", @@ -27,21 +27,30 @@ }, "homepage": "https://github.com/n1k0/kept", "dependencies": { + "browserify": "^11.0.1", + "connect": "^3.3.3", + "connect-livereload": "^0.5.2", "gulp": "^3.8.*", - "gulp-gh-pages": "^0.3.*", - "gulp-uglify": "^0.3.*", + "gulp-gh-pages": "^0.5.*", + "gulp-uglify": "^1.4.*", "marked": "^0.3.*", - "react": "^0.10.*", - "react-bootstrap": "^0.10.*", - "reactify": "^0.13.*", - "vinyl-source-stream": "^0.1.*", - "watchify": "^1.0.6", - "browserify": "^6.0.2", - "gulp-webserver": "^0.8.3" + "opn": "^3.0.2", + "react": "^0.14.0-beta3", + "react-bootstrap": "^0.25.100-react-pre.0", + "react-dom": "^0.14.0-beta3", + "reactify": "^1.1.*", + "serve-static": "^1.7.1", + "tiny-lr": "^0.1.4", + "vinyl-source-stream": "^1.1.*", + "watchify": "^3.4.0" }, "devDependencies": { - "jest-cli": "latest", - "jsxhint": "latest", - "react-tools": "latest" + "babel": "^5.8.23", + "chai": "^3.2.0", + "eslint": "^1.4.1", + "eslint-plugin-react": "^3.3.2", + "jsdom": "^6.3.0", + "mocha": "^2.3.2", + "sinon": "^1.16.1" } } diff --git a/preprocessor.js b/preprocessor.js deleted file mode 100644 index 32f4a00..0000000 --- a/preprocessor.js +++ /dev/null @@ -1,7 +0,0 @@ -var ReactTools = require('react-tools'); - -module.exports = { - process: function(src) { - return ReactTools.transform(src); - } -}; diff --git a/src/css/kept.css b/src/css/kept.css index 3da7782..fc28243 100644 --- a/src/css/kept.css +++ b/src/css/kept.css @@ -57,10 +57,12 @@ body { .kept-modal .kept-panel .panel-heading { cursor: default; } - .kept-panel h3 a { - display: block; + .kept-panel h3 button { + display: inline-block; float: right; margin-left: .5em; + padding: 0; + color: #fff; } .targetted { diff --git a/src/js/components/DefaultContent.js b/src/js/components/DefaultContent.jsx similarity index 97% rename from src/js/components/DefaultContent.js rename to src/js/components/DefaultContent.jsx index c0eaf4a..c3c3fe8 100644 --- a/src/js/components/DefaultContent.js +++ b/src/js/components/DefaultContent.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); diff --git a/src/js/components/GlyphiconLink.js b/src/js/components/GlyphiconLink.jsx similarity index 57% rename from src/js/components/GlyphiconLink.js rename to src/js/components/GlyphiconLink.jsx index 185f5a6..c0309bf 100644 --- a/src/js/components/GlyphiconLink.js +++ b/src/js/components/GlyphiconLink.jsx @@ -1,14 +1,13 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); +var Button = require("react-bootstrap").Button; var Glyphicon = require("react-bootstrap").Glyphicon; var GlyphiconLink = React.createClass({ render: function() { - return this.transferPropsTo( - + return ( + ); } }); diff --git a/src/js/components/KeptApp.js b/src/js/components/KeptApp.jsx similarity index 99% rename from src/js/components/KeptApp.js rename to src/js/components/KeptApp.jsx index 067e4f7..bb54664 100644 --- a/src/js/components/KeptApp.js +++ b/src/js/components/KeptApp.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var utils = require("../utils"); @@ -107,7 +105,6 @@ var KeptApp = React.createClass({ move: function(fromIndex, toIndex) { // permut don't mutate array, return a new array var items = utils.permut(this.state.items, fromIndex, toIndex); - this.save(items); }, diff --git a/src/js/components/KeptColumns.js b/src/js/components/KeptColumns.jsx similarity index 90% rename from src/js/components/KeptColumns.js rename to src/js/components/KeptColumns.jsx index ba9d479..ee77733 100644 --- a/src/js/components/KeptColumns.js +++ b/src/js/components/KeptColumns.jsx @@ -1,6 +1,3 @@ -/** - * @jsx React.DOM - */ "use strict"; var React = require("react"); @@ -15,6 +12,7 @@ var KeptColumns = React.createClass({ var key = index * this.props.columns + this.props.column; return {this.props.itemData.title || "Untitled"} - - + + ); return ( -
- + {this.getComponent(this.props.itemData)}
diff --git a/src/js/components/KeptItems.js b/src/js/components/KeptItems.jsx similarity index 92% rename from src/js/components/KeptItems.js rename to src/js/components/KeptItems.jsx index 64919d4..b46c35a 100644 --- a/src/js/components/KeptItems.js +++ b/src/js/components/KeptItems.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var utils = require("../utils"); @@ -19,7 +17,6 @@ var KeepItems = React.createClass({ }, onResize: function(event){ - console.log(event, event.target.innerWidth); var col = 1; if (this.state.columnsWidth > 0) { col = Math.floor(event.target.innerWidth / this.state.columnsWidth); @@ -43,11 +40,12 @@ var KeepItems = React.createClass({ .range(this.state.columns) .map(function(_, index) { var colItems = this.props.items.filter(function(item, i) { - return i%this.state.columns === index; + return i % this.state.columns === index; }, this); return ( ); - }); - - it("should render HTML content", function() { - expect(comp.getDOMNode().outerHTML.length > 0).toBe(true); - }); - - it("should add a new text entry when clicking on Text link", function() { - var newTextLink = TestUtils.findRenderedDOMComponentWithClass(comp, "new-text"); - - TestUtils.Simulate.click(newTextLink); - - expect(fakeCreateText).toHaveBeenCalled(); - }); - - it("should add a new todo entry when clicking on Todo link", function() { - var newTodoLink = TestUtils.findRenderedDOMComponentWithClass(comp, "new-todo"); - - TestUtils.Simulate.click(newTodoLink); - - expect(fakeCreateTodo).toHaveBeenCalled(); - }); -}); diff --git a/src/js/components/__tests__/DefaultContent-test.jsx b/src/js/components/__tests__/DefaultContent-test.jsx new file mode 100644 index 0000000..29e40d9 --- /dev/null +++ b/src/js/components/__tests__/DefaultContent-test.jsx @@ -0,0 +1,57 @@ +"use strict"; + +var React = require("react"); +var TestUtils = require("react/lib/ReactTestUtils"); +var sinon = require("sinon"); +var expect = require("chai").expect; + +var DefaultContent = require("../DefaultContent"); +describe("DefaultContent", function() { + describe("#render", function() { + var renderer = TestUtils.createRenderer(); + var result; + beforeEach(function() { + renderer.render( + + ); + result = renderer.getRenderOutput(); + }); + + it("should render HTML content", function() { + expect(result.props.children.length > 0).eql(true); + }); + }); + + describe("add default items", function() { + var comp; + var fakeCreateText = sinon.spy(); + var fakeCreateTodo = sinon.spy(); + + function fakeNewItem(type) { + return type === "text" ? fakeCreateText : fakeCreateTodo; + } + + beforeEach(function() { + comp = TestUtils.renderIntoDocument( + + ); + }); + + it("should add a new text entry when clicking on Text link", function() { + + var newTextLink = TestUtils.findRenderedDOMComponentWithClass(comp, "new-text"); + + TestUtils.Simulate.click(newTextLink); + + expect(fakeCreateText.calledOnce).eql(true); + }); + + it("should add a new todo entry when clicking on Todo link", function() { + var newTodoLink = TestUtils.findRenderedDOMComponentWithClass(comp, "new-todo"); + + TestUtils.Simulate.click(newTodoLink); + + expect(fakeCreateTodo.calledOnce).eql(true); + }); + }); +}); diff --git a/src/js/components/__tests__/KeptApp-test.js b/src/js/components/__tests__/KeptApp-test.jsx similarity index 50% rename from src/js/components/__tests__/KeptApp-test.js rename to src/js/components/__tests__/KeptApp-test.jsx index 753198c..c41c37e 100644 --- a/src/js/components/__tests__/KeptApp-test.js +++ b/src/js/components/__tests__/KeptApp-test.jsx @@ -1,20 +1,15 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; - -jest.dontMock('../KeptMenuBar'); -jest.dontMock('../text/KeptTextForm'); -jest.dontMock('../todo/KeptTodoForm'); -jest.dontMock('../KeptApp'); - -var KeptApp = require('../KeptApp'); +var TestUtils = require("react/lib/ReactTestUtils"); +var React = require("react"); +var expect = require("chai").expect; +var renderer = TestUtils.createRenderer(); +var KeptApp = require("../KeptApp"); var KeptTextForm = require("../text/KeptTextForm"); var KeptTodoForm = require("../todo/KeptTodoForm"); describe("KeptApp", function() { - var comp, fakeStore; + var fakeStore; beforeEach(function() { fakeStore = { @@ -24,19 +19,28 @@ describe("KeptApp", function() { save: function() { } }; - comp = TestUtils.renderIntoDocument(); }); describe("#render", function() { it("should render HTML content", function() { - expect(comp.getDOMNode().outerHTML.length > 0).toBe(true); + renderer.render( + + ); + var dom = renderer.getRenderOutput(); + expect(dom.props.children.length).to.be.above(0); }); }); describe("#formCreator", function() { + var comp; + + beforeEach(function() { + comp = TestUtils.renderIntoDocument(); + }); + it("should generate a form creation function", function() { - expect(typeof comp.formCreator("text")).toEqual("function"); - expect(typeof comp.formCreator("todo")).toEqual("function"); + expect(comp.formCreator("text")).to.be.a("function"); + expect(comp.formCreator("todo")).to.be.a("function"); }); it("should add a text form component to state", function() { @@ -44,7 +48,7 @@ describe("KeptApp", function() { textForm({}); - expect(TestUtils.isComponentOfType(comp.state.form, KeptTextForm)).toBe(true); + expect(TestUtils.isElementOfType (comp.state.form, KeptTextForm)).eql(true); }); it("should add a todo form component to state", function() { @@ -52,7 +56,7 @@ describe("KeptApp", function() { todoForm({}); - expect(TestUtils.isComponentOfType(comp.state.form, KeptTodoForm)).toBe(true); + expect(TestUtils.isElementOfType (comp.state.form, KeptTodoForm)).eql(true); }); }); }); diff --git a/src/js/components/__tests__/KeptColumns-test.js b/src/js/components/__tests__/KeptColumns-test.jsx similarity index 69% rename from src/js/components/__tests__/KeptColumns-test.js rename to src/js/components/__tests__/KeptColumns-test.jsx index 3872431..66fae7e 100644 --- a/src/js/components/__tests__/KeptColumns-test.js +++ b/src/js/components/__tests__/KeptColumns-test.jsx @@ -1,11 +1,8 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; - -jest.dontMock('../KeptEntry'); -jest.dontMock('../KeptColumns'); +var React = require("react"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; var KeptEntry = require("../KeptEntry"); var KeptColumns = require("../KeptColumns"); @@ -17,7 +14,7 @@ describe("KeptColumns", function() { var items = [ {id: 1, type: "text", text: "text id #1"}, {id: 2, type: "text", text: "text id #2"}, - {id: 3, type: "text", text: "text id #3"}, + {id: 3, type: "text", text: "text id #3"} ]; var columns = 3; var col = 1; @@ -25,7 +22,7 @@ describe("KeptColumns", function() { var comp = TestUtils.renderIntoDocument(); var entries = TestUtils.scryRenderedComponentsWithType(comp, KeptEntry); - expect(entries.length).toEqual(3); + expect(entries.length).to.eql(3); }); }); }); diff --git a/src/js/components/__tests__/KeptEntry-test.js b/src/js/components/__tests__/KeptEntry-test.jsx similarity index 64% rename from src/js/components/__tests__/KeptEntry-test.js rename to src/js/components/__tests__/KeptEntry-test.jsx index 8081227..87eb18e 100644 --- a/src/js/components/__tests__/KeptEntry-test.js +++ b/src/js/components/__tests__/KeptEntry-test.jsx @@ -1,16 +1,11 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; +var React = require("react"); +var reactDom = require("react-dom"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; +var sinon = require("sinon"); -jest.dontMock('react-bootstrap/Panel'); -jest.dontMock('../KeptEntry'); -jest.dontMock('../GlyphiconLink'); -jest.dontMock('../text/KeptText'); -jest.dontMock('../todo/KeptTodo'); -var KeptEntry = require("react-bootstrap/Panel"); -var KeptEntry = require("../GlyphiconLink"); var KeptEntry = require("../KeptEntry"); var KeptText = require("../text/KeptText"); var KeptTodo = require("../todo/KeptTodo"); @@ -19,18 +14,17 @@ describe("KeptEntry", function() { var comp, fakeUpdate, fakeEdit; function renderWithProps(props) { - return TestUtils.renderIntoDocument(KeptEntry(props)); + return TestUtils.renderIntoDocument( + + ); } beforeEach(function() { - fakeUpdate = jest.genMockFn(); - fakeEdit = jest.genMockFn(); + fakeUpdate = sinon.spy(); + fakeEdit = sinon.spy(); }); - afterEach(function() { - fakeUpdate.mockClear(); - fakeEdit.mockClear(); - }); + describe("#handleClickEdit", function() { it("should call edit", function() { @@ -45,14 +39,14 @@ describe("KeptEntry", function() { edit: fakeEdit }); - TestUtils.Simulate.click(comp.getDOMNode().querySelector("h3 a.edit")); + TestUtils.Simulate.click(reactDom.findDOMNode(comp.refs.editBt)); - expect(fakeEdit).lastCalledWith({ + expect(fakeEdit.calledWith({ type: "text", id: 42, title: "test text", text: "text" - }); + })).to.eql(true); }); }); @@ -65,7 +59,7 @@ describe("KeptEntry", function() { text: "text" }}); - expect(TestUtils.findRenderedComponentWithType(comp, KeptText)).toBeTruthy(); + expect(TestUtils.findRenderedComponentWithType(comp, KeptText)).to.not.eql(null); }); it("should render a todo entry", function() { @@ -80,7 +74,7 @@ describe("KeptEntry", function() { update: fakeUpdate }); - expect(TestUtils.findRenderedComponentWithType(comp, KeptTodo)).toBeTruthy(); + expect(TestUtils.findRenderedComponentWithType(comp, KeptTodo)).to.not.eql(null); }); }); }); diff --git a/src/js/components/__tests__/KeptItems-test.js b/src/js/components/__tests__/KeptItems-test.jsx similarity index 74% rename from src/js/components/__tests__/KeptItems-test.js rename to src/js/components/__tests__/KeptItems-test.jsx index bf543fd..6c30aab 100644 --- a/src/js/components/__tests__/KeptItems-test.js +++ b/src/js/components/__tests__/KeptItems-test.jsx @@ -1,25 +1,21 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; - -jest.dontMock('../KeptItems'); -jest.dontMock('../DefaultContent'); -jest.dontMock('../KeptColumns'); +var React = require("react"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; +var sinon = require("sinon"); var KeptItems = require("../KeptItems"); var DefaultContent = require("../DefaultContent"); var KeptColumns = require("../KeptColumns"); -var range = require('../../utils').range; - describe("KeptItems", function() { describe("#render", function() { var array = [1,2,3]; - + var range; beforeEach(function(){ - range.mockReturnValue(array); + range = sinon.stub(); + range.returns(array); }); it("should render default content when items list is empty", function() { @@ -37,14 +33,14 @@ describe("KeptItems", function() { {id: 2, type: "text", text: "text id #2"}, {id: 3, type: "text", text: "text id #3"}, {id: 4, type: "text", text: "text id #3"}, - {id: 5, type: "text", text: "text id #3"}, + {id: 5, type: "text", text: "text id #3"} ]; var comp = TestUtils.renderIntoDocument(); var entries = TestUtils.scryRenderedComponentsWithType(comp, KeptColumns); - expect(entries.length).toEqual(array.length); + expect(entries).to.have.length.of(array.length); }); }); }); diff --git a/src/js/components/text/KeptText.js b/src/js/components/text/KeptText.jsx similarity index 95% rename from src/js/components/text/KeptText.js rename to src/js/components/text/KeptText.jsx index b7e718f..22b560e 100644 --- a/src/js/components/text/KeptText.js +++ b/src/js/components/text/KeptText.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); diff --git a/src/js/components/text/KeptTextForm.js b/src/js/components/text/KeptTextForm.jsx similarity index 71% rename from src/js/components/text/KeptTextForm.js rename to src/js/components/text/KeptTextForm.jsx index c90857a..9abf713 100644 --- a/src/js/components/text/KeptTextForm.js +++ b/src/js/components/text/KeptTextForm.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); @@ -26,13 +24,13 @@ var KeptTextForm = React.createClass({ }, handleSubmit: function() { - var rawId = this.refs.id.getDOMNode().value; + var rawId = this.refs.id.value; var process = rawId ? this.props.update : this.props.create; process({ type: "text", id: rawId ? parseInt(rawId, 10) : null, - title: this.refs.title.getDOMNode().value.trim(), - text: this.refs.text.getDOMNode().value.trim() + title: this.refs.title.value.trim(), + text: this.refs.text.value.trim() }); }, @@ -44,9 +42,10 @@ var KeptTextForm = React.createClass({ render: function() { return ( - -
-
+ + Create new Text + +
-
-
- -   - Cancel -
-
-
+ + + + +   + Cancel + + ); } }); diff --git a/src/js/components/text/__tests__/KeptText-test.js b/src/js/components/text/__tests__/KeptText-test.js deleted file mode 100644 index f102475..0000000 --- a/src/js/components/text/__tests__/KeptText-test.js +++ /dev/null @@ -1,22 +0,0 @@ -/** @jsx React.DOM */ - -"use strict"; - -var TestUtils = require('react/addons').addons.TestUtils; - -jest.dontMock('marked'); -jest.dontMock('../KeptText'); -var KeptText = require('../KeptText'); - -describe("#render", function() { - var comp; - - beforeEach(function() { - var data = {type: "text", text: "# plop"}; - comp = TestUtils.renderIntoDocument(); - }); - - it("should render markdown text as HTML", function() { - expect(comp.getDOMNode().querySelector("h1").textContent).toEqual("plop"); - }); -}); diff --git a/src/js/components/text/__tests__/KeptText-test.jsx b/src/js/components/text/__tests__/KeptText-test.jsx new file mode 100644 index 0000000..c7715a0 --- /dev/null +++ b/src/js/components/text/__tests__/KeptText-test.jsx @@ -0,0 +1,22 @@ +"use strict"; + +var React = require("react"); +var reactDom = require("react-dom"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; + +var KeptText = require("../KeptText"); +describe("KeptText", function(){ + describe("#render", function() { + var comp; + + beforeEach(function() { + var data = {type: "text", text: "# plop"}; + comp = TestUtils.renderIntoDocument(); + }); + + it("should render markdown text as HTML", function() { + expect(reactDom.findDOMNode(comp).querySelector("h1").textContent).to.eql("plop"); + }); + }); +}); diff --git a/src/js/components/text/__tests__/KeptTextForm-test.js b/src/js/components/text/__tests__/KeptTextForm-test.jsx similarity index 63% rename from src/js/components/text/__tests__/KeptTextForm-test.js rename to src/js/components/text/__tests__/KeptTextForm-test.jsx index e44f991..aa9f92f 100644 --- a/src/js/components/text/__tests__/KeptTextForm-test.js +++ b/src/js/components/text/__tests__/KeptTextForm-test.jsx @@ -1,12 +1,12 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; +var React = require("react"); +var reactDom = require("react-dom"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; +var sinon = require("sinon"); -jest.dontMock('react-bootstrap'); -jest.dontMock('../KeptTextForm'); -var KeptTextForm = require('../KeptTextForm'); +var KeptTextForm = require("../KeptTextForm"); describe("KeptTextForm", function() { var fakeCreate, fakeUpdate; @@ -19,14 +19,16 @@ describe("KeptTextForm", function() { }; beforeEach(function() { - fakeCreate = jest.genMockFn(); - fakeUpdate = jest.genMockFn(); + fakeCreate = sinon.spy(); + fakeUpdate = sinon.spy(); }); describe("#handleSubmit", function() { it("should create a new text", function() { var comp = TestUtils.renderIntoDocument( - ); + + ); + var form = comp.getDOMNode().querySelector("form"); TestUtils.Simulate.change(form.querySelector("input[type=text]"), @@ -35,39 +37,40 @@ describe("KeptTextForm", function() { {target: {value: "# world"}}); TestUtils.Simulate.submit(form); - expect(fakeCreate).toBeCalledWith({ + expect(fakeCreate.calledWith({ id: null, type: "text", title: "Hello", text: "# world" - }); + })).to.eql(true); }); it("should update an existing text", function() { var comp = TestUtils.renderIntoDocument( ); - var form = comp.getDOMNode().querySelector("form"); - TestUtils.Simulate.change(form.querySelector("input[type=text]"), + TestUtils.Simulate.change(comp.refs.title , {target: {value: "Plip"}}); - TestUtils.Simulate.change(form.querySelector("textarea"), + TestUtils.Simulate.change(comp.refs.text, {target: {value: "# plip"}}); - TestUtils.Simulate.submit(form); + TestUtils.Simulate.submit(comp.refs.title.form); - expect(fakeUpdate).toBeCalledWith({ + expect(fakeUpdate.calledWith({ id: 1, type: "text", title: "Plip", text: "# plip" - }); + })).to.eql(true); }); }); describe("#render", function() { it("should render HTML content", function() { - var comp = TestUtils.renderIntoDocument(); + var comp = TestUtils.renderIntoDocument( + + ); - expect(comp.getDOMNode().querySelector("form")).toBeTruthy(); + expect(reactDom.findDOMNode(comp).querySelector("form")).to.not.eql(null); }); }); }); diff --git a/src/js/components/todo/KeptTodo.js b/src/js/components/todo/KeptTodo.jsx similarity index 91% rename from src/js/components/todo/KeptTodo.js rename to src/js/components/todo/KeptTodo.jsx index cfb688d..558a47e 100644 --- a/src/js/components/todo/KeptTodo.js +++ b/src/js/components/todo/KeptTodo.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); @@ -54,9 +52,9 @@ var KeptTodo = React.createClass({ return (
-
    { +
      { this.state.tasks.map(function(task, key) { - return ; + return ; }.bind(this)) }

    Clear completed

    diff --git a/src/js/components/todo/KeptTodoForm.js b/src/js/components/todo/KeptTodoForm.jsx similarity index 65% rename from src/js/components/todo/KeptTodoForm.js rename to src/js/components/todo/KeptTodoForm.jsx index 74fd5d4..b6d6b06 100644 --- a/src/js/components/todo/KeptTodoForm.js +++ b/src/js/components/todo/KeptTodoForm.jsx @@ -1,5 +1,3 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); @@ -27,7 +25,7 @@ var KeptTodoForm = React.createClass({ }, focusLatestInput: function() { - var inputs = this.getDOMNode().querySelectorAll("input[type=text]"); + var inputs = this.refs.tasks.querySelectorAll("input[type=text]"); inputs[inputs.length - 1].focus(); }, @@ -35,13 +33,13 @@ var KeptTodoForm = React.createClass({ this.props.resetForm(); }, - handleSubmit: function() { - var id = parseInt(this.refs.id.getDOMNode().value.trim(), 10); + handleSave: function() { + var id = parseInt(this.refs.id.value.trim(), 10); var process = id ? this.props.update : this.props.create; process({ type: "todo", id: id, - title: this.refs.title.getDOMNode().value.trim(), + title: this.refs.title.value.trim(), tasks: (this.state.tasks || []).filter(function(task) { return !!task.label; }) @@ -69,32 +67,32 @@ var KeptTodoForm = React.createClass({ }, render: function() { - console.log("---"); return ( - -
    -
    + + Create new Todo + +
    -
      { +
        { this.state.tasks.map(function(task, key) { - return ; }, this) }
      -
    -
    - -   - -   - Cancel -
    -
    -
    + + + + +   + +   + Cancel + + ); } }); diff --git a/src/js/components/todo/KeptTodoTask.js b/src/js/components/todo/KeptTodoTask.jsx similarity index 90% rename from src/js/components/todo/KeptTodoTask.js rename to src/js/components/todo/KeptTodoTask.jsx index da1a582..0474a13 100644 --- a/src/js/components/todo/KeptTodoTask.js +++ b/src/js/components/todo/KeptTodoTask.jsx @@ -1,12 +1,10 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); var KeptTodoTask = React.createClass({ handleChange: function() { - this.props.toggle(this.props.key); + this.props.toggle(this.props.index); }, render: function() { diff --git a/src/js/components/todo/KeptTodoTaskForm.js b/src/js/components/todo/KeptTodoTaskForm.jsx similarity index 77% rename from src/js/components/todo/KeptTodoTaskForm.js rename to src/js/components/todo/KeptTodoTaskForm.jsx index 31ab150..d24aa3b 100644 --- a/src/js/components/todo/KeptTodoTaskForm.js +++ b/src/js/components/todo/KeptTodoTaskForm.jsx @@ -1,25 +1,22 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); var KeptTodoTaskForm = React.createClass({ handleUpdate: function() { - this.props.updateTask(this.props.key, { - label: this.refs.label.getDOMNode().value.trim(), - done: this.refs.done.getDOMNode().checked + this.props.updateTask(this.props.index, { + label: this.refs.label.value.trim(), + done: this.refs.done.checked }); }, handleRemove: function(event) { event.preventDefault(); - this.props.removeTask(this.props.key); + this.props.removeTask(this.props.index); }, render: function() { var data = this.props.data; - console.log("rendering KeptTodoTaskForm", data.label); var checkedValue = data.done ? "checked" : ""; return (
  • diff --git a/src/js/components/todo/__tests__/KeepTodo-test.js b/src/js/components/todo/__tests__/KeepTodo-test.jsx similarity index 70% rename from src/js/components/todo/__tests__/KeepTodo-test.js rename to src/js/components/todo/__tests__/KeepTodo-test.jsx index 18805f3..1ef1277 100644 --- a/src/js/components/todo/__tests__/KeepTodo-test.js +++ b/src/js/components/todo/__tests__/KeepTodo-test.jsx @@ -1,23 +1,21 @@ -/** @jsx React.DOM */ - "use strict"; -var TestUtils = require('react/addons').addons.TestUtils; +var React = require("react"); +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; +var sinon = require("sinon"); -jest.dontMock('../KeptTodoTask'); -jest.dontMock('../KeptTodo'); -jest.dontMock('react-bootstrap/ProgressBar'); -var KeptTodo = require('../KeptTodo'); +var KeptTodo = require("../KeptTodo"); describe("KeptTodo", function() { var comp, fakeUpdate; function renderWithProps(props) { - return TestUtils.renderIntoDocument(KeptTodo(props)); + return TestUtils.renderIntoDocument(); } beforeEach(function() { - fakeUpdate = jest.genMockFn(); + fakeUpdate = sinon.spy(); comp = renderWithProps({ update: fakeUpdate, data: { @@ -34,22 +32,22 @@ describe("KeptTodo", function() { }); afterEach(function() { - fakeUpdate.mockClear(); + fakeUpdate.reset(); }); describe("#getProgress", function() { it("should compute task completion percentage", function() { - expect(comp.getDOMNode().querySelector('[aria-valuenow="33"]')).toBeTruthy(); + expect(comp.getDOMNode().querySelector('[aria-valuenow="33"]')).to.not.eql(null); }); }); describe("#toggle", function() { it("should toggle a given task status", function() { - var checkboxes = comp.getDOMNode().querySelectorAll(".list-group-item input[type=checkbox]"); + var checkboxes = comp.refs.taskItems.querySelectorAll(".list-group-item input[type=checkbox]"); TestUtils.Simulate.change(checkboxes[1]); // second one, key=1 - expect(fakeUpdate).lastCalledWith({ + expect(fakeUpdate.calledWith({ type: "todo", id: 1, title: "todo", @@ -58,7 +56,7 @@ describe("KeptTodo", function() { {label: "todo2", done: false}, {label: "todo3", done: false} ] - }); + })).to.eql(true); }); }); @@ -68,7 +66,7 @@ describe("KeptTodo", function() { TestUtils.Simulate.click(clearLink); - expect(fakeUpdate).lastCalledWith({ + expect(fakeUpdate.calledWith({ type: "todo", id: 1, title: "todo", @@ -76,13 +74,13 @@ describe("KeptTodo", function() { {label: "todo1", done: false}, {label: "todo3", done: false} ] - }); + })).to.eql(true); }); }); describe("#render", function() { it("should render expected amount of entries", function() { - expect(comp.getDOMNode().querySelectorAll(".list-group-item").length).toEqual(3); + expect(comp.refs.taskItems.querySelectorAll(".list-group-item").length).to.eql(3); }); }); }); diff --git a/src/js/kept.js b/src/js/kept.jsx similarity index 53% rename from src/js/kept.js rename to src/js/kept.jsx index d8ff739..97879ce 100644 --- a/src/js/kept.js +++ b/src/js/kept.jsx @@ -1,11 +1,10 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); +var reactDom = require("react-dom"); var KeptApp = require("./components/KeptApp"); var KeptStore = require("./store"); var store = new KeptStore(); -React.renderComponent(, - document.getElementById('kept')); +reactDom.render(, + document.getElementById("kept")); diff --git a/src/js/mixins/Resize.js b/src/js/mixins/Resize.js index d06b57a..c10c7a5 100644 --- a/src/js/mixins/Resize.js +++ b/src/js/mixins/Resize.js @@ -1,29 +1,29 @@ -"use strict"; - -var Resize = { - componentDidMount: function() { - // checking if we are inside a browser - if (!window) { - return; - } - - window.addEventListener('resize', this._onResize); - this._onResize({target:window}); - }, - - componentWillUnmount: function() { - // checking if we are inside a browser - if (!window) { - return; - } - - window.removeEventListener('resize', this._onResize); - }, - - _onResize: function(event){ - if (typeof this.onResize !== "function") return; - this.onResize(event); - } -}; - -module.exports = Resize; +"use strict"; + +var Resize = { + componentDidMount: function() { + // checking if we are inside a browser + if (!window) { + return; + } + + window.addEventListener("resize", this._onResize); + this._onResize({target:window}); + }, + + componentWillUnmount: function() { + // checking if we are inside a browser + if (!window) { + return; + } + + window.removeEventListener("resize", this._onResize); + }, + + _onResize: function(event){ + if (typeof this.onResize !== "function") return; + this.onResize(event); + } +}; + +module.exports = Resize; diff --git a/src/js/mixins/__tests__/Resize-test.js b/src/js/mixins/__tests__/Resize-test.jsx similarity index 56% rename from src/js/mixins/__tests__/Resize-test.js rename to src/js/mixins/__tests__/Resize-test.jsx index 5b8313d..598d7ad 100644 --- a/src/js/mixins/__tests__/Resize-test.js +++ b/src/js/mixins/__tests__/Resize-test.jsx @@ -1,19 +1,17 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); -var TestUtils = require('react/addons').addons.TestUtils; +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; -jest.dontMock('../Resize'); -var Resize = require('../Resize'); +var Resize = require("../Resize"); var TestComp = React.createClass({ mixins: [Resize], getInitialState: function() { return { - columns: 1, + columns: 1 }; }, @@ -30,11 +28,10 @@ describe("Columns", function(){ var compo; beforeEach(function() { - compo = ; - TestUtils.renderIntoDocument(compo); + compo = TestUtils.renderIntoDocument(); }); it("should have a state columns", function(){ - expect(compo.state.columns).toBe(3); + expect(compo.state.columns).to.eql(3); }); -}); \ No newline at end of file +}); diff --git a/src/js/mixins/__tests__/UndoStack-test.js b/src/js/mixins/__tests__/UndoStack-test.jsx similarity index 66% rename from src/js/mixins/__tests__/UndoStack-test.js rename to src/js/mixins/__tests__/UndoStack-test.jsx index d5d216d..3f428e9 100644 --- a/src/js/mixins/__tests__/UndoStack-test.js +++ b/src/js/mixins/__tests__/UndoStack-test.jsx @@ -1,12 +1,10 @@ -/** @jsx React.DOM */ - "use strict"; var React = require("react"); -var TestUtils = require('react/addons').addons.TestUtils; +var TestUtils = require("react/lib/ReactTestUtils"); +var expect = require("chai").expect; -jest.dontMock('../UndoStack'); -var UndoStack = require('../UndoStack'); +var UndoStack = require("../UndoStack"); var TestComp = React.createClass({ mixins: [UndoStack], @@ -32,18 +30,17 @@ describe("UndoStack", function() { var comp; beforeEach(function() { - comp = ; - TestUtils.renderIntoDocument(comp); + comp = TestUtils.renderIntoDocument(); comp.snapshot(); }); it("should undo", function() { comp.setState({text: "v2"}); - expect(comp.state.text).toEqual("v2"); + expect(comp.state.text).to.equal("v2"); comp.undo(); - expect(comp.state.text).toEqual("v1"); + expect(comp.state.text).to.equal("v1"); }); it("should redo", function() { @@ -52,6 +49,6 @@ describe("UndoStack", function() { comp.redo(); - expect(comp.state.text).toEqual("v2"); + expect(comp.state.text).to.equal("v2"); }); }); diff --git a/src/js/utils/__tests__/utils-test.js b/src/js/utils/__tests__/utils-test.js index eb4ac52..c33c9ad 100644 --- a/src/js/utils/__tests__/utils-test.js +++ b/src/js/utils/__tests__/utils-test.js @@ -1,7 +1,9 @@ "use strict"; -jest.dontMock('..'); -var utils = require('..'); +var expect = require("chai").expect; + +var utils = require("../index.js"); + describe("nextId", function() { var items; @@ -11,23 +13,23 @@ describe("nextId", function() { }); it("should create a next id if items list is empty", function() { - expect(utils.nextId([])).toEqual(1); + expect(utils.nextId([])).to.equal(1); }); it("should find the next available id", function() { - expect(utils.nextId(items)).toEqual(2); + expect(utils.nextId(items)).to.equal(2); }); }); describe("range", function(){ it("should return an array of given lenght", function() { var array = utils.range(5); - expect(array.length).toBe(5); + expect(array.length).to.equal(5); }); it("should return an array where values equal index", function() { var array = utils.range(3); - expect(array).toEqual([0,1,2]); + expect(array).to.eql([0,1,2]); }); }); @@ -36,13 +38,13 @@ describe("permut", function(){ it("should not modify the orinal array", function() { var array = utils.permut(original, 1, 2); - expect(array).not.toEqual(original); + expect(array).not.to.equal(original); }); it("should permut 2 elements in an array", function() { var array = utils.permut(original, 1, 2); - expect(array[1]).toEqual(original[2]); - expect(array[2]).toEqual(original[1]); + expect(array[1]).to.equal(original[2]); + expect(array[2]).to.equal(original[1]); }); }); diff --git a/test/dom.js b/test/dom.js new file mode 100644 index 0000000..3334888 --- /dev/null +++ b/test/dom.js @@ -0,0 +1,7 @@ +var jsdom = require("jsdom"); + +// A super simple DOM ready for React to render into +// Store this DOM and the window in global scope ready for React to access +global.document = jsdom.jsdom(""); +global.window = document.defaultView; +global.navigator = window.navigator; diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..be8c57d --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--require test/dom.js +--compilers js:babel/register +--reporter spec