diff --git a/.gitignore b/.gitignore index 544f1daf6b545f..104f8c4d5e2a13 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules npm-debug.log build +coverage # Exclude compiled files lib diff --git a/.npmignore b/.npmignore index 5082f42df029b1..e6f5b877516aa2 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ build +coverage docs example -icon-builder \ No newline at end of file +icon-builder diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 00000000000000..545cfc45ad66e3 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,57 @@ +// Karma configuration +module.exports = function(config) { + config.set({ + + basePath: '', + + frameworks: [ 'browserify', 'mocha', 'chai-sinon' ], + + files: [ + 'node_modules/babel/node_modules/babel-core/browser-polyfill.js', + 'test/**/*spec.js' + ], + + preprocessors: { + 'test/**/*spec.js': [ 'browserify' ] + }, + + browserify: { + debug: true, + extensions: [ '.js', '.jsx' ], + paths: [ './node_modules', './src' ], + transform: [ + ['babelify', { + stage: 1, + sourceMap: 'inline' + }], + 'browserify-istanbul' + ] + }, + + reporters: ['mocha', 'coverage'], + + coverageReporter: { + dir: 'coverage', + + subdir: function(browser) { + return browser.toLowerCase().split(/[ /-]/)[0]; + }, + + reporters: [ + // Uncomment when 'TypeError: Cannot read property 'text' of undefined' has been addressed in Istanbul. +// { type: 'html', subdir: '.' }, + { type: 'lcovonly', subdir: '.', file: 'lcov.info' }, + { type: 'text', subdir: '.', file: 'text.txt' }, + { type: 'text-summary', subdir: '.' } + ] + }, + + port: 9876, + colors: true, + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + autoWatch: false, + singleRun: false, + browsers: [ 'PhantomJS' ] + }) +} diff --git a/package.json b/package.json index 7c41b35510f38b..772182d93157df 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "description": "Material Design UI components built with React", "main": "./lib", "scripts": { - "test": "echo \"No test implemented\" && exit 0", + "test": "npm run test-base -- --single-run", + "test-watch": "npm run test-base -- --auto-watch", + "test-base": "./node_modules/.bin/karma start", "prebuild": "rm -rf lib", "eslint": "gulp eslint", "build": "npm run eslint && babel --stage 1 ./src --out-dir ./lib", @@ -38,11 +40,26 @@ "devDependencies": { "babel": "^5.4.3", "babel-eslint": "^3.1.17", + "babelify": "^6.1.3", + "browserify-istanbul": "^0.2.1", + "chai": "^3.2.0", "eslint": "^0.23.0", "eslint-plugin-react": "^2.5.2", "gulp": "^3.9.0", "gulp-eslint": "^0.15.0", + "karma": "^0.13.3", + "karma-browserify": "^4.2.1", + "karma-chai-sinon": "^0.1.5", + "karma-coverage": "^0.4.2", + "karma-mocha": "^0.2.0", + "karma-mocha-reporter": "^1.0.4", + "karma-phantomjs-launcher": "^0.2.0", + "mocha": "^2.2.5", + "phantomjs": "^1.9.17", "react-router": "^0.13.3", - "react-tap-event-plugin": "^0.1.6" + "react-stub-context": "^0.3.0", + "react-tap-event-plugin": "^0.1.6", + "sinon": "^1.15.4", + "sinon-chai": "^2.8.0" } } diff --git a/src/checkbox.jsx b/src/checkbox.jsx index f7b811c5e27c32..e569720c4c07a0 100644 --- a/src/checkbox.jsx +++ b/src/checkbox.jsx @@ -15,10 +15,12 @@ let Checkbox = React.createClass({ }, propTypes: { + checked: React.PropTypes.bool, + checkedIcon: React.PropTypes.element, + defaultChecked: React.PropTypes.bool, iconStyle: React.PropTypes.object, labelStyle: React.PropTypes.object, onCheck: React.PropTypes.func, - checkedIcon: React.PropTypes.element, unCheckedIcon: React.PropTypes.element, }, @@ -90,7 +92,6 @@ let Checkbox = React.createClass({ unCheckedIcon, ...other, } = this.props; - let styles = this.getStyles(); let boxStyles = this.mergeAndPrefix( diff --git a/test/checkbox-spec.js b/test/checkbox-spec.js new file mode 100644 index 00000000000000..2f2df381ee3528 --- /dev/null +++ b/test/checkbox-spec.js @@ -0,0 +1,79 @@ +import React from 'react/addons'; +import Checkbox from 'checkbox'; +import injectTheme from './fixtures/inject-theme'; + +const TestUtils = React.addons.TestUtils; + + +describe('Checkbox', () => { + const CHECKMARK_PATH = 'M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'; + let ThemedCheckbox; + + + beforeEach(() => { + ThemedCheckbox = injectTheme(Checkbox); + }); + + + it('should display checkmark when checked by default', () => { + let render = TestUtils.renderIntoDocument(); + let input = TestUtils.findRenderedDOMComponentWithTag(render, 'input'); + let svgs = TestUtils.scryRenderedDOMComponentsWithTag(render, 'svg'); + let checkMarkNode = svgs[1].getDOMNode(); + + expect(input.getDOMNode().hasAttribute('checked')).to.be.true; + expect(checkMarkNode.style.opacity).to.equal('1'); + expect(checkMarkNode.firstChild.getAttribute('d')).to.equal(CHECKMARK_PATH); + }); + + + it('should NOT display checkmark when not checked by default', () => { + let render = TestUtils.renderIntoDocument(); + let input = TestUtils.findRenderedDOMComponentWithTag(render, 'input'); + let svgs = TestUtils.scryRenderedDOMComponentsWithTag(render, 'svg'); + let checkMarkNode = svgs[1].getDOMNode(); + + expect(input.getDOMNode().hasAttribute('checked')).to.be.false; + expect(checkMarkNode.style.opacity).to.equal('0'); + expect(checkMarkNode.firstChild.getAttribute('d')).to.equal(CHECKMARK_PATH); + }); + + + describe('when initially unchecked', () => { + let renderedCheckbox; + + + beforeEach(() => { + renderedCheckbox = TestUtils.renderIntoDocument(); + }); + + + it('should display checkmark when clicked once', () => { + let input = TestUtils.findRenderedDOMComponentWithTag(renderedCheckbox, 'input'); + let inputNode = input.getDOMNode(); + inputNode.checked = !inputNode.checked; + TestUtils.Simulate.change(input); + + let svgs = TestUtils.scryRenderedDOMComponentsWithTag(renderedCheckbox, 'svg'); + let checkMarkNode = svgs[1].getDOMNode(); + expect(checkMarkNode.style.opacity).to.equal('1'); + expect(checkMarkNode.firstChild.getAttribute('d')).to.equal(CHECKMARK_PATH); + }); + + + it('should NOT display checkmark when clicked twice', () => { + let input = TestUtils.findRenderedDOMComponentWithTag(renderedCheckbox, 'input'); + let inputNode = input.getDOMNode(); + // Simulate events + inputNode.checked = !inputNode.checked; + TestUtils.Simulate.change(input); + inputNode.checked = !inputNode.checked; + TestUtils.Simulate.change(input); + + let svgs = TestUtils.scryRenderedDOMComponentsWithTag(renderedCheckbox, 'svg'); + let checkMarkNode = svgs[1].getDOMNode(); + expect(checkMarkNode.style.opacity).to.equal('0'); + expect(checkMarkNode.firstChild.getAttribute('d')).to.equal(CHECKMARK_PATH); + }); + }); +}); diff --git a/test/fixtures/inject-theme.jsx b/test/fixtures/inject-theme.jsx new file mode 100644 index 00000000000000..44b959d5d97b80 --- /dev/null +++ b/test/fixtures/inject-theme.jsx @@ -0,0 +1,12 @@ +import stubContext from 'react-stub-context'; +import ThemeManager from 'styles/theme-manager'; + +const Manager = new ThemeManager(); + + +function injectTheme(Component, theme) { + let injectedTheme = theme || Manager.getCurrentTheme(); + return stubContext(Component, {muiTheme: injectedTheme}); +} + +module.exports = injectTheme; diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000000000..63d9c8ae556a71 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--require chai +--reporter html +--ui bdd