diff --git a/ui/src/features/components/JobForm/constants.js b/ui/src/features/components/JobForm/constants.js
index ca10b0a1c..8b715ba0b 100644
--- a/ui/src/features/components/JobForm/constants.js
+++ b/ui/src/features/components/JobForm/constants.js
@@ -5,6 +5,7 @@ export const testTypes = {
};
export const inputTypes = {
+ LIST: 'LIST',
INPUT_LIST: 'INPUT_LIST',
NUMERIC_INPUT: 'NUMERIC_INPUT',
NUMERIC_WITH_SWITCH: 'NUMERIC_WITH_SWITCH',
@@ -12,5 +13,8 @@ export const inputTypes = {
SWITCHER_TWO_SIDES: 'SWITCHER_TWO_SIDES',
TEXT_FIELD: 'TEXT_FIELD',
RADIO: 'RADIO',
- MULTI_SELECT: 'MULTI_SELECT'
+ MULTI_SELECT: 'MULTI_SELECT',
+ LINK_ACTION: 'LINK_ACTION',
+ DROPDOWN: 'DROPDOWN',
+ SUBMIT_BUTTON: 'SUBMIT_BUTTON'
};
diff --git a/ui/src/features/components/JobForm/index.js b/ui/src/features/components/JobForm/index.js
index fa6c94bed..cfb6f153a 100644
--- a/ui/src/features/components/JobForm/index.js
+++ b/ui/src/features/components/JobForm/index.js
@@ -26,6 +26,10 @@ import { faClock, faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import IconButton from '../../../components/IconButton';
import { createJobRequest, createStateForEditJob } from './utils';
+import Button from '../../../components/Button';
+import SimpleTable from '../SimpleTable';
+import { chaosExperimentsForDropdown } from '../../redux/selectors/chaosExperimentsSelector';
+import Dropdown from '../../../components/Dropdown/Dropdown.export';
const DESCRIPTION = 'Predator executes tests through jobs. Use this form to specify the parameters for the job you want to execute.';
@@ -51,7 +55,6 @@ class Form extends React.Component {
optionToValue: { 'Load test': 'load_test', 'Functional test': 'functional_test' },
valueToOption: { load_test: 'Load test', functional_test: 'Functional test' }
},
-
{
group: 'section_a',
bottom: [
@@ -88,7 +91,6 @@ class Form extends React.Component {
}
],
children: [
-
{
name: 'arrival_rate',
key: 'arrival_rate',
@@ -175,6 +177,7 @@ class Form extends React.Component {
info: 'When the test finishes, a report will be sent to the emails included.',
element: 'Email',
type: inputTypes.INPUT_LIST,
+ values: (state) => state['emails'].map((value) => ({ value, label: value })),
flexBasis: '49%'
},
{
@@ -186,13 +189,110 @@ class Form extends React.Component {
type: inputTypes.MULTI_SELECT,
options: (props) => props.webhooks,
flexBasis: '49%'
-
}
-
]
},
- {
+ ...(props.experiments.length > 0 ? [{
group: 'section_c',
+ children:
+ [
+ {
+ name: 'experiments',
+ key: 'experiments',
+ floatingLabelText: 'Running Experiments',
+ info: 'Chaos experiments running within the test',
+ element: 'RunningExperiments',
+ type: inputTypes.LIST,
+ rows: (state) => state.experiments.map((experiment, index) => {
+ return [
+
+ experiment name:
+ {experiment.experiment_name}
+ start at:
+ {experiment.start_after}
+
+ ]
+ }),
+ flexBasis: '80%'
+ },
+ {
+ name: 'add_experiment_option',
+ key: 'add_experiment_option',
+ floatingLabelText: '+Add Experiment',
+ onClick: () => {
+ this.setState({
+ add_experiment_form_experiment_name: '',
+ add_experiment_form_experiment_id: '',
+ add_experiment_form_start_after: 0,
+ add_experiment_form_hidden: false
+ })
+ },
+ type: inputTypes.LINK_ACTION,
+ justifyContent: 'flex-end'
+ },
+ {
+ name: 'add_experiment_form_experiment_name',
+ key: 'add_experiment_form_experiment_name',
+ floatingLabelText: 'Experiment',
+ list: (props) => props.experiments,
+ placeholder: 'Select experiment to run',
+ element: 'new_experiment',
+ selectedOption: (state) => ({ key: state.add_experiment_form_experiment_id, value: state.add_experiment_form_experiment_name }),
+ type: inputTypes.DROPDOWN,
+ onChange: ({ value, key }) => {
+ this.setState({
+ add_experiment_form_experiment_name: value,
+ add_experiment_form_experiment_id: key
+ })
+ },
+ flexBasis: '49%',
+ hiddenCondition: (state) => state.add_experiment_form_hidden === true
+ },
+ {
+ name: 'add_experiment_form_start_after',
+ key: 'add_experiment_form_start_after',
+ floatingLabelText: 'Start At',
+ info: 'When to start the experiment within the test timeframe (milliseconds)',
+ element: 'StartAt',
+ type: inputTypes.NUMERIC_INPUT,
+ justifyContent: 'flex-end',
+ height: '35px',
+ hiddenCondition: (state) => state.add_experiment_form_hidden === true
+ },
+ {
+ name: 'add_experiment_submit',
+ key: 'add_experiment_submit',
+ floatingLabelText: 'Add',
+ element: 'AddExperiment',
+ type: inputTypes.SUBMIT_BUTTON,
+ inverted: true,
+ justifyContent: 'flex-end',
+ paddingTop: '22px',
+ height: '35px',
+ onClick: () => {
+ const newExperiment = {
+ experiment_id: this.state.add_experiment_form_experiment_id,
+ experiment_name: this.state.add_experiment_form_experiment_name,
+ start_after: this.state.add_experiment_form_start_after
+ }
+ const experiments = [...this.state.experiments, newExperiment]
+ this.state.experiments.push(newExperiment)
+ this.setState((prevState) => {
+ return {
+ ...prevState,
+ experiments: experiments,
+ add_experiment_form_hidden: true
+ }
+ })
+ console.log(this.state.experiments)
+ },
+ disabled: (state) => state.add_experiment_form_experiment_name.trim().length === 0 || state.add_experiment_form_start_after === 0,
+ hiddenCondition: (state) => state.add_experiment_form_hidden === true
+ }
+ ]
+ }] : []),
+ {
+ group: 'section_d',
flexDirection: 'column',
children: [
{
@@ -233,6 +333,7 @@ class Form extends React.Component {
run_immediately: false,
emails: [],
webhooks: [],
+ experiments: [],
helpInfo: undefined,
parallelism: undefined,
max_virtual_users: undefined,
@@ -252,7 +353,11 @@ class Form extends React.Component {
parallelism: true,
max_virtual_users: true
},
- mode: 'Simple'
+ mode: 'Simple',
+ add_experiment_form_hidden: true,
+ add_experiment_form_experiment_id: '',
+ add_experiment_form_experiment_name: '',
+ add_experiment_form_start_after: 0
};
if (this.props.editMode) {
@@ -283,6 +388,7 @@ class Form extends React.Component {
componentDidMount () {
this.props.getWebhooks();
+ this.props.getChaosExperiments();
}
componentDidUpdate (prevProps, prevState, snapshot) {
@@ -424,6 +530,28 @@ class Form extends React.Component {
const { cron_expression } = this.state;
switch (oneItem.type) {
+ case inputTypes.LINK_ACTION:
+ return (
+
+
+ {oneItem.floatingLabelText}
+
+
+ )
+ case inputTypes.SUBMIT_BUTTON:
+ return (
+
+
+
+ )
case inputTypes.SWITCHER:
return (
@@ -440,7 +568,6 @@ class Form extends React.Component {
);
case inputTypes.SWITCHER_TWO_SIDES:
return (
-
{oneItem.leftOption}
{oneItem.rightOption}
-
);
+ case inputTypes.LIST: {
+ return (
+ }>
+
+
+ )
+ }
case inputTypes.INPUT_LIST:
return (
}>
({ value, label: value }))}
+ disabled={oneItem.disabled}
+ values={oneItem.values(this.state)}
onAddItem={(evt) => this.handleInputListAdd(oneItem.name, evt)}
onRemoveItem={evt => this.handleInputListRemove(oneItem.name, evt)}
// validationFunc={validateFunc}
@@ -471,7 +607,18 @@ class Form extends React.Component {
);
-
+ case inputTypes.DROPDOWN:
+ return (
+ }>
+
+
+ )
case inputTypes.TEXT_FIELD:
return (
);
-
case inputTypes.RADIO:
return (
}>
-
- {
- this.onChangeProperty(oneItem.name, value)
- const newState = oneItem.newState && oneItem.newState(value);
- this.setState(newState);
- }}
- disabled={this.state.disabled[oneItem.name]}
- width={'80px'}
- />
-
-
+
+ }>
+
+ {
+ this.onChangeProperty(oneItem.name, value)
+ const newState = oneItem.newState && oneItem.newState(value);
+ this.setState(newState);
+ }}
+ disabled={this.state.disabled[oneItem.name]}
+ width={'80px'}
+ />
+
+
+
);
case inputTypes.NUMERIC_WITH_SWITCH:
return (
@@ -628,7 +777,8 @@ function mapStateToProps (state) {
processingAction: processingCreateJob(state),
serverError: createJobFailure(state),
createJobSuccess: createJobSuccess(state),
- webhooks: webhooksForDropdown(state)
+ webhooks: webhooksForDropdown(state),
+ experiments: chaosExperimentsForDropdown(state)
};
}
@@ -636,7 +786,8 @@ const mapDispatchToProps = {
clearErrorOnCreateJob: Actions.clearErrorOnCreateJob,
createJob: Actions.createJob,
editJob: Actions.editJob,
- getWebhooks: Actions.getWebhooks
+ getWebhooks: Actions.getWebhooks,
+ getChaosExperiments: Actions.getChaosExperiments
};
export default connect(mapStateToProps, mapDispatchToProps)(Form);
diff --git a/ui/src/features/components/JobForm/style.scss b/ui/src/features/components/JobForm/style.scss
index d9831a35a..211187fec 100644
--- a/ui/src/features/components/JobForm/style.scss
+++ b/ui/src/features/components/JobForm/style.scss
@@ -18,4 +18,29 @@
letter-spacing: normal;
color: #557eff;
margin-left: 10px;
-}
\ No newline at end of file
+}
+
+.actions-style {
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: normal;
+ font-style: normal;
+ font-stretch: normal;
+ line-height: normal;
+ letter-spacing: normal;
+ color: #557eff;
+ margin-left: 10px;
+}
+
+.list-container {
+ display: flex;
+ align-items: center;
+}
+
+.list-item {
+ margin: 10px;
+ &__title {
+ color: #557eff;
+
+ }
+}
diff --git a/ui/src/features/components/JobForm/utils.js b/ui/src/features/components/JobForm/utils.js
index 3f494c190..fb4ce9322 100644
--- a/ui/src/features/components/JobForm/utils.js
+++ b/ui/src/features/components/JobForm/utils.js
@@ -29,6 +29,7 @@ export const createStateForEditJob = (job, dropdownWebhooks) => {
export const createJobRequest = (opts) => {
// job_type is for rerun
+ debugger;
let body = {
test_id: opts.test_id,
type: opts.type || opts.job_type,
@@ -38,6 +39,7 @@ export const createJobRequest = (opts) => {
run_immediately: (opts.run_immediately === undefined) ? false : opts.run_immediately,
emails: opts.emails,
webhooks: opts.webhooks,
+ experiments: opts.experiments,
notes: opts.notes,
parallelism: opts.parallelism ? parseInt(opts.parallelism) : undefined,
max_virtual_users: opts.max_virtual_users ? parseInt(opts.max_virtual_users) : undefined
diff --git a/ui/src/features/configurationColumn.js b/ui/src/features/configurationColumn.js
index 112d3e611..c98d29709 100644
--- a/ui/src/features/configurationColumn.js
+++ b/ui/src/features/configurationColumn.js
@@ -1,5 +1,5 @@
import { TableHeader } from '../components/ReactTable';
-import React, { useEffect, useState } from 'react';
+import React, { useState } from 'react';
import { get } from 'lodash';
import Checkbox from '../components/Checkbox/Checkbox';
diff --git a/ui/src/features/redux/selectors/chaosExperimentsSelector.js b/ui/src/features/redux/selectors/chaosExperimentsSelector.js
index b9e4d5854..ad782c7de 100644
--- a/ui/src/features/redux/selectors/chaosExperimentsSelector.js
+++ b/ui/src/features/redux/selectors/chaosExperimentsSelector.js
@@ -1,6 +1,12 @@
+import { createSelector } from 'reselect';
+
export const chaosExperimentsList = (state) => state.ChaosExperimentsReducer.get('chaosExperiments');
export const chaosExperimentsLoading = (state) => state.ChaosExperimentsReducer.get('chaosExperiments_loading');
export const chaosExperimentFailure = (state) => state.ChaosExperimentsReducer.get('chaosExperiment_error');
export const createChaosExperimentSuccess = (state) => state.ChaosExperimentsReducer.get('create_chaosExperiment_success');
export const deleteChaosExperimentSuccess = (state) => state.ChaosExperimentsReducer.get('delete_chaosExperiment_success');
export const deleteChaosExperimentFailure = (state) => state.ChaosExperimentsReducer.get('delete_chaosExperiment_failure');
+
+export const chaosExperimentsForDropdown = createSelector(chaosExperimentsList, (experiments) => {
+ return experiments.map((experiment) => ({ key: experiment.id, value: experiment.name }));
+});