);
9 | // expect(wrapper.type()).to.eql('div');
10 | // });
11 |
12 | // it('has style with height 100%', () => {
13 | // const wrapper = shallow(
);
14 | // const expectedStyles = {
15 | // height: '100%',
16 | // background: '#333'
17 | // }
18 | // expect(wrapper.prop('style')).to.eql(expectedStyles);
19 | // });
20 |
21 | // it('contains a header explaining the app', () => {
22 | // const wrapper = shallow(
);
23 | // expect(wrapper.find('.welcome-header')).to.have.length(1);
24 | // });
25 | // });
26 |
27 | // Refactored tests
28 | describe('(Container) Root', () => {
29 | const wrapper = shallow(
', () => {
32 | expect(wrapper.type()).to.eql('div');
33 | });
34 |
35 | it('has style with height 100%', () => {
36 | const expectedStyles = {
37 | height: '100%',
38 | background: '#333'
39 | }
40 | expect(wrapper.prop('style')).to.eql(expectedStyles);
41 | });
42 |
43 | it('contains a header explaining the app', () => {
44 | expect(wrapper.find('.welcome-header')).to.have.length(1);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-testing-starter-kit",
3 | "version": "0.1.0",
4 | "description": "React starter kit with nice testing environment set up.",
5 | "main": "src/main.js",
6 | "directories": {
7 | "test": "tests",
8 | "src": "src",
9 | "dist": "dist"
10 | },
11 | "dependencies": {
12 | "react": "^0.14.6",
13 | "react-dom": "^0.14.6",
14 | "yargs": "^3.31.0"
15 | },
16 | "devDependencies": {
17 | "babel-core": "^6.4.0",
18 | "babel-loader": "^6.2.1",
19 | "babel-polyfill": "^6.3.14",
20 | "babel-preset-es2015": "^6.3.13",
21 | "babel-preset-react": "^6.3.13",
22 | "babel-register": "^6.3.13",
23 | "chai": "^3.4.1",
24 | "enzyme": "^1.2.0",
25 | "json-loader": "^0.5.4",
26 | "karma": "^0.13.19",
27 | "karma-chai": "^0.1.0",
28 | "karma-mocha": "^0.2.1",
29 | "karma-phantomjs-launcher": "^0.2.3",
30 | "karma-sourcemap-loader": "^0.3.6",
31 | "karma-spec-reporter": "0.0.23",
32 | "karma-webpack": "^1.7.0",
33 | "mocha": "^2.3.4",
34 | "phantomjs": "^1.9.19",
35 | "phantomjs-polyfill": "0.0.1",
36 | "react-addons-test-utils": "^0.14.6",
37 | "sinon": "^1.17.2",
38 | "webpack": "^1.12.11",
39 | "webpack-dev-server": "^1.14.1"
40 | },
41 | "scripts": {
42 | "test": "node_modules/.bin/karma start karma.config.js",
43 | "test:dev": "npm run test -- --watch",
44 | "build": "webpack",
45 | "dev": "webpack-dev-server --port 3000 --devtool eval --progress --colors --hot --content-base dist",
46 | "old_test": "mocha --compilers js:babel-register --require ./test/test_helper.js --recursive",
47 | "old_test:watch": "npm test -- --watch"
48 | },
49 | "repository": {
50 | "type": "git",
51 | "url": "tbd"
52 | },
53 | "keywords": [
54 | "react",
55 | "mocha",
56 | "chai",
57 | "enzyme",
58 | "testing"
59 | ],
60 | "author": "Spencer Dixon",
61 | "license": "ISC"
62 | }
63 |
--------------------------------------------------------------------------------
/karma.config.js:
--------------------------------------------------------------------------------
1 | var argv = require('yargs').argv;
2 | var path = require('path');
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | browsers: ['PhantomJS'],
7 | singleRun: !argv.watch, // just run once by default
8 | frameworks: ['mocha', 'chai'],
9 | // npm i karma-spec-reporter --save-dev
10 | // displays tests in a nice readable format
11 | reporters: ['spec'],
12 |
13 | // include some polyfills for babel and phantomjs
14 | files: [
15 | 'node_modules/babel-polyfill/dist/polyfill.js',
16 | './node_modules/phantomjs-polyfill/bind-polyfill.js',
17 | './test/**/*.js' // specify files to watch for tests
18 | ],
19 | preprocessors: {
20 | // these files we want to be precompiled with webpack
21 | // also run tests throug sourcemap for easier debugging
22 | ['./test/**/*.js']: ['webpack', 'sourcemap']
23 | },
24 | webpack: {
25 | devtool: 'inline-source-map',
26 | resolve: {
27 | // allow us to import components in tests like:
28 | // import Example from 'components/Example';
29 | root: path.resolve(__dirname, './src'),
30 |
31 | // allow us to avoid including extension name
32 | extensions: ['', '.js', '.jsx'],
33 |
34 | // required for enzyme to work properly
35 | alias: {
36 | 'sinon': 'sinon/pkg/sinon'
37 | }
38 | },
39 | module: {
40 | // don't run babel-loader through the sinon module
41 | noParse: [
42 | /node_modules\/sinon\//
43 | ],
44 | // run babel loader for our tests
45 | loaders: [
46 | { test: /\.js?$/, exclude: /node_modules/, loader: 'babel' },
47 | ],
48 | },
49 | // required for enzyme to work properly
50 | externals: {
51 | 'jsdom': 'window',
52 | 'cheerio': 'window',
53 | 'react/lib/ExecutionEnvironment': true,
54 | 'react/lib/ReactContext': 'window'
55 | },
56 | },
57 | webpackMiddleware: {
58 | noInfo: true
59 | },
60 | // tell karma all the plugins we're going to be using
61 | plugins: [
62 | 'karma-mocha',
63 | 'karma-chai',
64 | 'karma-webpack',
65 | 'karma-phantomjs-launcher',
66 | 'karma-spec-reporter',
67 | 'karma-sourcemap-loader'
68 | ]
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/test/components/CommentList.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | // Once we set up Karma to run our tests through webpack
4 | // we will no longer need to have these long relative paths
5 | import CommentList from '../../src/components/CommentList';
6 | import {
7 | describeWithDOM,
8 | mount,
9 | shallow,
10 | spyLifecycle
11 | } from 'enzyme';
12 |
13 | describe('(Component) CommentList', () => {
14 |
15 | // using special describeWithDOM helper that enzyme
16 | // provides so if other devs on my team don't have JSDom set up
17 | // properly or are using old version of node it won't bork their test suite
18 | //
19 | // All of our tests that depend on mounting should go inside one of these
20 | // special describe blocks
21 | describeWithDOM('Lifecycle methods', () => {
22 | it('calls componentDidMount', () => {
23 | spyLifecycle(CommentList);
24 |
25 | const props = {
26 | onMount: () => {},
27 | isActive: false
28 | }
29 |
30 | // using destructuring to pass props down
31 | // easily and then mounting the component
32 | mount(
);
33 |
34 | // CommentList's componentDidMount should have been
35 | // called once. spyLifecyle attaches sinon spys so we can
36 | // make this assertion
37 | expect(
38 | CommentList.prototype.componentDidMount.calledOnce
39 | ).to.be.true;
40 | });
41 |
42 | it('calls onMount prop once it mounts', () => {
43 | // create a spy for the onMount function
44 | const props = { onMount: sinon.spy() };
45 |
46 | // mount our component
47 | mount(
);
48 |
49 | // expect that onMount was called
50 | expect(props.onMount.calledOnce).to.be.true;
51 | });
52 | });
53 |
54 | it('should render as a
', () => {
55 | const props = { onMount: () => {} };
56 | const wrapper = shallow();
57 | expect(wrapper.type()).to.eql('ul');
58 | });
59 |
60 | describe('when active...', () => {
61 | const wrapper = shallow(
62 | // just passing isActive is an alias for true
63 | {}} isActive />
64 | )
65 | it('should render with className active-list', () => {
66 | expect(wrapper.prop('className')).to.eql('active-list');
67 | });
68 | });
69 |
70 | describe('when inactive...', () => {
71 | const wrapper = shallow(
72 | // just passing isActive is an alias for true
73 | {}} isActive={false} />
74 | )
75 | it('should render with className inactive-list', () => {
76 | expect(wrapper.prop('className')).to.eql('inactive-list');
77 | });
78 | });
79 | });
80 |
--------------------------------------------------------------------------------