From f4067df471e597e1194fec2c63abd70c8baf90ac Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Mon, 11 Dec 2017 20:01:30 +0100 Subject: [PATCH] Rewriting Expanding fields to be completely compatible with redux-form. Fixing multiple bugs in simple edit form related to expanding fields and to recent change in test identification (using id instead of name). --- .../EditExerciseSimpleConfigForm.js | 103 +------- .../EditExerciseSimpleConfigTest.js | 114 ++++++-- .../EditSimpleLimitsForm.js | 2 +- .../forms/EditTestsForm/EditTests.css | 2 +- .../forms/EditTestsForm/EditTestsForm.js | 4 +- .../forms/EditTestsForm/EditTestsTest.js | 32 ++- .../forms/Fields/ExpandingInputFilesField.js | 249 +++++++----------- .../forms/Fields/ExpandingTextField.js | 157 ++++------- src/components/forms/Fields/SelectField.js | 7 +- src/components/forms/Fields/TextField.js | 7 +- src/components/forms/Fields/commonStyles.less | 4 + src/helpers/exerciseSimpleForm.js | 87 +++--- src/locales/cs.json | 43 +-- src/locales/en.json | 19 +- 14 files changed, 357 insertions(+), 473 deletions(-) diff --git a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js index 45757969b..20a701843 100644 --- a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js +++ b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js @@ -5,15 +5,16 @@ import { reduxForm, getFormValues } from 'redux-form'; import { connect } from 'react-redux'; import { FormattedMessage } from 'react-intl'; import { Alert } from 'react-bootstrap'; -import FormBox from '../../widgets/FormBox'; -import EditExerciseSimpleConfigTest from './EditExerciseSimpleConfigTest'; +import FormBox from '../../widgets/FormBox'; +import Button from '../../widgets/FlatButton'; +import { RefreshIcon } from '../../icons'; import SubmitButton from '../SubmitButton'; import ResourceRenderer from '../../helpers/ResourceRenderer'; -import { createGetSupplementaryFiles } from '../../../redux/selectors/supplementaryFiles'; -import Button from '../../widgets/FlatButton'; -import { RefreshIcon } from '../../icons'; +import EditExerciseSimpleConfigTest from './EditExerciseSimpleConfigTest'; +import { createGetSupplementaryFiles } from '../../../redux/selectors/supplementaryFiles'; +import { encodeTestId } from '../../../redux/modules/simpleLimits'; const EditExerciseSimpleConfigForm = ({ reset, @@ -104,14 +105,16 @@ const EditExerciseSimpleConfigForm = ({ {(...files) =>
- {exerciseTests.map((test, i) => + {exerciseTests.map((test, idx) => undefined} /> )}
} @@ -134,83 +137,6 @@ EditExerciseSimpleConfigForm.propTypes = { exerciseTests: PropTypes.array }; -const validate = ({ config }) => { - const errors = {}; - - const configErrors = {}; - for (let i = 0; i < config.length; ++i) { - const test = config[i]; - const testErrors = {}; - - if (!test.expectedOutput || test.expectedOutput === '') { - testErrors['expectedOutput'] = ( - - ); - } - - if (test.useOutFile && (!test.outputFile || test.outputFile === '')) { - testErrors['outputFile'] = ( - - ); - } - - if (!test.judgeBinary || test.judgeBinary === '') { - testErrors['judgeBinary'] = ( - - ); - } - - if ( - test.useCustomJudge && - (!test.customJudgeBinary || test.customJudgeBinary === '') - ) { - testErrors['customJudgeBinary'] = ( - - ); - } - - const inputErrorMessage = ( - - ); - const inFilesArr = - test.inputFiles && Array.isArray(test.inputFiles) ? test.inputFiles : []; - for (const inputFilePair of inFilesArr) { - if ( - (!inputFilePair.first || inputFilePair.first === '') && - inputFilePair.second !== '' - ) { - testErrors['inputFiles'] = inputErrorMessage; - } - if ( - (!inputFilePair.second || inputFilePair.second === '') && - inputFilePair.first !== '' - ) { - testErrors['inputFiles'] = inputErrorMessage; - } - } - - configErrors[i] = testErrors; - } - errors['config'] = configErrors; - - return errors; -}; - export default connect((state, { exercise }) => { const getSupplementaryFilesForExercise = createGetSupplementaryFiles( exercise.supplementaryFilesIds @@ -223,13 +149,12 @@ export default connect((state, { exercise }) => { reduxForm({ form: 'editExerciseSimpleConfig', enableReinitialize: true, - keepDirtyOnReinitialize: true, + keepDirtyOnReinitialize: false, immutableProps: [ 'formValues', 'supplementaryFiles', 'exerciseTests', 'handleSubmit' - ], - validate + ] })(EditExerciseSimpleConfigForm) ); diff --git a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigTest.js b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigTest.js index 67d7f3748..7fb896ff5 100644 --- a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigTest.js +++ b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigTest.js @@ -1,9 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Field } from 'redux-form'; +import { Field, FieldArray } from 'redux-form'; import { Row, Col } from 'react-bootstrap'; import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; +import Icon from 'react-fontawesome'; +import Button from '../../widgets/FlatButton'; import { SelectField, TextField, @@ -11,6 +13,7 @@ import { ExpandingInputFilesField, CheckboxField } from '../Fields'; +import Confirm from '../../forms/Confirm'; import './EditExerciseSimpleConfigForm.css'; @@ -53,23 +56,47 @@ const messages = defineMessages({ } }); +const validateExpectedOutput = value => + !value || value.trim() === '' + ? + : undefined; + +const validateOutputFile = value => + !value || value.trim() === '' + ? + : undefined; + +const validateCustomJudge = value => + !value || value.trim() === '' + ? + : undefined; + const EditExerciseSimpleConfigTest = ({ supplementaryFiles, formValues, testName, test, - i, + testKey, + testIndex, + smartFill, intl }) => { - const supplementaryFilesOptions = [{ key: '', name: '...' }].concat( - supplementaryFiles - .sort((a, b) => a.name.localeCompare(b.name, intl.locale)) - .filter((item, pos, arr) => arr.indexOf(item) === pos) - .map(data => ({ - key: data.name, - name: data.name - })) - ); + const supplementaryFilesOptions = supplementaryFiles + .sort((a, b) => a.name.localeCompare(b.name, intl.locale)) + .filter((item, pos, arr) => arr.indexOf(item) === pos) + .map(data => ({ + key: data.name, + name: data.name + })); return (
@@ -87,7 +114,7 @@ const EditExerciseSimpleConfigTest = ({ defaultMessage="Input" /> - } /> @@ -108,6 +135,7 @@ const EditExerciseSimpleConfigTest = ({ name={`${test}.inputStdin`} component={SelectField} options={supplementaryFilesOptions} + addEmptyOption={true} label={ - {formValues && formValues.config && - formValues.config[i] && - (formValues.config[i].useOutFile === true || - formValues.config[i].useOutFile === 'true') && + formValues.config[testKey] && + (formValues.config[testKey].useOutFile === true || + formValues.config[testKey].useOutFile === 'true') && {formValues && formValues.config && - formValues.config[i] && - (formValues.config[i].useCustomJudge === true || - formValues.config[i].useCustomJudge === 'true') + formValues.config[testKey] && + (formValues.config[testKey].useCustomJudge === true || + formValues.config[testKey].useCustomJudge === 'true') ? } /> @@ -256,19 +289,19 @@ const EditExerciseSimpleConfigTest = ({ ]} label={ } />} {formValues && formValues.config && - formValues.config[i] && - (formValues.config[i].useCustomJudge === true || - formValues.config[i].useCustomJudge === 'true') && - } />} + {testIndex === 0 && +
+ + } + > + + +
}
@@ -285,10 +339,12 @@ const EditExerciseSimpleConfigTest = ({ EditExerciseSimpleConfigTest.propTypes = { testName: PropTypes.string.isRequired, test: PropTypes.string.isRequired, - i: PropTypes.number.isRequired, + testKey: PropTypes.string.isRequired, + testIndex: PropTypes.number.isRequired, supplementaryFiles: PropTypes.array.isRequired, exerciseTests: PropTypes.array, formValues: PropTypes.object, + smartFill: PropTypes.func.isRequired, intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; diff --git a/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js b/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js index c72d2512e..c560f8fe9 100644 --- a/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js +++ b/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js @@ -235,7 +235,7 @@ const validate = ({ limits }) => { export default reduxForm({ form: 'editSimpleLimits', enableReinitialize: true, - keepDirtyOnReinitialize: true, + keepDirtyOnReinitialize: false, immutableProps: [ 'environments', 'tests', diff --git a/src/components/forms/EditTestsForm/EditTests.css b/src/components/forms/EditTestsForm/EditTests.css index 2fa581e99..3a64352fa 100644 --- a/src/components/forms/EditTestsForm/EditTests.css +++ b/src/components/forms/EditTestsForm/EditTests.css @@ -1,4 +1,4 @@ .testRow { margin-bottom: 0; - margin-top: -20px; + margin-top: 0; } diff --git a/src/components/forms/EditTestsForm/EditTestsForm.js b/src/components/forms/EditTestsForm/EditTestsForm.js index c44815f15..5f2135cc7 100644 --- a/src/components/forms/EditTestsForm/EditTestsForm.js +++ b/src/components/forms/EditTestsForm/EditTestsForm.js @@ -78,7 +78,7 @@ class EditTestsForm extends Component { hasSucceeded={hasSucceeded} dirty={dirty} hasFailed={hasFailed} - handleSubmit={data => handleSubmit(data).then(reset)} + handleSubmit={handleSubmit} messages={{ submit: ( { reduxForm({ form: 'editTests', enableReinitialize: true, - keepDirtyOnReinitialize: true, + keepDirtyOnReinitialize: false, validate })(EditTestsForm) ); diff --git a/src/components/forms/EditTestsForm/EditTestsTest.js b/src/components/forms/EditTestsForm/EditTestsTest.js index 2ec97d62f..cb7c048ec 100644 --- a/src/components/forms/EditTestsForm/EditTestsTest.js +++ b/src/components/forms/EditTestsForm/EditTestsTest.js @@ -53,20 +53,24 @@ const EditTestsTest = ({ fields, isUniform, testValues }) => { )} -
- -
+ {fields.length < 99 && +
+ +
} ); }; diff --git a/src/components/forms/Fields/ExpandingInputFilesField.js b/src/components/forms/Fields/ExpandingInputFilesField.js index f421629e4..8a3a35851 100644 --- a/src/components/forms/Fields/ExpandingInputFilesField.js +++ b/src/components/forms/Fields/ExpandingInputFilesField.js @@ -1,170 +1,107 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; -import { - Row, - Col, - FormGroup, - FormControl, - HelpBlock, - ControlLabel -} from 'react-bootstrap'; -import classNames from 'classnames'; +import { Field } from 'redux-form'; +import { ControlLabel } from 'react-bootstrap'; +import Icon from 'react-fontawesome'; -import styles from './commonStyles.less'; - -const EMPTY_VALUE = { first: '', second: '' }; - -class ExpandingInputFilesField extends Component { - state = { texts: [] }; - - componentDidMount() { - const { input: { value } } = this.props; - const initialValue = Array.isArray(value) - ? value.concat([EMPTY_VALUE]) - : [EMPTY_VALUE]; - this.setState({ texts: initialValue }); - } +import FlatButton from '../../widgets/FlatButton'; - changeText = (i, text, isFirst, onChange) => { - const { texts } = this.state; - if (isFirst) { - texts[i] = { first: text.trim(), second: texts[i].second }; - } else { - texts[i] = { first: texts[i].first, second: text.trim() }; - } - if (i === texts.length - 1) { - texts.push(EMPTY_VALUE); - } - this.setState({ texts }); +import SelectField from './SelectField'; +import TextField from './TextField'; - const texts2 = texts.slice(0, texts.length - 1); - onChange(texts2); - }; - - removeIfEmpty = (i, onChange) => { - const { texts } = this.state; - if ( - i !== texts.length - 1 && - texts[i].first === '' && - texts[i].second === '' - ) { - texts.splice(i, 1); - this.setState({ texts }); - - const texts2 = texts.slice(0, texts.length - 1); - onChange(texts2); - } - }; +import styles from './commonStyles.less'; - isReference = () => {}; +const EMPTY_VALUE = { file: '', name: '' }; - render() { - const { - leftLabel = '', - rightLabel = '', - input: { onChange, onFocus, onBlur, ...input }, - meta: { active, dirty, error, warning }, - style = {}, - options, - ignoreDirty = false, - ...props - } = this.props; - const { texts } = this.state; +const validate = value => + !value || value.trim() === '' + ? + : undefined; - return ( - - - - - {leftLabel} - -
- {texts.map((text, i) => - - this.changeText(i, e.target.value, true, onChange)} - onFocus={onFocus} - onBlur={e => { - onBlur(e); - this.removeIfEmpty(i, onChange); - }} - value={text.first} - componentClass="select" - bsClass={classNames({ - 'form-control': true, - [styles.dirty]: - i < texts.length - 1 && - dirty && - !ignoreDirty && - !error && - !warning, - [styles.active]: active - })} +const ExpandingInputFilesField = ({ + fields, + meta: { active, dirty, error, warning }, + leftLabel = '', + rightLabel = '', + options, + ...props +}) => +
+ {fields.length > 0 && + + + + + + + + + {fields.map((field, index) => + + - - {rightLabel} - -
- {texts.map((text, i) => - - this.changeText(i, e.target.value, false, onChange)} - onFocus={onFocus} - onBlur={e => { - onBlur(e); - this.removeIfEmpty(i, onChange); - }} - value={text.second} - bsClass={classNames({ - 'form-control': true, - [styles.dirty]: - i < texts.length - 1 && - dirty && - !ignoreDirty && - !error && - !warning, - [styles.active]: active - })} + /> + +
+ + + + )} + +
+ + {leftLabel} + + + + {rightLabel} + + + +
+ - {options.map(({ key, name }, o) => - - )} - - )} - - -
+ - )} - - - - {error && - - {' '}{error}{' '} - } - {!error && - warning && - - {' '}{warning}{' '} - } - - ); - } -} + + fields.insert(index, EMPTY_VALUE)}> + + + + fields.remove(index)}> + + +
} +
+ {fields.length === 0 && + + + } + fields.push(EMPTY_VALUE)}> + + +
+
; ExpandingInputFilesField.propTypes = { - input: PropTypes.object, + fields: PropTypes.object.isRequired, meta: PropTypes.shape({ active: PropTypes.bool, dirty: PropTypes.bool, @@ -179,9 +116,7 @@ ExpandingInputFilesField.propTypes = { PropTypes.string, PropTypes.shape({ type: PropTypes.oneOf([FormattedMessage]) }) ]).isRequired, - style: PropTypes.object, - options: PropTypes.array, - ignoreDirty: PropTypes.bool + options: PropTypes.array }; export default ExpandingInputFilesField; diff --git a/src/components/forms/Fields/ExpandingTextField.js b/src/components/forms/Fields/ExpandingTextField.js index 658a862e2..a188ba8df 100644 --- a/src/components/forms/Fields/ExpandingTextField.js +++ b/src/components/forms/Fields/ExpandingTextField.js @@ -1,112 +1,63 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; +import { Field } from 'redux-form'; +import { ControlLabel } from 'react-bootstrap'; +import Icon from 'react-fontawesome'; -import { - FormGroup, - FormControl, - HelpBlock, - ControlLabel -} from 'react-bootstrap'; -import classNames from 'classnames'; +import FlatButton from '../../widgets/FlatButton'; -import styles from './commonStyles.less'; - -class ExpandingTextField extends Component { - state = { texts: [] }; - - componentDidMount() { - const { input: { value } } = this.props; - const initialValue = Array.isArray(value) - ? value.concat(['']) - : value.trim() !== '' ? [value, ''] : ['']; - this.setState({ texts: initialValue }); - } - - changeText = (i, text, onChange) => { - const { texts } = this.state; - texts[i] = text.trim(); - if (i === texts.length - 1) { - texts.push(''); - } - this.setState({ texts }); - - const texts2 = texts.slice(0, texts.length - 1); - onChange(texts2); - }; +import TextField from './TextField'; - removeIfEmpty = (i, onChange) => { - const { texts } = this.state; - if (i !== texts.length - 1 && texts[i] === '') { - texts.splice(i, 1); - this.setState({ texts }); - - const texts2 = texts.slice(0, texts.length - 1); - onChange(texts2); - } - }; - - isReference = () => {}; - - render() { - const { - label = '', - input: { onChange, onFocus, onBlur }, - meta: { active, dirty, error, warning }, - style = {}, - ignoreDirty = false, - ...props - } = this.props; - const { texts } = this.state; +import styles from './commonStyles.less'; - return ( - - {label} -
- {texts.map((text, i) => - this.changeText(i, e.target.value, onChange)} - onFocus={onFocus} - onBlur={e => { - onBlur(e); - this.removeIfEmpty(i, onChange); - }} - value={text} - bsClass={classNames({ - 'form-control': true, - [styles.dirty]: - i < texts.length - 1 && - dirty && - !ignoreDirty && - !error && - !warning, - [styles.active]: active - })} - {...props} - /> - )} -
{' '} - {error && - - {' '}{error}{' '} - } - {!error && - warning && - - {' '}{warning}{' '} - } -
- ); - } -} +const ExpandingTextField = ({ + fields, + meta: { active, dirty, error, warning }, + label, + ...props +}) => +
+ + {label} + + + + {fields.map((field, index) => + + + + + + )} + +
+ + + fields.insert(index, '')}> + + + + fields.remove(index)}> + + +
+
+ {fields.length === 0 && + + + } + fields.push('')}> + + +
+
; ExpandingTextField.propTypes = { - input: PropTypes.object, + fields: PropTypes.object.isRequired, meta: PropTypes.shape({ active: PropTypes.bool, dirty: PropTypes.bool, @@ -116,9 +67,7 @@ ExpandingTextField.propTypes = { label: PropTypes.oneOfType([ PropTypes.string, PropTypes.shape({ type: PropTypes.oneOf([FormattedMessage]) }) - ]).isRequired, - style: PropTypes.object, - ignoreDirty: PropTypes.bool + ]).isRequired }; export default ExpandingTextField; diff --git a/src/components/forms/Fields/SelectField.js b/src/components/forms/Fields/SelectField.js index e199041ad..a2724dd35 100644 --- a/src/components/forms/Fields/SelectField.js +++ b/src/components/forms/Fields/SelectField.js @@ -26,9 +26,10 @@ const SelectField = ({ controlId={input.name} validationState={error ? 'error' : warning ? 'warning' : undefined} > - - {label} - + {label && + + {label} + } - - {label} - + {label && + + {label} + } { const confTests = - tests && config[0] && config[0].tests - ? config[0].tests.sort((a, b) => { - const aName = tests.find(test => test.id === a.name).name; - const bName = tests.find(test => test.id === b.name).name; - return aName.localeCompare(bName, locale); - }) - : []; + tests && config[0] && config[0].tests ? config[0].tests : []; - let res = []; - for (let test of confTests) { - let testObj = { name: test.name }; - const variables = test.pipelines.reduce( - (acc, pipeline) => acc.concat(pipeline.variables), - [] - ); + let res = {}; + for (let test of tests) { + const testConf = confTests.find(t => t.name === test.id); + let testObj = { name: test.id }; + + const variables = + testConf && testConf.pipelines + ? testConf.pipelines.reduce( + (acc, pipeline) => acc.concat(pipeline.variables), + [] + ) + : []; const inputFiles = variables.find( variable => variable.name === 'input-files' @@ -108,10 +107,10 @@ export const getSimpleConfigInitValues = (config, tests, locale) => { if (inputFiles) { testObj.inputFiles = inputFiles.value ? inputFiles.value.map((value, i) => ({ - first: value, - second: + file: value, + name: actualInputs && actualInputs.value && actualInputs.value[i] - ? actualInputs.value[i] + ? actualInputs.value[i].trim() : '' })) : []; @@ -125,17 +124,21 @@ export const getSimpleConfigInitValues = (config, tests, locale) => { } const runArgs = variables.find(variable => variable.name === 'run-args'); - if (runArgs) { - testObj.runArgs = runArgs.value; + testObj.runArgs = []; + if (runArgs && runArgs.value) { + testObj.runArgs = Array.isArray(runArgs.value) + ? runArgs.value + : [runArgs.value]; } const actualOutput = variables.find( variable => variable.name === 'actual-output' ); - if (actualOutput) { + if (actualOutput && actualOutput.value && actualOutput.value.trim()) { testObj.useOutFile = true; - testObj.outputFile = actualOutput.value; + testObj.outputFile = actualOutput.value.trim(); } else { + testObj.useOutFile = false; testObj.outputFile = ''; } @@ -154,9 +157,11 @@ export const getSimpleConfigInitValues = (config, tests, locale) => { ); testObj.useCustomJudge = false; - if (customJudge) { + testObj.customJudgeBinary = ''; + testObj.judgeBinary = ''; + if (customJudge && customJudge.value) { testObj.customJudgeBinary = customJudge.value; - testObj.useCustomJudge = customJudge.value.trim() !== ''; + testObj.useCustomJudge = true; } if (!testObj.useCustomJudge) { testObj.judgeBinary = @@ -168,16 +173,14 @@ export const getSimpleConfigInitValues = (config, tests, locale) => { const judgeArgs = variables.find( variable => variable.name === 'judge-args' ); - if (judgeArgs) { - testObj.judgeArgs = judgeArgs.value; + testObj.judgeArgs = []; + if (judgeArgs && judgeArgs.value) { + testObj.judgeArgs = Array.isArray(judgeArgs.value) + ? judgeArgs.value + : [judgeArgs.value]; } - res.push(testObj); - } - - // fill new tests with default judge - for (let i = confTests.length; i < tests.length; ++i) { - res.push({ judgeBinary: 'recodex-judge-normal' }); + res[encodeTestId(test.id)] = testObj; } return { config: res }; @@ -187,13 +190,13 @@ export const transformAndSendConfigValues = ( formData, pipelines, environments, - sortedTests, + tests, setConfig ) => { let testVars = []; - for (let testIndex = 0; testIndex < sortedTests.length; ++testIndex) { - const test = formData.config[testIndex]; - const testName = sortedTests[testIndex].id; + for (let t of tests) { + const testName = t.id; + const test = formData.config[encodeTestId(testName)]; let variables = []; variables.push({ @@ -229,8 +232,8 @@ export const transformAndSendConfigValues = ( if (test.useOutFile) { variables.push({ name: 'actual-output', - type: 'file[]', - value: test.outputFile + type: 'file', + value: test.useOutFile ? test.outputFile.trim() : '' }); } @@ -239,8 +242,8 @@ export const transformAndSendConfigValues = ( const inFilesArr = test.inputFiles && Array.isArray(test.inputFiles) ? test.inputFiles : []; for (const item of inFilesArr) { - inputFiles.push(item.first); - renamedNames.push(item.second); + inputFiles.push(item.file); + renamedNames.push(item.name.trim()); } variables.push({ name: 'input-files', @@ -267,7 +270,7 @@ export const transformAndSendConfigValues = ( pipeline => pipeline.runtimeEnvironmentIds.indexOf(envId) >= 0 ); - let tests = []; + let testsCfg = []; for (const testVar of testVars) { const compilationPipelineId = envPipelines.filter( pipeline => pipeline.parameters.isCompilationPipeline @@ -279,7 +282,7 @@ export const transformAndSendConfigValues = ( ? pipeline.parameters.producesFiles : pipeline.parameters.producesStdout) )[0].id; - tests.push({ + testsCfg.push({ name: testVar.name, pipelines: [ { @@ -295,7 +298,7 @@ export const transformAndSendConfigValues = ( } envs.push({ name: envId, - tests: tests + tests: testsCfg }); } diff --git a/src/locales/cs.json b/src/locales/cs.json index f2f4536fb..d660d4f58 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -251,6 +251,8 @@ "app.editExerciseConfigForm.failed": "Uložení se nezdařilo. Prosíme opakujte akci později.", "app.editExerciseConfigForm.pipelines": "Pipeliny", "app.editExerciseConfigForm.removeLastTest": "Odstranit poslední test", + "app.editExerciseConfigForm.smartFill": "Smart Fill", + "app.editExerciseConfigForm.smartFill.yesNoQuestion": "Do you really wish to overwrite configuration of all subsequent tests using the first test as a template? Files will be paired to individual test configurations by a heuristics based on matching name substrings.", "app.editExerciseConfigForm.submit": "Změnit konfiguraci", "app.editExerciseConfigForm.submitting": "Ukládání konfigurace ...", "app.editExerciseConfigForm.success": "Konfigurace byla uložena.", @@ -286,26 +288,26 @@ "app.editExerciseSimpleConfigForm.submitting": "Ukládám konfiguraci ...", "app.editExerciseSimpleConfigForm.success": "Konfigurace uložena", "app.editExerciseSimpleConfigForm.validating": "Validuji ...", - "app.editExerciseSimpleConfigForm.validation.customJudge": "Please select the custom judge binary for this test or use one of the standard judges instead.", - "app.editExerciseSimpleConfigForm.validation.expectedOutput": "Please fill the expected output file.", - "app.editExerciseSimpleConfigForm.validation.inputFilesNotPaired": "Input files are not properly paired with their names. Please make sure each file has a name.", - "app.editExerciseSimpleConfigForm.validation.judgeBinary": "Please select the judge type for this test.", - "app.editExerciseSimpleConfigForm.validation.outputFile": "Please fill the name of the output file or use standard input instead.", - "app.editExerciseSimpleConfigTests.customJudgeBinary": "Custom judge binary:", - "app.editExerciseSimpleConfigTests.executionArguments": "Execution arguments:", - "app.editExerciseSimpleConfigTests.executionTitle": "Execution", - "app.editExerciseSimpleConfigTests.expectedOutput": "Expected output:", - "app.editExerciseSimpleConfigTests.inputFilesActual": "Input file:", - "app.editExerciseSimpleConfigTests.inputFilesRename": "Renamed file name:", - "app.editExerciseSimpleConfigTests.inputStdin": "Stdin:", - "app.editExerciseSimpleConfigTests.inputTitle": "Input", - "app.editExerciseSimpleConfigTests.judgeArgs": "Judge arguments:", - "app.editExerciseSimpleConfigTests.judgeBinary": "Judge binary:", - "app.editExerciseSimpleConfigTests.judgeTitle": "Judge", - "app.editExerciseSimpleConfigTests.outputFile": "Output file:", - "app.editExerciseSimpleConfigTests.outputTitle": "Output", - "app.editExerciseSimpleConfigTests.useCustomJudge": "Use custom judge binary", - "app.editExerciseSimpleConfigTests.useOutfile": "Use output file instead of stdout", + "app.editExerciseSimpleConfigForm.validation.customJudge": "Prosím, zvolte spustitelný soubor sudího, nebo si vyberte z předpřipravené množiny vestavěných sudí.", + "app.editExerciseSimpleConfigForm.validation.expectedOutput": "Prosím zvolte soubor s očekávaným výstupem.", + "app.editExerciseSimpleConfigForm.validation.inputFilesNotPaired": "Vstupní soubory nemají správně vyplněné položky přejmenování. Každý soubor musí mít své jméno.", + "app.editExerciseSimpleConfigForm.validation.judgeBinary": "Prosím, vyberte sudího pro tento test.", + "app.editExerciseSimpleConfigForm.validation.outputFile": "Prosím, vyberte jméno výstupního souboru.", + "app.editExerciseSimpleConfigTests.customJudgeBinary": "Spustitelný soubor sudího:", + "app.editExerciseSimpleConfigTests.executionArguments": "Spouštěcí argumenty:", + "app.editExerciseSimpleConfigTests.executionTitle": "Spuštění", + "app.editExerciseSimpleConfigTests.expectedOutput": "Očekávaný výstup:", + "app.editExerciseSimpleConfigTests.inputFilesActual": "Vstupní soubor:", + "app.editExerciseSimpleConfigTests.inputFilesRename": "Přejmenování:", + "app.editExerciseSimpleConfigTests.inputStdin": "Std. vstup:", + "app.editExerciseSimpleConfigTests.inputTitle": "Vstup", + "app.editExerciseSimpleConfigTests.judgeArgs": "Argumenty sudího:", + "app.editExerciseSimpleConfigTests.judgeTitle": "Sudí", + "app.editExerciseSimpleConfigTests.judgeType": "Sudí:", + "app.editExerciseSimpleConfigTests.outputFile": "Výstupní soubor:", + "app.editExerciseSimpleConfigTests.outputTitle": "Výstup", + "app.editExerciseSimpleConfigTests.useCustomJudge": "Použít vlastní soubor sudího", + "app.editExerciseSimpleConfigTests.useOutfile": "Použít výstupní soubor místo std. výstupu", "app.editGroup.cannotDeleteRootGroup": "Toto je primární skupina a jako taková nemůže být smazána.", "app.editGroup.deleteGroup": "Smazat skupinu", "app.editGroup.deleteGroupWarning": "Smazání skupiny odstraní také všechny podskupiny, skupinové úlohy a zadané úlohy včetně studentských řešení.", @@ -544,6 +546,7 @@ "app.exitCodes.mono.201": "No main method", "app.exitCodes.mono.202": "More main methods", "app.exitCodes.unknown": "Unknown", + "app.expandingTextField.noItems": "There are no items yet...", "app.externalRegistrationForm.failed": "Registrace se nezdařila. Prosíme, zkontrolujte vyplněné informace.", "app.externalRegistrationForm.instance": "Instance:", "app.externalRegistrationForm.password": "Heslo:", diff --git a/src/locales/en.json b/src/locales/en.json index 3b0bc6ce5..26606ad21 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -251,6 +251,8 @@ "app.editExerciseConfigForm.failed": "Saving failed. Please try again later.", "app.editExerciseConfigForm.pipelines": "Pipelines", "app.editExerciseConfigForm.removeLastTest": "Remove last test", + "app.editExerciseConfigForm.smartFill": "Smart Fill", + "app.editExerciseConfigForm.smartFill.yesNoQuestion": "Do you really wish to overwrite configuration of all subsequent tests using the first test as a template? Files will be paired to individual test configurations by a heuristics based on matching name substrings.", "app.editExerciseConfigForm.submit": "Change configuration", "app.editExerciseConfigForm.submitting": "Saving configuration ...", "app.editExerciseConfigForm.success": "Configuration was changed.", @@ -286,22 +288,22 @@ "app.editExerciseSimpleConfigForm.submitting": "Saving Configuration ...", "app.editExerciseSimpleConfigForm.success": "Configuration Saved.", "app.editExerciseSimpleConfigForm.validating": "Validating ...", - "app.editExerciseSimpleConfigForm.validation.customJudge": "Please select the custom judge binary for this test or use one of the standard judges instead.", - "app.editExerciseSimpleConfigForm.validation.expectedOutput": "Please fill the expected output file.", - "app.editExerciseSimpleConfigForm.validation.inputFilesNotPaired": "Input files are not properly paired with their names. Please make sure each file has a name.", - "app.editExerciseSimpleConfigForm.validation.judgeBinary": "Please select the judge type for this test.", - "app.editExerciseSimpleConfigForm.validation.outputFile": "Please fill the name of the output file or use standard input instead.", - "app.editExerciseSimpleConfigTests.customJudgeBinary": "Custom judge binary:", + "app.editExerciseSimpleConfigForm.validation.customJudge": "Please, select the custom judge binary for this test or use one of the standard judges instead.", + "app.editExerciseSimpleConfigForm.validation.expectedOutput": "Please, fill in the expected output file.", + "app.editExerciseSimpleConfigForm.validation.inputFilesNotPaired": "Input files are not properly paired with their names. Please, make sure each file has a name.", + "app.editExerciseSimpleConfigForm.validation.judgeBinary": "Please, select the judge type for this test.", + "app.editExerciseSimpleConfigForm.validation.outputFile": "Please, fill in the name of the output file.", + "app.editExerciseSimpleConfigTests.customJudgeBinary": "Custom judge executable:", "app.editExerciseSimpleConfigTests.executionArguments": "Execution arguments:", "app.editExerciseSimpleConfigTests.executionTitle": "Execution", "app.editExerciseSimpleConfigTests.expectedOutput": "Expected output:", "app.editExerciseSimpleConfigTests.inputFilesActual": "Input file:", - "app.editExerciseSimpleConfigTests.inputFilesRename": "Renamed file name:", + "app.editExerciseSimpleConfigTests.inputFilesRename": "Rename as:", "app.editExerciseSimpleConfigTests.inputStdin": "Stdin:", "app.editExerciseSimpleConfigTests.inputTitle": "Input", "app.editExerciseSimpleConfigTests.judgeArgs": "Judge arguments:", - "app.editExerciseSimpleConfigTests.judgeBinary": "Judge binary:", "app.editExerciseSimpleConfigTests.judgeTitle": "Judge", + "app.editExerciseSimpleConfigTests.judgeType": "Judge:", "app.editExerciseSimpleConfigTests.outputFile": "Output file:", "app.editExerciseSimpleConfigTests.outputTitle": "Output", "app.editExerciseSimpleConfigTests.useCustomJudge": "Use custom judge binary", @@ -544,6 +546,7 @@ "app.exitCodes.mono.201": "No main method", "app.exitCodes.mono.202": "More main methods", "app.exitCodes.unknown": "Unknown", + "app.expandingTextField.noItems": "There are no items yet...", "app.externalRegistrationForm.failed": "Registration failed. Please check your information.", "app.externalRegistrationForm.instance": "Instance:", "app.externalRegistrationForm.password": "Password:",