Add dynamic tests

This commit is contained in:
Berkeley Martinez
2016-05-13 20:04:56 -07:00
parent c0f4fecb6f
commit acac4b3286
6 changed files with 73 additions and 64 deletions

View File

@ -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) {

View File

@ -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 (
<div>
<Col
lg={ 3 }
md={ showPreview ? 3 : 4 }>
<SidePanel { ...challenge }/>
<SidePanel />
</Col>
<Col
lg={ showPreview ? 6 : 9 }

View File

@ -8,11 +8,27 @@ import { Col, Row } from 'react-bootstrap';
import TestSuite from './Test-Suite.jsx';
import Output from './Output.jsx';
import ToolPanel from './Tool-Panel.jsx';
import { challengeSelector } from '../redux/selectors';
const mapStateToProps = createSelector(
challengeSelector,
state => 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 }>
<div>
<h4 className='text-center challenge-instructions-title'>
{ title }
{ title || 'Happy Coding!' }
</h4>
<hr />
<Row>
@ -78,7 +93,9 @@ export class SidePanel extends PureComponent {
<ToolPanel />
<Output />
<br />
<TestSuite />
<TestSuite
refresh={ refresh }
tests={ tests } />
</div>
);
}

View File

@ -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: <code>myDog</code> should contain the property <code>name</code> and it should be a <code>string</code>.');"
}, {
err: "message",
text: "assert((function(z){if(z.hasOwnProperty(\"legs\") && z.legs !== undefined && typeof z.legs === \"number\"){return true;}else{return false;}})(myDog), 'message: <code>myDog</code> should contain the property <code>legs</code> and it should be a <code>number</code>.');"
}, {
err: "message",
text: "assert((function(z){if(z.hasOwnProperty(\"tails\") && z.tails !== undefined && typeof z.tails === \"number\"){return true;}else{return false;}})(myDog), 'message: <code>myDog</code> should contain the property <code>tails</code> and it should be a <code>number</code>.');"
}, {
err: "message",
text: "assert((function(z){if(z.hasOwnProperty(\"friends\") && z.friends !== undefined && Array.isArray(z.friends)){return true;}else{return false;}})(myDog), 'message: <code>myDog</code> should contain the property <code>friends</code> and it should be an <code>array</code>.');"
}, {
err: "message",
text: "assert((function(z){return Object.keys(z).length === 4;})(myDog), 'message: <code>myDog</code> 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 (
<Row key={ text.slice(-6) + index }>
<Col
@ -46,9 +27,7 @@ export default class extends PureComponent {
</Col>
<Col
className='test-output'
dangerouslySetInnerHTML={{
__html: text.split('message: ').pop().replace(/\'\);/g, '')
}}
dangerouslySetInnerHTML={{ __html: text }}
xs={ 10 } />
</Row>
);
@ -56,12 +35,12 @@ export default class extends PureComponent {
}
render() {
const { tests } = this.props;
const { tests, refresh } = this.props;
return (
<div
className='challenge-test-suite'
style={{ marginTop: '10px' }}>
{ this.renderTests(tests) }
{ this.renderTests(tests, refresh) }
</div>
);
}

View File

@ -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

View File

@ -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, '')
}));
}