diff --git a/.eslintrc b/.eslintrc index d3a7a2e1f924ad48169b5580f6731ead68d8613c..3bd5636c76bed7367a8feb3732798827c90dc101 100755 --- a/.eslintrc +++ b/.eslintrc @@ -4,7 +4,9 @@ "env": { "browser": true, "node": true, - "es6": true + "es6": true, + "mocha": true, + "jasmine": true }, "rules": { "generator-star-spacing": [0], diff --git a/.gitignore b/.gitignore index d9750f32880bac5f287c460d94a883e9f180feb1..c03ea9a927fda38c111c59035773baf37eb2c635 100755 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ # misc .DS_Store npm-debug.log* + +/coverage diff --git a/.travis.yml b/.travis.yml index bdb3d6a82407732dc1442774f4dcb25f90ac58b9..7115c31653ef166ac6c8bc9535d0a6a5c49e8c4d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,32 @@ language: node_js node_js: - "8" + +env: + matrix: + - TEST_TYPE=lint + - TEST_TYPE=test-all + - TEST_TYPE=test-dist + +addons: + apt: + packages: + - xvfb + +install: + - export DISPLAY=':99.0' + - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + - npm install + +script: + - | + if [ "$TEST_TYPE" = lint ]; then + npm run lint + elif [ "$TEST_TYPE" = test-all ]; then + npm run test:all + elif [ "$TEST_TYPE" = test-dist ]; then + npm run site + mv dist/* ./ + php -S localhost:8000 & + npm test .e2e.js + fi diff --git a/package.json b/package.json index ceef972cfd8bc30d1bcc239eb2347ecf411b5677..4e43a268672238be5139fc11a2a006f72c410355 100755 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "scripts": { "start": "roadhog server", "build": "roadhog build", - "lint": "eslint --ext .js src test mock", - "test": "npm run lint", + "lint": "eslint --ext .js src mock tests", "precommit": "npm run lint", - "site": "roadhog-api-doc static" + "site": "roadhog-api-doc static", + "test": "jest", + "test:all": "node ./tests/run-tests.js" }, "dependencies": { "antd": "next", @@ -25,11 +26,14 @@ }, "devDependencies": { "babel-eslint": "^7.1.1", + "babel-jest": "^21.0.0", "babel-plugin-dva-hmr": "^0.3.2", "babel-plugin-import": "^1.2.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-runtime": "^6.9.0", "babel-runtime": "^6.9.2", + "cross-port-killer": "^1.0.1", + "enzyme": "^2.9.1", "eslint": "^3.0.0", "eslint-config-airbnb": "latest", "eslint-plugin-babel": "^4.0.0", @@ -39,9 +43,38 @@ "eslint-plugin-react": "^7.0.1", "gh-pages": "^1.0.0", "husky": "^0.13.4", + "jest": "^21.0.1", "mockjs": "^1.0.1-beta3", + "nightmare": "^2.10.0", + "react-test-renderer": "^15.6.1", "redbox-react": "^1.3.2", "roadhog": "^1.0.2", "roadhog-api-doc": "^0.1.5" + }, + "babel": { + "presets": [ + "es2015", + "stage-0", + "react" + ], + "plugins": [ + "transform-decorators-legacy" + ] + }, + "jest": { + "setupFiles": [ + "/tests/setupTests.js" + ], + "testMatch": [ + "**/?(*.)(spec|test|e2e).js?(x)" + ], + "setupTestFrameworkScriptFile": "/tests/jasmine.js", + "moduleFileExtensions": [ + "js", + "jsx" + ], + "moduleNameMapper": { + "\\.(css|less)$": "/tests/styleMock.js" + } } } diff --git a/src/components/Result/index.js b/src/components/Result/index.js index 4af5310f01855e1be4439bd15c7cb8ddd5d5d503..389a4ef55cd4880a289a99b360d9d5f1273aec60 100644 --- a/src/components/Result/index.js +++ b/src/components/Result/index.js @@ -3,7 +3,9 @@ import classNames from 'classnames'; import { Icon } from 'antd'; import styles from './index.less'; -export default ({ className, type, title, description, extra, actions, ...restProps }) => { +export default function Result({ + className, type, title, description, extra, actions, ...restProps +}) { const iconMap = { error: , success: , @@ -18,4 +20,4 @@ export default ({ className, type, title, description, extra, actions, ...restPr {actions &&
{actions}
} ); -}; +} diff --git a/src/e2e/home.e2e.js b/src/e2e/home.e2e.js new file mode 100644 index 0000000000000000000000000000000000000000..6de577b969659d8e5727133fe5d33d06d6e295cf --- /dev/null +++ b/src/e2e/home.e2e.js @@ -0,0 +1,9 @@ +import Nightmare from 'nightmare'; + +describe('Homepage', () => { + it('it should have logo text', async () => { + const page = Nightmare().goto('http://localhost:8000'); + const text = await page.evaluate(() => document.body.innerHTML).end(); + expect(text).toContain('

Ant Design Pro

'); + }); +}); diff --git a/src/e2e/login.e2e.js b/src/e2e/login.e2e.js new file mode 100644 index 0000000000000000000000000000000000000000..2e46578ee3aa7da5ddb1dfe6414f3f0511d8b99a --- /dev/null +++ b/src/e2e/login.e2e.js @@ -0,0 +1,27 @@ +import Nightmare from 'nightmare'; + +describe('Login', () => { + let page; + beforeEach(() => { + page = Nightmare(); + page.goto('http://localhost:8000/#/user/login'); + }); + + it('should login with failure', async () => { + await page.type('#userName', 'mockuser') + .type('#password', 'wrong_password') + .click('button[type="submit"]') + .wait('.ant-alert-error') // should display error + .end(); + }); + + xit('should login successfully', async () => { + const text = await page.type('#userName', 'admin') + .type('#password', '888888') + .click('button[type="submit"]') + .wait('.ant-layout-sider h1') // should display error + .evaluate(() => document.body.innerHTML) + .end(); + expect(text).toContain('

Ant Design Pro

'); + }); +}); diff --git a/src/routes/Dashboard.test.js b/src/routes/Dashboard.test.js new file mode 100644 index 0000000000000000000000000000000000000000..5e28863f6a404faf3854b0856fd540e3dbf3ac53 --- /dev/null +++ b/src/routes/Dashboard.test.js @@ -0,0 +1,10 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Dashboard from './Dashboard'; + +it('renders Dashboard', () => { + const wrapper = shallow( + + ); + expect(wrapper.find('Table').props().dataSource).toEqual([]); +}); diff --git a/src/routes/Result/Success.test.js b/src/routes/Result/Success.test.js new file mode 100644 index 0000000000000000000000000000000000000000..9bc9b8dffdc39e5db8dbfd5b2c9e5203a4845e26 --- /dev/null +++ b/src/routes/Result/Success.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Success from './Success'; + +it('renders with Result', () => { + const wrapper = shallow(); + expect(wrapper.find('Result').length).toBe(1); + expect(wrapper.find('Result').prop('type')).toBe('success'); +}); diff --git a/src/utils/utils.js b/src/utils/utils.js index 473665033232996dbac9f9bb911c28ba6ca918ea..18b3284d25984f27e25d1249fb3d45a274f9b93b 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,10 +1,10 @@ import moment from 'moment'; -function fixedZero(val) { +export function fixedZero(val) { return val * 1 < 10 ? `0${val}` : val; } -function getTimeDistance(type) { +export function getTimeDistance(type) { const now = new Date(); const oneDay = 1000 * 60 * 60 * 24; @@ -48,8 +48,3 @@ function getTimeDistance(type) { return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)]; } } - -export default { - fixedZero, - getTimeDistance, -}; diff --git a/tests/jasmine.js b/tests/jasmine.js new file mode 100644 index 0000000000000000000000000000000000000000..5ff26bff5210628b9fdff3152e29afd646a8e164 --- /dev/null +++ b/tests/jasmine.js @@ -0,0 +1 @@ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; diff --git a/tests/run-tests.js b/tests/run-tests.js new file mode 100644 index 0000000000000000000000000000000000000000..adf22a67026b7299c78b9d74d3e02b255e7be93e --- /dev/null +++ b/tests/run-tests.js @@ -0,0 +1,32 @@ +const { spawn } = require('child_process'); +const { kill } = require('cross-port-killer'); + +const env = Object.create(process.env); +env.BROWSER = 'none'; +const startServer = spawn('npm', ['start'], { + env, +}); + +startServer.stderr.on('data', (data) => { + // eslint-disable-next-line + console.log(data); +}); + +startServer.on('exit', () => { + kill(process.env.PORT || 8000); +}); + +// eslint-disable-next-line +console.log('Starting development server for e2e tests...'); +startServer.stdout.on('data', (data) => { + if (data.toString().indexOf('The app is running at') >= 0) { + // eslint-disable-next-line + console.log('Development server is started, ready to run tests.'); + const testCmd = spawn('npm', ['run', 'jest'], { + stdio: 'inherit', + }); + testCmd.on('exit', () => { + startServer.kill(); + }); + } +}); diff --git a/tests/setupTests.js b/tests/setupTests.js new file mode 100644 index 0000000000000000000000000000000000000000..51ed96ee2f248c677c371e31ac5553102f76acb6 --- /dev/null +++ b/tests/setupTests.js @@ -0,0 +1,11 @@ +import { jsdom } from 'jsdom'; + +// fixed jsdom miss +const documentHTML = '
'; +global.document = jsdom(documentHTML); +global.window = document.defaultView; +global.navigator = global.window.navigator; + +global.requestAnimationFrame = global.requestAnimationFrame || function requestAnimationFrame(cb) { + return setTimeout(cb, 0); +}; diff --git a/tests/styleMock.js b/tests/styleMock.js new file mode 100644 index 0000000000000000000000000000000000000000..f053ebf7976e3726d11f3c03fade2170903889a5 --- /dev/null +++ b/tests/styleMock.js @@ -0,0 +1 @@ +module.exports = {};