* Feat: Initial backend view * Feat: Refactor frame runner * Feat: backend challenge submit runs tests * Feat: Backend challenge request * Feat: Whitelist hyperdev in csp * Fix: Use app tests instead of challenge tests * Feat: Allow hyperdev subdomains * Fix(csp): allow hypderdev.space subdomains * feat(challenge): submit backend * feat: Add timeout to test runner (5 sec) * chore(seed): Add more to test backend * fix(csp): s/hyperdev/gomix/g * fix(app): fix code mirror skeleton filepath * fix(app): remove Gitter saga import * fix(app): codemirrorskeleton does not need it's own folder fix(app): cmk needs to work with Null types * fix: No longer restart the browser when challenges change * fix(app): Update jquery for challenges * fix(seed): Remove to promise jquery call * fix(lint): Undo merge error undefined is no allowed * fix(app): linting errors due to bad merge * fix(seed): Remove old seed file
174 lines
3.8 KiB
JavaScript
174 lines
3.8 KiB
JavaScript
import React, { PropTypes, PureComponent } from 'react';
|
|
import { createSelector } from 'reselect';
|
|
import { reduxForm } from 'redux-form';
|
|
import {
|
|
Button,
|
|
Col,
|
|
Row
|
|
} from 'react-bootstrap';
|
|
|
|
import SolutionInput from '../Solution-Input.jsx';
|
|
import TestSuite from '../Test-Suite.jsx';
|
|
import Output from '../Output.jsx';
|
|
import { submitChallenge, executeChallenge } from '../../redux/actions.js';
|
|
import { challengeSelector } from '../../redux/selectors.js';
|
|
import { descriptionRegex } from '../../utils.js';
|
|
import {
|
|
isValidURL,
|
|
makeRequired,
|
|
createFormValidator
|
|
} from '../../../../utils/form.js';
|
|
|
|
const propTypes = {
|
|
id: PropTypes.string,
|
|
title: PropTypes.string,
|
|
description: PropTypes.arrayOf(PropTypes.string),
|
|
tests: PropTypes.array,
|
|
output: PropTypes.string,
|
|
executeChallenge: PropTypes.func.isRequired,
|
|
submitChallenge: PropTypes.func.isRequired,
|
|
// provided by redux form
|
|
submitting: PropTypes.bool,
|
|
fields: PropTypes.object,
|
|
resetForm: PropTypes.func.isRequired,
|
|
handleSubmit: PropTypes.func.isRequired
|
|
};
|
|
|
|
const fields = [ 'solution' ];
|
|
|
|
const fieldValidators = {
|
|
solution: makeRequired(isValidURL)
|
|
};
|
|
|
|
const mapStateToProps = createSelector(
|
|
challengeSelector,
|
|
state => state.challengesApp.output,
|
|
state => state.challengesApp.tests,
|
|
(
|
|
{
|
|
challenge: {
|
|
id,
|
|
title,
|
|
description
|
|
} = {}
|
|
},
|
|
output,
|
|
tests
|
|
) => ({
|
|
id,
|
|
title,
|
|
tests,
|
|
description,
|
|
output
|
|
})
|
|
);
|
|
|
|
const mapDispatchToActions = {
|
|
executeChallenge,
|
|
submitChallenge
|
|
};
|
|
|
|
export class BackEnd extends PureComponent {
|
|
|
|
renderDescription(description) {
|
|
if (!Array.isArray(description)) {
|
|
return null;
|
|
}
|
|
return description.map((line, index) => {
|
|
if (descriptionRegex.test(line)) {
|
|
return (
|
|
<div
|
|
dangerouslySetInnerHTML={{ __html: line }}
|
|
key={ line.slice(-6) + index }
|
|
/>
|
|
);
|
|
}
|
|
return (
|
|
<p
|
|
className='wrappable'
|
|
dangerouslySetInnerHTML= {{ __html: line }}
|
|
key={ line.slice(-6) + index }
|
|
/>
|
|
);
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
title,
|
|
description,
|
|
tests,
|
|
output,
|
|
// provided by redux-form
|
|
fields: { solution },
|
|
submitting,
|
|
handleSubmit,
|
|
executeChallenge
|
|
} = this.props;
|
|
|
|
const buttonCopy = submitting ?
|
|
'Submit and go to my next challenge' :
|
|
"I've completed this challenge";
|
|
return (
|
|
<div>
|
|
<Col
|
|
md={ 6 }
|
|
mdOffset={ 3 }
|
|
xs={ 12 }
|
|
>
|
|
<Row>
|
|
<h3>{ title }</h3>
|
|
{ this.renderDescription(description) }
|
|
</Row>
|
|
<Row>
|
|
<form
|
|
name='BackEndChallenge'
|
|
onSubmit={ handleSubmit(executeChallenge) }
|
|
>
|
|
<SolutionInput
|
|
placeholder='https://your-app.com'
|
|
solution={ solution }
|
|
/>
|
|
<Button
|
|
block={ true }
|
|
bsStyle='primary'
|
|
className='btn-big'
|
|
onClick={ submitting ? null : null }
|
|
type={ submitting ? null : 'submit' }
|
|
>
|
|
{ buttonCopy } (ctrl + enter)
|
|
</Button>
|
|
</form>
|
|
</Row>
|
|
<Row>
|
|
<Output
|
|
defaultOutput={
|
|
`/**
|
|
* Test output will go here
|
|
*/`
|
|
}
|
|
output={ output }
|
|
/>
|
|
</Row>
|
|
<Row>
|
|
<TestSuite tests={ tests } />
|
|
</Row>
|
|
</Col>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
BackEnd.displayName = 'BackEnd';
|
|
BackEnd.propTypes = propTypes;
|
|
|
|
export default reduxForm(
|
|
{
|
|
form: 'BackEndChallenge',
|
|
fields,
|
|
validate: createFormValidator(fieldValidators)
|
|
},
|
|
mapStateToProps,
|
|
mapDispatchToActions
|
|
)(BackEnd);
|