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