added validation to name field 05/63505/1
authorYarin Dekel <yarind@amdocs.com>
Wed, 29 Aug 2018 14:31:54 +0000 (17:31 +0300)
committerYarin Dekel <yarind@amdocs.com>
Wed, 29 Aug 2018 14:32:05 +0000 (17:32 +0300)
Issue-ID: SDC-1690
Change-Id: I5332fbdcf67b6e21176faf40292a2164753d48c5
Signed-off-by: Yarin Dekel <yarind@amdocs.com>
workflow-designer-ui/src/main/frontend/src/features/version/create/CreateVersionView.jsx
workflow-designer-ui/src/main/frontend/src/features/version/create/__tests__/__snapshots__/CreateVersionView_snapshot-test.js.snap
workflow-designer-ui/src/main/frontend/src/features/workflow/create/CreateWorkflow.js
workflow-designer-ui/src/main/frontend/src/features/workflow/create/CreateWorkflowView.jsx
workflow-designer-ui/src/main/frontend/src/features/workflow/create/__tests__/CreateWorkflowView_snapshot-test.js
workflow-designer-ui/src/main/frontend/src/features/workflow/create/__tests__/__snapshots__/CreateWorkflowView_snapshot-test.js.snap
workflow-designer-ui/src/main/frontend/src/features/workflow/create/__tests__/createWorkflowSaga-test.js
workflow-designer-ui/src/main/frontend/src/features/workflow/create/createWorkflowConstants.js
workflow-designer-ui/src/main/frontend/src/features/workflow/create/createWorkflowSaga.js
workflow-designer-ui/src/main/frontend/src/features/workflow/workflowReducer.js
workflow-designer-ui/src/main/frontend/src/i18n/languages.json

index f3a2ccc..23e5db1 100644 (file)
@@ -60,7 +60,7 @@ class CreateVersionView extends Component {
     render() {
         const { closeCreateVersionModal } = this.props;
         return (
-            <form onSubmit={this.handleSubmitForm}>
+            <form onSubmit={this.handleSubmitForm} autoComplete="off">
                 <div className="new-version-page custom-modal-wrapper">
                     <div className="form-custom-modal">
                         <Select
index b2a5600..190677f 100644 (file)
@@ -27,8 +27,9 @@ import {
 import {
     inputChangeAction,
     submitWorkflowAction,
-    putNameError
+    clearValidationError
 } from 'features/workflow/create/createWorkflowConstants';
+import { clearWorkflowAction } from 'features/workflow/workflowConstants';
 
 function mapStateToProps(state) {
     return {
@@ -44,11 +45,11 @@ function mapDispatchToProps(dispatch) {
     return {
         submitWorkflow: payload => {
             dispatch(submitWorkflowAction(payload));
-            dispatch(hideModalAction());
         },
         closeCreateWorkflowModal: () => dispatch(hideModalAction()),
-        putNameError: () => dispatch(putNameError()),
-        workflowInputChange: payload => dispatch(inputChangeAction(payload))
+        clearValidationError: () => dispatch(clearValidationError()),
+        workflowInputChange: payload => dispatch(inputChangeAction(payload)),
+        clearWorkflow: () => dispatch(clearWorkflowAction)
     };
 }
 
index 5b09132..6aee9f7 100644 (file)
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-import React from 'react';
+import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import { Input, Button } from 'sdc-ui/lib/react';
 import { I18n } from 'react-redux-i18n';
 import Description from 'shared/components/Description';
 
-const CreateWorkflowView = props => {
-    const {
-        workflowInputChange,
-        workflowDescription,
-        workflowName,
-        submitWorkflow,
-        closeCreateWorkflowModal,
-        workflowParams,
-        history,
-        errorMessage,
-        putNameError
-    } = props;
+class CreateWorkflowView extends Component {
+    static propTypes = {
+        submitWorkflow: PropTypes.func,
+        workflowInputChange: PropTypes.func,
+        workflowDescription: PropTypes.string,
+        workflowName: PropTypes.string,
+        closeCreateWorkflowModal: PropTypes.func,
+        workflowParams: PropTypes.object,
+        history: PropTypes.object,
+        errorMessage: PropTypes.string,
+        clearValidationError: PropTypes.func,
+        clearWorkflow: PropTypes.func
+    };
 
-    function handleSubmitForm(e) {
-        e.preventDefault();
-        if (workflowParams.name) {
-            submitWorkflow({ ...workflowParams, history });
-        } else {
-            putNameError();
-        }
+    componentDidMount() {
+        const { clearValidationError, clearWorkflow } = this.props;
+        clearValidationError();
+        clearWorkflow();
     }
+    handleSubmitForm = e => {
+        e.preventDefault();
+        const { workflowParams, history, submitWorkflow } = this.props;
+        submitWorkflow({ ...workflowParams, history });
+    };
 
-    return (
-        <form onSubmit={handleSubmitForm}>
-            <div className="new-workflow-page custom-modal-wrapper">
-                <div className="form-custom-modal">
-                    <Input
-                        name="workflowName"
-                        value={workflowName || ''}
-                        type="text"
-                        label={I18n.t('workflow.general.name')}
-                        onChange={val =>
-                            workflowInputChange({
-                                name: val
-                            })
-                        }
-                        errorMessage={errorMessage}
-                        isRequired
-                    />
-                    <Description
-                        value={workflowDescription || ''}
-                        label={I18n.t('workflow.general.description')}
-                        onDataChange={workflowInputChange}
-                    />
+    render() {
+        const {
+            workflowInputChange,
+            workflowDescription,
+            workflowName,
+            closeCreateWorkflowModal,
+            errorMessage
+        } = this.props;
+        return (
+            <form onSubmit={this.handleSubmitForm} autoComplete="off">
+                <div className="new-workflow-page custom-modal-wrapper">
+                    <div className="form-custom-modal">
+                        <Input
+                            name="workflowName"
+                            value={workflowName || ''}
+                            type="text"
+                            label={I18n.t('workflow.general.name')}
+                            onChange={val =>
+                                workflowInputChange({
+                                    name: val
+                                })
+                            }
+                            errorMessage={errorMessage}
+                            isRequired
+                        />
+                        <Description
+                            value={workflowDescription || ''}
+                            label={I18n.t('workflow.general.description')}
+                            onDataChange={workflowInputChange}
+                        />
+                    </div>
+                    <div className="modal-action-bar sdc-modal__footer">
+                        <Button btnType="primary">
+                            {I18n.t('buttons.createBtn')}
+                        </Button>
+                        <Button
+                            btnType="secondary"
+                            onClick={closeCreateWorkflowModal}>
+                            {I18n.t('buttons.closeBtn')}
+                        </Button>
+                    </div>
                 </div>
-                <div className="modal-action-bar sdc-modal__footer">
-                    <Button btnType="primary">
-                        {I18n.t('buttons.createBtn')}
-                    </Button>
-                    <Button
-                        btnType="secondary"
-                        onClick={closeCreateWorkflowModal}>
-                        {I18n.t('buttons.closeBtn')}
-                    </Button>
-                </div>
-            </div>
-        </form>
-    );
-};
-
-CreateWorkflowView.propTypes = {
-    submitWorkflow: PropTypes.func,
-    workflowInputChange: PropTypes.func,
-    workflowDescription: PropTypes.string,
-    workflowName: PropTypes.string,
-    closeCreateWorkflowModal: PropTypes.func,
-    workflowParams: PropTypes.object,
-    history: PropTypes.object,
-    errorMessage: PropTypes.string,
-    putNameError: PropTypes.func
-};
+            </form>
+        );
+    }
+}
 
 CreateWorkflowView.defaultProps = {
     submitWorkflow: () => {},
     workflowInputChange: () => {},
-    closeCreateWorkflowModal: () => {}
+    closeCreateWorkflowModal: () => {},
+    clearWorkflow: () => {}
 };
 
 export default CreateWorkflowView;
index a0d81c7..e34cea9 100644 (file)
@@ -22,7 +22,14 @@ import CreateWorkflowView from 'features/workflow/create/CreateWorkflowView';
 
 describe('New Workflow View Snapshot', () => {
     it('renders correctly', () => {
-        const tree = renderer.create(<CreateWorkflowView />).toJSON();
+        const tree = renderer
+            .create(
+                <CreateWorkflowView
+                    clearValidationError={() => {}}
+                    clearWorkflow={() => {}}
+                />
+            )
+            .toJSON();
 
         expect(tree).toMatchSnapshot();
     });
index c9782c1..244a66d 100644 (file)
@@ -47,6 +47,12 @@ describe('New workflow saga test', () => {
             }
         };
         const gen = watchSubmitWorkflow(action);
+
+        /**
+         * expecting the error message to return as undefined
+         * from validateNameField method
+         */
+        expect(gen.next().value).toEqual(undefined);
         expect(gen.next().value).toEqual(
             call(newWorkflowApi.createNewWorkflow, action.payload)
         );
index fc67605..de18a1b 100644 (file)
@@ -18,9 +18,13 @@ export const NEW_VERSION = {
     baseId: null,
     description: null
 };
+export const MIN_NAME_LENGTH = 6;
+export const MAX_NAME_LENGTH = 40;
+export const CHARS_VALIDATION_EXP = /^[\w\s\d]+$/;
 export const WORKFLOW_INPUT_CHANGE = 'createWorkflow/INPUT_CHANGE';
 export const SUBMIT_WORKFLOW = 'createWorkflow/SUBMIT_WORKFLOW';
-export const EMPTY_NAME_ERROR = 'createWorkflow/EMPTY_NAME_ERROR';
+export const VALIDATION_ERROR = 'createWorkflow/VALIDATION_ERROR';
+export const CLEAR_VALIDATION_ERROR = 'createWorkflow/CLEAR_VALIDATION_ERROR';
 
 export const inputChangeAction = payload => ({
     type: WORKFLOW_INPUT_CHANGE,
@@ -32,6 +36,9 @@ export const submitWorkflowAction = payload => ({
     payload
 });
 
-export const putNameError = () => ({
-    type: EMPTY_NAME_ERROR
+export const putValidationError = payload => ({
+    type: VALIDATION_ERROR,
+    payload
 });
+
+export const clearValidationError = () => ({ type: CLEAR_VALIDATION_ERROR });
index 7f98800..e918556 100644 (file)
 * limitations under the License.
 */
 import { takeEvery, call, put } from 'redux-saga/effects';
-import { SUBMIT_WORKFLOW } from 'features/workflow/create/createWorkflowConstants';
+import { I18n } from 'react-redux-i18n';
+
+import {
+    SUBMIT_WORKFLOW,
+    NEW_VERSION,
+    MAX_NAME_LENGTH,
+    MIN_NAME_LENGTH,
+    CHARS_VALIDATION_EXP,
+    putValidationError
+} from 'features/workflow/create/createWorkflowConstants';
 import {
     setWorkflowAction,
     clearWorkflowAction
 } from 'features/workflow/workflowConstants';
+import { hideModalAction } from 'shared/modal/modalWrapperActions';
 import newWorkflowApi from 'features/workflow/create/createWorkflowApi';
 import { genericNetworkErrorAction } from 'wfapp/appConstants';
 import { submitVersionAction } from 'features/version/create/createVersionConstants';
-import { NEW_VERSION } from 'features/workflow/create/createWorkflowConstants';
 
 export function* watchSubmitWorkflow(action) {
     try {
-        const workflow = yield call(
-            newWorkflowApi.createNewWorkflow,
-            action.payload
-        );
-        //Calling to create empty version
-        const workflowId = workflow.id;
-        const { history } = action.payload;
-        yield put(submitVersionAction({ history, workflowId, ...NEW_VERSION }));
-        yield put(setWorkflowAction(workflow));
+        const { name } = action.payload;
+        const validationError = yield validateNameField(name);
+        if (validationError) {
+            yield put(putValidationError(validationError));
+        } else {
+            const workflow = yield call(
+                newWorkflowApi.createNewWorkflow,
+                action.payload
+            );
+            //Calling to create empty version
+            const workflowId = workflow.id;
+            const { history } = action.payload;
+            yield put(
+                submitVersionAction({ history, workflowId, ...NEW_VERSION })
+            );
+            yield put(setWorkflowAction(workflow));
+            yield put(hideModalAction());
+        }
     } catch (error) {
         yield put(clearWorkflowAction);
         yield put(genericNetworkErrorAction(error));
     }
 }
 
+export function validateNameField(name) {
+    let errorMessage;
+    if (!name) {
+        errorMessage = I18n.t('workflow.errorMessages.emptyName');
+    } else if (!CHARS_VALIDATION_EXP.test(name)) {
+        errorMessage = I18n.t('workflow.errorMessages.invalidCharacters');
+    } else if (name.length < MIN_NAME_LENGTH || name.length > MAX_NAME_LENGTH) {
+        errorMessage = I18n.t('workflow.errorMessages.nameFieldLength', {
+            minValue: 6,
+            maxValue: 40
+        });
+    }
+    return errorMessage;
+}
+
 export function* watchWorkflow() {
     yield takeEvery(SUBMIT_WORKFLOW, watchSubmitWorkflow);
 }
index 3b7e17b..c0b0557 100644 (file)
 * limitations under the License.
 */
 
-import { I18n } from 'react-redux-i18n';
 import {
     WORKFLOW_INPUT_CHANGE,
-    EMPTY_NAME_ERROR
+    VALIDATION_ERROR,
+    CLEAR_VALIDATION_ERROR
 } from 'features/workflow/create/createWorkflowConstants';
 import {
     SET_WORKFLOW,
@@ -37,10 +37,15 @@ function workflowReducer(state = {}, action) {
             return {
                 ...action.payload
             };
-        case EMPTY_NAME_ERROR:
+        case VALIDATION_ERROR:
             return {
                 ...state,
-                error: I18n.t('workflow.errorMessages.emptyName')
+                error: action.payload
+            };
+        case CLEAR_VALIDATION_ERROR:
+            return {
+                ...state,
+                error: ''
             };
         default:
             return state;
index 3654645..42f2475 100644 (file)
@@ -64,7 +64,8 @@
             "errorMessages": {
                 "alreadyExists": "Already exists",
                 "invalidCharacters": "Alphanumeric and underscore only",
-                "emptyName": "Field is required"
+                "emptyName": "Field is required",
+                "nameFieldLength": "Name must be at least %{minValue} characters and no more than %{maxValue} characters"
             },
             "composition": {
                 "bpmnError" : "BPMN.IO Error",