From acac4b328632c6e6d238512ebcdf3d40c57963e4 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Fri, 13 May 2016 20:04:56 -0700 Subject: [PATCH] Add dynamic tests --- client/less/challenge.less | 16 +++++-- .../challenges/components/Challenge.jsx | 20 ++++---- .../challenges/components/Side-Panel.jsx | 37 +++++++++++---- .../challenges/components/Test-Suite.jsx | 47 +++++-------------- common/app/routes/challenges/redux/reducer.js | 6 ++- common/app/routes/challenges/utils.js | 11 ++++- 6 files changed, 73 insertions(+), 64 deletions(-) diff --git a/client/less/challenge.less b/client/less/challenge.less index 2302876a9e..ca77c61eb8 100644 --- a/client/less/challenge.less +++ b/client/less/challenge.less @@ -117,17 +117,23 @@ color: @gray-light; } -.big-error-icon { +.big-icon { font-size: 30px; - color: @brand-danger; - top:50%; } -.big-success-icon { - font-size: 30px; +.error-icon { + color: @brand-danger; + top: 50%; +} + +.success-icon { color: @brand-primary; } +.refresh-icon { + color: @alert-info-bg; +} + iframe.iphone { border: none; @media(min-width: 992px) { diff --git a/common/app/routes/challenges/components/Challenge.jsx b/common/app/routes/challenges/components/Challenge.jsx index 35196a4d70..a1c3ad4b9d 100644 --- a/common/app/routes/challenges/components/Challenge.jsx +++ b/common/app/routes/challenges/components/Challenge.jsx @@ -11,13 +11,14 @@ import { challengeSelector } from '../redux/selectors'; const mapStateToProps = createSelector( challengeSelector, + state => state.challengesApp.tests, state => state.challengesApp.files, state => state.challengesApp.path, - ({ challenge, showPreview, mode }, files, path) => ({ - content: files[path] && files[path].contents, - challenge, + ({ challenge, showPreview, mode }, tests, files = {}, path = '') => ({ + content: files[path] && files[path].contents || '', showPreview, - mode + mode, + tests }) ); @@ -26,11 +27,8 @@ export class Challenge extends PureComponent { static propTypes = { showPreview: PropTypes.bool, - challenge: PropTypes.object - }; - - static defaultProps = { - challenge: {} + content: PropTypes.string, + mode: PropTypes.string }; renderPreview(showPreview) { @@ -47,13 +45,13 @@ export class Challenge extends PureComponent { } render() { - const { content, challenge, mode, showPreview } = this.props; + const { content, mode, showPreview } = this.props; return (
- + state.app.windowHeight, state => state.app.navHeight, - (windowHeight, navHeight) => ({ height: windowHeight - navHeight - 20 }) + state => state.challengesApp.tests, + state => state.challengesApp.refresh, + ( + { challenge: { title, description } = {} }, + windowHeight, + navHeight, + tests, + refresh + ) => ({ + title, + description, + height: windowHeight - navHeight - 20, + tests, + refresh + }) ); export class SidePanel extends PureComponent { @@ -24,14 +40,13 @@ export class SidePanel extends PureComponent { static propTypes = { description: PropTypes.arrayOf(PropTypes.string), - height: PropTypes.number + height: PropTypes.number, + tests: PropTypes.arrayOf(PropTypes.object), + title: PropTypes.string, + refresh: PropTypes.bool }; - static defaultProps = { - description: [ 'Happy Coding!' ] - }; - - renderDescription(description, descriptionRegex) { + renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) { return description.map((line, index) => { if (descriptionRegex.test(line)) { return ( @@ -50,7 +65,7 @@ export class SidePanel extends PureComponent { } render() { - const { title, description, height } = this.props; + const { title, description, height, tests = [], refresh } = this.props; const style = { overflowX: 'hidden', overflowY: 'auto' @@ -64,7 +79,7 @@ export class SidePanel extends PureComponent { style={ style }>

- { title } + { title || 'Happy Coding!' }


@@ -78,7 +93,9 @@ export class SidePanel extends PureComponent {
- +
); } diff --git a/common/app/routes/challenges/components/Test-Suite.jsx b/common/app/routes/challenges/components/Test-Suite.jsx index 17e69353f0..9599e3352a 100644 --- a/common/app/routes/challenges/components/Test-Suite.jsx +++ b/common/app/routes/challenges/components/Test-Suite.jsx @@ -1,42 +1,23 @@ import React, { PropTypes } from 'react'; +import classnames from 'classnames'; import PureComponent from 'react-pure-render/component'; - import { Col, Row } from 'react-bootstrap'; -/* eslint-disable max-len, quotes */ -const tests = [{ - err: null, - text: "assert((function(z){if(z.hasOwnProperty(\"name\") && z.name !== undefined && typeof z.name === \"string\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property name and it should be a string.');" - }, { - err: "message", - text: "assert((function(z){if(z.hasOwnProperty(\"legs\") && z.legs !== undefined && typeof z.legs === \"number\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property legs and it should be a number.');" - }, { - err: "message", - text: "assert((function(z){if(z.hasOwnProperty(\"tails\") && z.tails !== undefined && typeof z.tails === \"number\"){return true;}else{return false;}})(myDog), 'message: myDog should contain the property tails and it should be a number.');" - }, { - err: "message", - text: "assert((function(z){if(z.hasOwnProperty(\"friends\") && z.friends !== undefined && Array.isArray(z.friends)){return true;}else{return false;}})(myDog), 'message: myDog should contain the property friends and it should be an array.');" - }, { - err: "message", - text: "assert((function(z){return Object.keys(z).length === 4;})(myDog), 'message: myDog should only contain all the given properties.');" -}]; -/* eslint-enable max-len, quotes */ - export default class extends PureComponent { static displayName = 'TestSuite'; static proptTypes = { - tests: PropTypes.arrayOf(PropTypes.object) + tests: PropTypes.arrayOf(PropTypes.object), + refresh: PropTypes.bool }; - static defaultProps = { - tests: tests - }; - - renderTests(tests = []) { + renderTests(tests = [], refresh = false) { return tests.map(({ err, text = '' }, index)=> { - var iconClass = err ? - 'ion-close-circled big-error-icon' : - 'ion-checkmark-circled big-success-icon'; + const iconClass = classnames({ + 'big-icon': true, + 'ion-close-circled error-icon': !refresh && !err, + 'ion-checkmark-circled success-icon': !refresh && err, + 'ion-refresh refresh-icon': refresh + }); return ( ); @@ -56,12 +35,12 @@ export default class extends PureComponent { } render() { - const { tests } = this.props; + const { tests, refresh } = this.props; return (
- { this.renderTests(tests) } + { this.renderTests(tests, refresh) }
); } diff --git a/common/app/routes/challenges/redux/reducer.js b/common/app/routes/challenges/redux/reducer.js index e58113dc1c..cd73838df1 100644 --- a/common/app/routes/challenges/redux/reducer.js +++ b/common/app/routes/challenges/redux/reducer.js @@ -3,7 +3,7 @@ import { createPoly } from '../../../../utils/polyvinyl'; import types from './types'; import { BONFIRE, HTML, JS } from '../../../utils/challengeTypes'; -import { buildSeed, getPath } from '../utils'; +import { buildSeed, createTests, getPath } from '../utils'; const initialState = { challenge: '', @@ -22,8 +22,10 @@ const mainReducer = handleActions( }), [types.updateCurrentChallenge]: (state, { payload: challenge }) => ({ ...state, + refresh: true, challenge: challenge.dashedName, - path: getPath(challenge) + path: getPath(challenge), + tests: createTests(challenge) }), // map diff --git a/common/app/routes/challenges/utils.js b/common/app/routes/challenges/utils.js index 9959f48455..bde8590743 100644 --- a/common/app/routes/challenges/utils.js +++ b/common/app/routes/challenges/utils.js @@ -1,5 +1,5 @@ import { compose } from 'redux'; -import { HTML, JS } from '../../utils/challengeTypes'; +import { BONFIRE, HTML, JS } from '../../utils/challengeTypes'; export function encodeScriptTags(value) { return value @@ -41,10 +41,17 @@ export function buildSeed({ challengeSeed = [] } = {}) { const pathsMap = { [HTML]: 'main.html', - [JS]: 'main.js' + [JS]: 'main.js', + [BONFIRE]: 'main.js' }; export function getPath({ challengeType }) { return pathsMap[challengeType] || 'main'; } +export function createTests({ tests = [] }) { + return tests + .map(test => ({ + text: test.split('message: ').pop().replace(/\'\);/g, '') + })); +}