feat(client): show and execute backend project tests (#35673)

This commit is contained in:
Valeriy 2019-06-11 18:46:36 +03:00 committed by mrugesh
parent 1abe4971f4
commit 839e81426e
6 changed files with 44 additions and 83 deletions

View File

@ -12,6 +12,7 @@ import {
consoleOutputSelector, consoleOutputSelector,
initTests, initTests,
updateChallengeMeta, updateChallengeMeta,
updateProjectFormValues,
backendNS backendNS
} from '../redux'; } from '../redux';
import { createGuideUrl } from '../utils'; import { createGuideUrl } from '../utils';
@ -24,6 +25,7 @@ import Output from '../components/Output';
import CompletionModal from '../components/CompletionModal'; import CompletionModal from '../components/CompletionModal';
import HelpModal from '../components/HelpModal'; import HelpModal from '../components/HelpModal';
import ProjectToolPanel from '../project/Tool-Panel'; import ProjectToolPanel from '../project/Tool-Panel';
import ProjectForm from '../project/ProjectForm';
import { import {
createFormValidator, createFormValidator,
isValidURL, isValidURL,
@ -32,6 +34,8 @@ import {
} from '../../../components/formHelpers'; } from '../../../components/formHelpers';
import Spacer from '../../../components/helpers/Spacer'; import Spacer from '../../../components/helpers/Spacer';
import { backend } from '../../../../utils/challengeTypes';
import '../components/test-frame.css'; import '../components/test-frame.css';
// provided by redux form // provided by redux form
@ -52,6 +56,7 @@ const propTypes = {
tests: PropTypes.array, tests: PropTypes.array,
title: PropTypes.string, title: PropTypes.string,
updateChallengeMeta: PropTypes.func.isRequired, updateChallengeMeta: PropTypes.func.isRequired,
updateProjectFormValues: PropTypes.func.isRequired,
...reduxFormPropTypes ...reduxFormPropTypes
}; };
@ -74,7 +79,8 @@ const mapDispatchToActions = {
challengeMounted, challengeMounted,
executeChallenge, executeChallenge,
initTests, initTests,
updateChallengeMeta updateChallengeMeta,
updateProjectFormValues
}; };
const formFields = ['solution']; const formFields = ['solution'];
@ -150,6 +156,7 @@ export class BackEnd extends Component {
data: { data: {
challengeNode: { challengeNode: {
fields: { blockName, slug }, fields: { blockName, slug },
challengeType,
title, title,
description, description,
instructions instructions
@ -158,7 +165,8 @@ export class BackEnd extends Component {
output, output,
tests, tests,
submitting, submitting,
executeChallenge executeChallenge,
updateProjectFormValues
} = this.props; } = this.props;
// TODO: Should be tied to user.isSignedIn // TODO: Should be tied to user.isSignedIn
@ -177,13 +185,21 @@ export class BackEnd extends Component {
description={description} description={description}
instructions={instructions} instructions={instructions}
/> />
<Form {challengeType === backend ? (
buttonText={`${buttonCopy} (Ctrl + Enter)`} <Form
formFields={formFields} buttonText={`${buttonCopy}`}
id={backendNS} formFields={formFields}
options={options} id={backendNS}
submit={executeChallenge} options={options}
/> submit={executeChallenge}
/>
) : (
<ProjectForm
isFrontEnd={false}
onSubmit={executeChallenge}
updateProjectForm={updateProjectFormValues}
/>
)}
<ProjectToolPanel guideUrl={createGuideUrl(slug)} /> <ProjectToolPanel guideUrl={createGuideUrl(slug)} />
<br /> <br />
<Output <Output

View File

@ -1,32 +1,18 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { Form } from '../../../components/formHelpers';
Form
// isValidURL,
// makeRequired,
// createFormValidator
} from '../../../components/formHelpers';
const propTypes = { const propTypes = {
isFrontEnd: PropTypes.bool, isFrontEnd: PropTypes.bool,
isSubmitting: PropTypes.bool, isSubmitting: PropTypes.bool,
openModal: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
updateProjectForm: PropTypes.func.isRequired updateProjectForm: PropTypes.func.isRequired
}; };
const frontEndFields = ['solution']; const frontEndFields = ['solution'];
const backEndFields = ['solution', 'githubLink']; const backEndFields = ['solution', 'githubLink'];
// const fieldValidators = {
// solution: makeRequired(isValidURL)
// };
// const backEndFieldValidators = {
// ...fieldValidators,
// githubLink: makeRequired(isValidURL)
// };
const options = { const options = {
types: { types: {
solution: 'url', solution: 'url',
@ -38,64 +24,20 @@ const options = {
export class ProjectForm extends Component { export class ProjectForm extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
keysDown: {
Control: false,
Enter: false
}
};
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
} }
componentDidMount() { componentDidMount() {
this.props.updateProjectForm({}); this.props.updateProjectForm({});
window.addEventListener('keydown', this.handleKeyDown);
window.addEventListener('keyup', this.handleKeyUp);
} }
componentDidUpdate() { componentDidUpdate() {
this.props.updateProjectForm({}); this.props.updateProjectForm({});
} }
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener('keydown', this.handleKeyDown); this.props.updateProjectForm({});
window.removeEventListener('keyup', this.handleKeyUp);
}
handleKeyDown(e) {
if (e.key === 'Control') {
this.setState(state => ({
...state,
keysDown: { ...state.keysDown, Control: true }
}));
}
if (e.key === 'Enter') {
this.setState(state => ({
...state,
keysDown: { ...state.keysDown, Enter: true }
}));
}
}
handleKeyUp(e) {
if (e.key === 'Control') {
this.setState(state => ({
...state,
keysDown: { ...state.keysDown, Control: false }
}));
}
if (e.key === 'Enter') {
this.setState(state => ({
...state,
keysDown: { ...state.keysDown, Enter: false }
}));
}
} }
handleSubmit(values) { handleSubmit(values) {
const { this.props.updateProjectForm(values);
keysDown: { Control, Enter } this.props.onSubmit();
} = this.state;
if ((Control && Enter) || !Enter) {
this.props.openModal('completion');
this.props.updateProjectForm(values);
}
} }
render() { render() {
const { isSubmitting, isFrontEnd } = this.props; const { isSubmitting, isFrontEnd } = this.props;
@ -104,14 +46,11 @@ export class ProjectForm extends Component {
: "I've completed this challenge"; : "I've completed this challenge";
return ( return (
<Form <Form
buttonText={`${buttonCopy} (Ctrl + Enter)`} buttonText={`${buttonCopy}`}
formFields={isFrontEnd ? frontEndFields : backEndFields} formFields={isFrontEnd ? frontEndFields : backEndFields}
id={isFrontEnd ? 'front-end-form' : 'back-end-form'} id={isFrontEnd ? 'front-end-form' : 'back-end-form'}
options={options} options={options}
submit={this.handleSubmit} submit={this.handleSubmit}
// validate={createFormValidator(
// isFrontEnd ? fieldValidators : backEndFieldValidators
// )}
/> />
); );
} }

View File

@ -117,7 +117,7 @@ export class Project extends Component {
/> />
<ProjectForm <ProjectForm
isFrontEnd={isFrontEnd} isFrontEnd={isFrontEnd}
openModal={openCompletionModal} onSubmit={openCompletionModal}
updateProjectForm={updateProjectFormValues} updateProjectForm={updateProjectFormValues}
/> />
<ToolPanel guideUrl={createGuideUrl(slug)} /> <ToolPanel guideUrl={createGuideUrl(slug)} />

View File

@ -192,10 +192,15 @@ export const challengeDataSelector = state => {
...challengeData, ...challengeData,
url url
}; };
} else if ( } else if (challengeType === challengeTypes.backEndProject) {
challengeType === challengeTypes.frontEndProject || const values = projectFormValuesSelector(state);
challengeType === challengeTypes.backendEndProject const { solution: url } = values;
) { challengeData = {
...challengeData,
...values,
url
};
} else if (challengeType === challengeTypes.frontEndProject) {
challengeData = { challengeData = {
...challengeData, ...challengeData,
...projectFormValuesSelector(state) ...projectFormValuesSelector(state)

View File

@ -61,7 +61,8 @@ const buildFunctions = {
[challengeTypes.bonfire]: buildJSChallenge, [challengeTypes.bonfire]: buildJSChallenge,
[challengeTypes.html]: buildDOMChallenge, [challengeTypes.html]: buildDOMChallenge,
[challengeTypes.modern]: buildDOMChallenge, [challengeTypes.modern]: buildDOMChallenge,
[challengeTypes.backend]: buildBackendChallenge [challengeTypes.backend]: buildBackendChallenge,
[challengeTypes.backEndProject]: buildBackendChallenge
}; };
export async function buildChallenge(challengeData) { export async function buildChallenge(challengeData) {

View File

@ -40,7 +40,7 @@ exports.viewTypes = {
[js]: 'classic', [js]: 'classic',
[bonfire]: 'classic', [bonfire]: 'classic',
[frontEndProject]: 'project', [frontEndProject]: 'project',
[backEndProject]: 'project', [backEndProject]: 'backend',
[modern]: 'modern', [modern]: 'modern',
[step]: 'step', [step]: 'step',
[quiz]: 'quiz', [quiz]: 'quiz',