├── .gitignore ├── .travis.yml ├── src ├── actions │ └── MemberActions.js ├── stores │ ├── MemberStore.js │ └── __tests__ │ │ └── MemberStore-test.js └── components │ ├── __tests__ │ └── MemberList-test.js │ └── MemberList.jsx ├── LICENSE ├── README.md ├── package.json ├── karma.conf.js └── tests.webpack.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "4.0" 5 | - "0.12" 6 | - "0.11" 7 | - "0.10" 8 | - "iojs" 9 | -------------------------------------------------------------------------------- /src/actions/MemberActions.js: -------------------------------------------------------------------------------- 1 | var Reflux = require('reflux'); 2 | 3 | var MemberActions = Reflux.createActions([ 4 | 'addMember', 5 | ]); 6 | 7 | module.exports = MemberActions; 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2015 Kenneth Chung 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/kentor/react-flux-testing.svg)](https://travis-ci.org/kentor/react-flux-testing) 2 | 3 | Sample repository of testing React and Flux (Reflux) with Karma, 4 | Jasmine, and Webpack. 5 | 6 | `npm install` then `npm test`. 7 | 8 | Note: If you haven't already started a React project, I'd recommend using [Redux](https://github.com/rackt/redux) rather than some form of Flux. Redux reducers is a lot easier to test than Flux stores, and does not need to use the technique shown in this repo. 9 | -------------------------------------------------------------------------------- /src/stores/MemberStore.js: -------------------------------------------------------------------------------- 1 | var Immutable = require('immutable'); 2 | var MemberActions = require('../actions/MemberActions'); 3 | var Reflux = require('reflux'); 4 | 5 | var members = Immutable.List(); 6 | 7 | var MemberStore = Reflux.createStore({ 8 | listenables: [ 9 | MemberActions, 10 | ], 11 | 12 | members() { 13 | return members; 14 | }, 15 | 16 | onAddMember(name) { 17 | members = members.push(Immutable.Map({ name })); 18 | this.triggerAsync(); 19 | }, 20 | }); 21 | 22 | module.exports = MemberStore; 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-flux-testing", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "test": "karma start karma.conf.js" 6 | }, 7 | "author": "Kenneth Chung", 8 | "dependencies": { 9 | "babel-core": "^5.8.22", 10 | "babel-loader": "^5.3.2", 11 | "core-js": "^1.1.0", 12 | "immutable": "^3.7.4", 13 | "jasmine-core": "^2.3.4", 14 | "karma": "^0.13.9", 15 | "karma-cli": "0.1.0", 16 | "karma-jasmine": "^0.3.6", 17 | "karma-phantomjs-launcher": "^0.2.1", 18 | "karma-webpack": "^1.7.0", 19 | "node-libs-browser": "^0.5.2", 20 | "phantomjs": "^1.9.18", 21 | "react": "^0.13.3", 22 | "reflux": "0.2.10", 23 | "webpack": "^1.11.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | browsers: [ 4 | 'PhantomJS', 5 | ], 6 | files: [ 7 | { 8 | pattern: 'tests.webpack.js', 9 | watched: false, 10 | }, 11 | ], 12 | frameworks: [ 13 | 'jasmine', 14 | ], 15 | preprocessors: { 16 | 'tests.webpack.js': [ 17 | 'webpack', 18 | ], 19 | }, 20 | reporters: [ 21 | 'dots', 22 | ], 23 | singleRun: true, 24 | webpack: { 25 | module: { 26 | loaders: [ 27 | { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader' }, 28 | ], 29 | }, 30 | watch: true, 31 | }, 32 | webpackServer: { 33 | noInfo: true, 34 | }, 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /src/stores/__tests__/MemberStore-test.js: -------------------------------------------------------------------------------- 1 | describe('MemberStore', () => { 2 | var MemberActions; 3 | var MemberStore; 4 | 5 | beforeEach(() => { 6 | MemberActions = require('../../actions/MemberActions'); 7 | MemberStore = require('../MemberStore'); 8 | 9 | MemberActions.addMember('foo'); 10 | jasmine.clock().tick(); 11 | }); 12 | 13 | it('can add a member', () => { 14 | var members = MemberStore.members(); 15 | expect(members.toJS()).toEqual([{ name: 'foo' }]); 16 | }); 17 | 18 | it('busts the require cache', () => { 19 | MemberActions.addMember('bar'); 20 | jasmine.clock().tick(); 21 | 22 | var members = MemberStore.members(); 23 | expect(members.toJS()).toEqual([{ name: 'foo' }, { name: 'bar' }]); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/__tests__/MemberList-test.js: -------------------------------------------------------------------------------- 1 | describe('MemberList', () => { 2 | var MemberList; 3 | var React = require('react'); 4 | var TestUtils = require('react/lib/ReactTestUtils'); 5 | 6 | beforeEach(() => { 7 | MemberList = require('../MemberList.jsx') 8 | }); 9 | 10 | it('can add a new member', () => { 11 | var c = TestUtils.renderIntoDocument(); 12 | var input = React.findDOMNode(c.refs.input); 13 | var submit = React.findDOMNode(c.refs.submit); 14 | 15 | expect(React.findDOMNode(c).textContent).toBe(''); 16 | 17 | TestUtils.Simulate.change(input, { target: { value: 'baz' } }); 18 | TestUtils.Simulate.click(submit); 19 | jasmine.clock().tick(1); 20 | 21 | expect(React.findDOMNode(c).textContent).toBe('baz'); 22 | expect(input.value).toBe(''); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/MemberList.jsx: -------------------------------------------------------------------------------- 1 | var MemberActions = require('../actions/MemberActions'); 2 | var MemberStore = require('../stores/MemberStore'); 3 | var React = require('react/addons'); 4 | var Reflux = require('reflux'); 5 | 6 | var MemberList = React.createClass({ 7 | mixins: [ 8 | React.addons.LinkedStateMixin, 9 | Reflux.ListenerMixin, 10 | ], 11 | 12 | getInitialState() { 13 | return { 14 | members: MemberStore.members(), 15 | newMemberName: '', 16 | }; 17 | }, 18 | 19 | componentDidMount() { 20 | this.listenTo(MemberStore, () => { 21 | this.setState(this.getInitialState()); 22 | }); 23 | }, 24 | 25 | addMember() { 26 | MemberActions.addMember(this.state.newMemberName); 27 | this.setState({ newMemberName: '' }); 28 | }, 29 | 30 | render() { 31 | return ( 32 |
33 | 38 | 39 | 40 |
42 | ); 43 | }, 44 | }); 45 | 46 | module.exports = MemberList; 47 | -------------------------------------------------------------------------------- /tests.webpack.js: -------------------------------------------------------------------------------- 1 | var Immutable = require('immutable'); 2 | 3 | /** 4 | * ES5 polyfills for PhantomJS 5 | */ 6 | require('core-js/es5'); 7 | 8 | /** 9 | * Create a set of webpack module ids for our project's modules, excluding 10 | * tests. This will be used to clear the module cache before each test. 11 | */ 12 | var projectContext = require.context('./src', true, /^((?!__tests__).)*.jsx?$/); 13 | var projectModuleIds = Immutable.Set( 14 | projectContext.keys().map(module => ( 15 | String(projectContext.resolve(module)) 16 | )) 17 | ); 18 | 19 | beforeEach(() => { 20 | /** 21 | * Clear the module cache before each test. Many of our modules, such as 22 | * Stores and Actions, are singletons that have state that we don't want to 23 | * carry over between tests. Clearing the cache makes `require(module)` 24 | * return a new instance of the singletons. Modules are still cached within 25 | * each test case. 26 | */ 27 | var cache = require.cache; 28 | projectModuleIds.forEach(id => delete cache[id]); 29 | 30 | /** 31 | * Automatically mock the built in setTimeout and setInterval functions. 32 | */ 33 | jasmine.clock().install(); 34 | }); 35 | 36 | afterEach(() => { 37 | jasmine.clock().uninstall(); 38 | }); 39 | 40 | /** 41 | * Load each test using webpack's dynamic require with contexts. 42 | */ 43 | var context = require.context('./src', true, /-test\.js?$/); 44 | context.keys().forEach(context); 45 | --------------------------------------------------------------------------------