Skip to content

Commit

Permalink
Start of unit test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
jkruder committed Aug 3, 2015
1 parent 70e6694 commit 40eaf29
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
npm-debug.log
build
coverage

# Exclude compiled files
lib
Expand Down
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build
coverage
docs
example
icon-builder
icon-builder
59 changes: 59 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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/fixtures/react-parent-context-patch.js',
'test/**/*spec.js'
],

preprocessors: {
// 'test/fixtures/react-parent-context-patch.js': [ 'browserify' ],
'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' ]
})
}
20 changes: 18 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -38,11 +40,25 @@
"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-tap-event-plugin": "^0.1.6",
"sinon": "^1.15.4",
"sinon-chai": "^2.8.0"
}
}
5 changes: 3 additions & 2 deletions src/checkbox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},

Expand Down Expand Up @@ -90,7 +92,6 @@ let Checkbox = React.createClass({
unCheckedIcon,
...other,
} = this.props;

let styles = this.getStyles();
let boxStyles =
this.mergeAndPrefix(
Expand Down
30 changes: 30 additions & 0 deletions test/checkbox-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react/addons';
import Checkbox from 'checkbox';
import ThemeContainer from './fixtures/theme-container';
import stubContext from 'react-stub-context';

import ThemeManager from 'styles/theme-manager';

const TestUtils = React.addons.TestUtils;
const Manager = new ThemeManager();


describe('Checkbox', () => {
let checkboxComponent;

it('should display checkmark when checked by default', () => {
// checkboxComponent = TestUtils.renderIntoDocument(
// <ThemeContainer>
// <Checkbox defaultChecked={true} checked={false} />
// </ThemeContainer>
// ).getBaseComponent()

let NewCheckbox = stubContext(Checkbox, {muiTheme: Manager.getCurrentTheme()});
checkboxComponent = TestUtils.renderIntoDocument(<NewCheckbox defaultChecked={true} />);
console.log(TestUtils.scryRenderedComponentsWithType(checkboxComponent, 'Checkbox'));

// console.log(Object.keys(checkboxComponent._wrappedElement));
// console.log(checkboxComponent.getWrappedElement().isChecked() !== undefined);
//expect(checkboxComponent.isChecked()).to.be.true;
});
});
191 changes: 191 additions & 0 deletions test/fixtures/react-parent-context-patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
var ReactInstanceMap = require("react/lib/ReactInstanceMap");
var ReactLifeCycle = require("react/lib/ReactLifeCycle");
var ReactNativeComponent = require("react/lib/ReactNativeComponent");
var ReactReconciler = require("react/lib/ReactReconciler");

var emptyObject = require("react/lib/emptyObject");
var invariant = require("react/lib/invariant");
var warning = require("react/lib/warning");

var ReactCompositeComponentMixin = require('react/lib/ReactCompositeComponent').Mixin;

var {mountComponent, updateComponent} = ReactCompositeComponentMixin;
var nextMountID = 1;


ReactCompositeComponentMixin.mountComponent = function(rootID, transaction, context) {
this._context = context;
this._mountOrder = nextMountID++;
this._rootNodeID = rootID;

var publicProps = this._processProps(this._currentElement.props);
var publicContext = this._processContext(context);

var Component = ReactNativeComponent.getComponentClassForElement(
this._currentElement
);

// Initialize the public class
var inst = new Component(publicProps, publicContext);
// These should be set up in the constructor, but as a convenience for
// simpler class abstractions, we set them up after the fact.
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;

this._instance = inst;

// Store a reference from the instance back to the internal representation
ReactInstanceMap.set(inst, this);

if ("production" !== process.env.NODE_ENV) {
// Since plain JS classes are defined without any special initialization
// logic, we can not catch common errors early. Therefore, we have to
// catch them here, at initialization time, instead.
("production" !== process.env.NODE_ENV ? warning(
!inst.getInitialState ||
inst.getInitialState.isReactClassApproved,
'getInitialState was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Did you mean to define a state property instead?',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
!inst.propTypes,
'propTypes was defined as an instance property on %s. Use a static ' +
'property to define propTypes instead.',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
!inst.contextTypes,
'contextTypes was defined as an instance property on %s. Use a ' +
'static property to define contextTypes instead.',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
typeof inst.componentShouldUpdate !== 'function',
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
(this.getName() || 'A component')
) : null);
}

var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
("production" !== process.env.NODE_ENV ? invariant(
typeof initialState === 'object' && !Array.isArray(initialState),
'%s.state: must be set to an object or null',
this.getName() || 'ReactCompositeComponent'
) : invariant(typeof initialState === 'object' && !Array.isArray(initialState)));

this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;

var renderedElement;

var previouslyMounting = ReactLifeCycle.currentlyMountingInstance;
ReactLifeCycle.currentlyMountingInstance = this;
try {
if (inst.componentWillMount) {
inst.componentWillMount();
// When mounting, calls to `setState` by `componentWillMount` will set
// `this._pendingStateQueue` without triggering a re-render.
if (this._pendingStateQueue) {
inst.state = this._processPendingState(inst.props, inst.context);
}
}

renderedElement = this._renderValidatedComponent();
} finally {
ReactLifeCycle.currentlyMountingInstance = previouslyMounting;
}

this._renderedComponent = this._instantiateReactComponent(
renderedElement,
this._currentElement.type // The wrapping type
);

var markup = ReactReconciler.mountComponent(
this._renderedComponent,
rootID,
transaction,
this._processChildContext(context)
);
if (inst.componentDidMount) {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}

return markup;
};

ReactCompositeComponentMixin.updateComponent = function(
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext
) {
var inst = this._instance;

var nextContext = inst.context;
var nextProps = inst.props;

// Distinguish between a props update versus a simple state update
if (prevParentElement !== nextParentElement) {
nextContext = this._processContext(nextUnmaskedContext);
nextProps = this._processProps(nextParentElement.props);

// An update here will schedule an update but immediately set
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.

if (inst.componentWillReceiveProps) {
inst.componentWillReceiveProps(nextProps, nextContext);
}
}

var nextState = this._processPendingState(nextProps, nextContext);

var shouldUpdate =
this._pendingForceUpdate ||
!inst.shouldComponentUpdate ||
inst.shouldComponentUpdate(nextProps, nextState, nextContext);

if ("production" !== process.env.NODE_ENV) {
("production" !== process.env.NODE_ENV ? warning(
typeof shouldUpdate !== 'undefined',
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
'boolean value. Make sure to return true or false.',
this.getName() || 'ReactCompositeComponent'
) : null);
}

if (shouldUpdate) {
this._pendingForceUpdate = false;
// Will set `this.props`, `this.state` and `this.context`.
this._performComponentUpdate(
nextParentElement,
nextProps,
nextState,
nextContext,
transaction,
nextUnmaskedContext
);
} else {
// If it's determined that a component should not update, we still want
// to set props and state but we shortcut the rest of the update.
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
};


console.warn('Applied react-parent-context-patch!');
Loading

0 comments on commit 40eaf29

Please sign in to comment.