From 004a3be2c27646451438e6b63e7d532dbbdeecac Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 4 May 2023 16:52:41 -0400 Subject: [PATCH 01/34] some attempts to not change the process instance status w/ burnettk --- .../models/process_instance.py | 2 +- .../routes/tasks_controller.py | 25 +++++++++++++------ spiffworkflow-frontend/src/interfaces.ts | 12 +++++++++ .../src/routes/ProcessInterstitial.tsx | 13 +++++++--- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index b3ab709d..1668565c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -172,7 +172,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): @classmethod def active_statuses(cls) -> list[str]: - return ["user_input_required", "waiting"] + return ["not_started", "user_input_required", "waiting"] class ProcessInstanceModelSchema(Schema): diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 66f93bb4..20a7e53f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -45,7 +45,7 @@ from spiffworkflow_backend.models.process_instance import ( ) from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from spiffworkflow_backend.models.process_model import ProcessModelInfo -from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from spiffworkflow_backend.models.task import Task, TaskModel # noqa: F401 from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.routes.process_api_blueprint import ( _find_principal_or_raise, @@ -400,6 +400,11 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st extensions = TaskService.get_extensions_from_task_model(task_model) return _render_instructions_for_end_user(task_model, extensions) + def render_data(return_type: str, entity: Union[ApiError, Task]) -> str: + return_hash: dict = {"type": type} + return_hash[return_type] = entity + return f"data: {current_app.json.dumps(return_hash)} \n\n" + tasks = get_reportable_tasks() while True: for spiff_task in tasks: @@ -411,12 +416,12 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield f"data: {current_app.json.dumps(api_error)} \n\n" + yield render_data('error', api_error) raise e if instructions and spiff_task.id not in reported_ids: task = ProcessInstanceService.spiff_task_to_api_task(processor, spiff_task) task.properties = {"instructionsForEndUser": instructions} - yield f"data: {current_app.json.dumps(task)} \n\n" + yield render_data('task', task) reported_ids.append(spiff_task.id) if spiff_task.state == TaskState.READY: try: @@ -427,7 +432,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st api_error = ApiError.from_workflow_exception( "engine_steps_error", "Failed complete an automated task.", exp=wfe ) - yield f"data: {current_app.json.dumps(api_error)} \n\n" + yield render_data('error', api_error) return except Exception as e: api_error = ApiError( @@ -435,7 +440,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield f"data: {current_app.json.dumps(api_error)} \n\n" + yield render_data('error', api_error) return processor.refresh_waiting_tasks() ready_engine_task_count = get_ready_engine_step_count(processor.bpmn_process_instance) @@ -454,10 +459,10 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield f"data: {current_app.json.dumps(api_error)} \n\n" + yield render_data('error', api_error) raise e task.properties = {"instructionsForEndUser": instructions} - yield f"data: {current_app.json.dumps(task)} \n\n" + yield render_data('task', task) def get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int: @@ -472,8 +477,12 @@ def get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int: ) -def _dequeued_interstitial_stream(process_instance_id: int) -> Generator[str, Optional[str], None]: +def _dequeued_interstitial_stream(process_instance_id: int) -> Generator[Optional[str], Optional[str], None]: process_instance = _find_process_instance_by_id_or_raise(process_instance_id) + if process_instance.status not in process_instance.__class__.active_statuses(): + yield f"data: {current_app.json.dumps(process_instance)} \n\n" + return + with ProcessInstanceQueueService.dequeued(process_instance): yield from _interstitial_stream(process_instance) diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 79866cd6..ac0980a2 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -351,3 +351,15 @@ export interface ProcessModelCaller { } export interface UserGroup {} + +type InterstitialPageResponseType = + | 'task_update' + | 'error' + | 'unrunnable_instance'; + +export interface InterstitialPageResponse { + type: InterstitialPageResponseType; + error?: any; + task?: ProcessInstanceTask; + process_instance?: ProcessInstance; +} diff --git a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx index a5f6b3d3..13cefa9f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx @@ -31,12 +31,17 @@ export default function ProcessInterstitial() { { headers: getBasicHeaders(), onmessage(ev) { + console.log('ev', ev); const retValue = JSON.parse(ev.data); - if ('error_code' in retValue) { - addError(retValue); - } else { - setData((prevData) => [retValue, ...prevData]); + if (retValue.type === 'error') { + addError(retValue.error); + } else if (retValue.type === 'task') { + setData((prevData) => [retValue.task, ...prevData]); setLastTask(retValue); + // } else if (retValue.type === 'unrunnable_instance') { + // // setData((prevData) => [retValue.task, ...prevData]); + // // setLastTask(retValue); + // setState('CLOSED'); } }, onclose() { From 97d48ea8b6df7f18d765df96a7ca9fd35751f159 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 5 May 2023 14:01:32 -0400 Subject: [PATCH 02/34] do not perform any tasks if instance is suspended from the interstitial page w/ burnettk --- .../routes/tasks_controller.py | 24 +++++++------ .../integration/test_process_api.py | 27 +++++++------- .../src/routes/ProcessInterstitial.tsx | 36 ++++++++++++------- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 20a7e53f..73aeeb28 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -45,7 +45,8 @@ from spiffworkflow_backend.models.process_instance import ( ) from spiffworkflow_backend.models.process_instance_event import ProcessInstanceEventType from spiffworkflow_backend.models.process_model import ProcessModelInfo -from spiffworkflow_backend.models.task import Task, TaskModel # noqa: F401 +from spiffworkflow_backend.models.task import Task +from spiffworkflow_backend.models.task import TaskModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.routes.process_api_blueprint import ( _find_principal_or_raise, @@ -401,7 +402,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st return _render_instructions_for_end_user(task_model, extensions) def render_data(return_type: str, entity: Union[ApiError, Task]) -> str: - return_hash: dict = {"type": type} + return_hash: dict = {"type": return_type} return_hash[return_type] = entity return f"data: {current_app.json.dumps(return_hash)} \n\n" @@ -416,14 +417,18 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield render_data('error', api_error) + yield render_data("error", api_error) raise e if instructions and spiff_task.id not in reported_ids: task = ProcessInstanceService.spiff_task_to_api_task(processor, spiff_task) task.properties = {"instructionsForEndUser": instructions} - yield render_data('task', task) + yield render_data("task", task) reported_ids.append(spiff_task.id) if spiff_task.state == TaskState.READY: + # do not do any processing if the instance is not currently active + if process_instance.status not in ProcessInstanceModel.active_statuses(): + yield render_data("unrunnable_instance", process_instance) + break try: processor.do_engine_steps(execution_strategy_name="one_at_a_time") processor.do_engine_steps(execution_strategy_name="run_until_user_message") @@ -432,7 +437,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st api_error = ApiError.from_workflow_exception( "engine_steps_error", "Failed complete an automated task.", exp=wfe ) - yield render_data('error', api_error) + yield render_data("error", api_error) return except Exception as e: api_error = ApiError( @@ -440,7 +445,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield render_data('error', api_error) + yield render_data("error", api_error) return processor.refresh_waiting_tasks() ready_engine_task_count = get_ready_engine_step_count(processor.bpmn_process_instance) @@ -459,10 +464,10 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st message=f"Failed to complete an automated task. Error was: {str(e)}", status_code=400, ) - yield render_data('error', api_error) + yield render_data("error", api_error) raise e task.properties = {"instructionsForEndUser": instructions} - yield render_data('task', task) + yield render_data("task", task) def get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int: @@ -479,9 +484,6 @@ def get_ready_engine_step_count(bpmn_process_instance: BpmnWorkflow) -> int: def _dequeued_interstitial_stream(process_instance_id: int) -> Generator[Optional[str], Optional[str], None]: process_instance = _find_process_instance_by_id_or_raise(process_instance_id) - if process_instance.status not in process_instance.__class__.active_statuses(): - yield f"data: {current_app.json.dumps(process_instance)} \n\n" - return with ProcessInstanceQueueService.dequeued(process_instance): yield from _interstitial_stream(process_instance) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 9a124f6d..37c6272d 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1688,14 +1688,15 @@ class TestProcessApi(BaseTest): # The second script task should produce rendered jinja text # The Manual Task should then return a message as well. assert len(results) == 2 - assert json_results[0]["state"] == "READY" - assert json_results[0]["title"] == "Script Task #2" - assert json_results[0]["properties"]["instructionsForEndUser"] == "I am Script Task 2" - assert json_results[1]["state"] == "READY" - assert json_results[1]["title"] == "Manual Task" + # import pdb; pdb.set_trace() + assert json_results[0]['task']["state"] == "READY" + assert json_results[0]['task']["title"] == "Script Task #2" + assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am Script Task 2" + assert json_results[1]['task']["state"] == "READY" + assert json_results[1]['task']["title"] == "Manual Task" response = client.put( - f"/v1.0/tasks/{process_instance_id}/{json_results[1]['id']}", + f"/v1.0/tasks/{process_instance_id}/{json_results[1]['task']['id']}", headers=headers, ) @@ -1705,14 +1706,14 @@ class TestProcessApi(BaseTest): results = list(_dequeued_interstitial_stream(process_instance_id)) json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(results) == 1 - assert json_results[0]["state"] == "READY" - assert json_results[0]["can_complete"] is False - assert json_results[0]["title"] == "Please Approve" - assert json_results[0]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane" + assert json_results[0]['task']["state"] == "READY" + assert json_results[0]['task']["can_complete"] is False + assert json_results[0]['task']["title"] == "Please Approve" + assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am a manual task in another lane" # Complete task as the finance user. response = client.put( - f"/v1.0/tasks/{process_instance_id}/{json_results[0]['id']}", + f"/v1.0/tasks/{process_instance_id}/{json_results[0]['task']['id']}", headers=self.logged_in_headers(finance_user), ) @@ -1722,8 +1723,8 @@ class TestProcessApi(BaseTest): results = list(_dequeued_interstitial_stream(process_instance_id)) json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(json_results) == 1 - assert json_results[0]["state"] == "COMPLETED" - assert json_results[0]["properties"]["instructionsForEndUser"] == "I am the end task" + assert json_results[0]['task']["state"] == "COMPLETED" + assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am the end task" def test_process_instance_list_with_default_list( self, diff --git a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx index 13cefa9f..1998c924 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx @@ -9,12 +9,14 @@ import { getBasicHeaders } from '../services/HttpService'; // @ts-ignore import InstructionsForEndUser from '../components/InstructionsForEndUser'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; -import { ProcessInstanceTask } from '../interfaces'; +import { ProcessInstance, ProcessInstanceTask } from '../interfaces'; import useAPIError from '../hooks/UseApiError'; export default function ProcessInterstitial() { const [data, setData] = useState([]); const [lastTask, setLastTask] = useState(null); + const [processInstance, setProcessInstance] = + useState(null); const [state, setState] = useState('RUNNING'); const params = useParams(); const navigate = useNavigate(); @@ -31,17 +33,14 @@ export default function ProcessInterstitial() { { headers: getBasicHeaders(), onmessage(ev) { - console.log('ev', ev); const retValue = JSON.parse(ev.data); if (retValue.type === 'error') { addError(retValue.error); } else if (retValue.type === 'task') { setData((prevData) => [retValue.task, ...prevData]); - setLastTask(retValue); - // } else if (retValue.type === 'unrunnable_instance') { - // // setData((prevData) => [retValue.task, ...prevData]); - // // setLastTask(retValue); - // setState('CLOSED'); + setLastTask(retValue.task); + } else if (retValue.type === 'unrunnable_instance') { + setProcessInstance(retValue.unrunnable_instance); } }, onclose() { @@ -54,9 +53,14 @@ export default function ProcessInterstitial() { const shouldRedirect = useCallback( (myTask: ProcessInstanceTask): boolean => { - return myTask && myTask.can_complete && userTasks.includes(myTask.type); + return ( + !processInstance && + myTask && + myTask.can_complete && + userTasks.includes(myTask.type) + ); }, - [userTasks] + [userTasks, processInstance] ); useEffect(() => { @@ -73,6 +77,9 @@ export default function ProcessInterstitial() { }, [lastTask, navigate, userTasks, shouldRedirect]); const getStatus = (): string => { + if (processInstance) { + return 'LOCKED'; + } if (!lastTask.can_complete && userTasks.includes(lastTask.type)) { return 'LOCKED'; } @@ -158,12 +165,15 @@ export default function ProcessInterstitial() { return
{myTask.error_message}
; } + let message = + 'There are no additional instructions or information for this task.'; + if (processInstance && processInstance.status !== 'completed') { + message = `The tasks cannot be completed on this instance because its status is "${processInstance.status}".`; + } + return (
- +
); }; From 08d5864e3e3ebe8bd53c902345aa7c954a20e21e Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 5 May 2023 14:54:38 -0400 Subject: [PATCH 03/34] added permissions file for demo site w/ burnettk --- .../config/permissions/demo.yml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml new file mode 100644 index 00000000..8d2008e4 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml @@ -0,0 +1,25 @@ +default_group: everybody + +groups: + admin: + users: + - jason@sartography.com + - kevin@sartography.com + - dan@sartography.com + - alex@sartography.com + - jon@sartography.com + - elizabeth@sartography.com + - madhurya@sartography.com + + tech_writers: + users: + - usama@sartography.com + - phillana@sartography.com + +permissions: + admin: + groups: [admin, tech_writers] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /* + From 6eca7e2503fee19e955ce550e31a36feb6ea40ef Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 5 May 2023 16:14:22 -0400 Subject: [PATCH 04/34] check permissions on process group and model list api endpoints w/ burnettk --- .../config/permissions/demo.yml | 1 - .../routes/process_groups_controller.py | 6 +- .../routes/process_models_controller.py | 3 +- .../services/authorization_service.py | 2 + .../services/process_model_service.py | 65 ++++++++++++++----- .../unit/test_authorization_service.py | 3 +- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml index 8d2008e4..dbded55b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml @@ -22,4 +22,3 @@ permissions: users: [] allowed_permissions: [create, read, update, delete] uri: /* - diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_groups_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_groups_controller.py index 114d327e..6d1a479a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_groups_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_groups_controller.py @@ -87,11 +87,7 @@ def process_group_update(modified_process_group_id: str, body: dict) -> flask.wr def process_group_list( process_group_identifier: Optional[str] = None, page: int = 1, per_page: int = 100 ) -> flask.wrappers.Response: - """Process_group_list.""" - if process_group_identifier is not None: - process_groups = ProcessModelService.get_process_groups(process_group_identifier) - else: - process_groups = ProcessModelService.get_process_groups() + process_groups = ProcessModelService.get_process_groups_for_api(process_group_identifier) batch = ProcessModelService().get_batch(items=process_groups, page=page, per_page=per_page) pages = len(process_groups) // per_page remainder = len(process_groups) % per_page diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py index 1234aaad..eb7f2f9b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py @@ -214,8 +214,7 @@ def process_model_list( page: int = 1, per_page: int = 100, ) -> flask.wrappers.Response: - """Process model list!""" - process_models = ProcessModelService.get_process_models( + process_models = ProcessModelService.get_process_models_for_api( process_group_id=process_group_identifier, recursive=recursive, filter_runnable_by_user=filter_runnable_by_user, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index 71823a8e..5a5bc44d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -577,6 +577,8 @@ class AuthorizationService: permissions_to_assign.append( PermissionToAssign(permission="read", target_uri="/process-instances/report-metadata") ) + permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/process-groups")) + permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/process-models")) permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/processes")) permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/processes/callers")) permissions_to_assign.append(PermissionToAssign(permission="read", target_uri="/service-tasks")) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index 4d9c852f..58191299 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -187,9 +187,7 @@ class ProcessModelService(FileSystemService): cls, process_group_id: Optional[str] = None, recursive: Optional[bool] = False, - filter_runnable_by_user: Optional[bool] = False, ) -> List[ProcessModelInfo]: - """Get process models.""" process_models = [] root_path = FileSystemService.root_path() if process_group_id: @@ -205,22 +203,35 @@ class ProcessModelService(FileSystemService): process_model = cls.get_process_model_from_relative_path(os.path.dirname(process_model_relative_path)) process_models.append(process_model) process_models.sort() - - if filter_runnable_by_user: - user = UserService.current_user() - new_process_model_list = [] - for process_model in process_models: - modified_process_model_id = ProcessModelInfo.modify_process_identifier_for_path_param(process_model.id) - uri = f"/v1.0/process-instances/{modified_process_model_id}" - has_permission = AuthorizationService.user_has_permission( - user=user, permission="create", target_uri=uri - ) - if has_permission: - new_process_model_list.append(process_model) - return new_process_model_list - return process_models + @classmethod + def get_process_models_for_api( + cls, + process_group_id: Optional[str] = None, + recursive: Optional[bool] = False, + filter_runnable_by_user: Optional[bool] = False, + ) -> List[ProcessModelInfo]: + process_models = cls.get_process_models(process_group_id, recursive) + + permission_to_check = "read" + permission_base_uri = "/v1.0/process-models" + if filter_runnable_by_user: + permission_to_check = "create" + permission_base_uri = "/v1.0/process-instances" + + user = UserService.current_user() + new_process_model_list = [] + for process_model in process_models: + modified_process_model_id = ProcessModelInfo.modify_process_identifier_for_path_param(process_model.id) + uri = f"{permission_base_uri}/{modified_process_model_id}" + has_permission = AuthorizationService.user_has_permission( + user=user, permission=permission_to_check, target_uri=uri + ) + if has_permission: + new_process_model_list.append(process_model) + return new_process_model_list + @classmethod def get_parent_group_array_and_cache_it( cls, process_identifier: str, process_group_cache: dict[str, ProcessGroup] @@ -256,6 +267,28 @@ class ProcessModelService(FileSystemService): process_groups.sort() return process_groups + @classmethod + def get_process_groups_for_api( + cls, + process_group_id: Optional[str] = None, + ) -> List[ProcessGroup]: + process_groups = cls.get_process_groups(process_group_id) + + permission_to_check = "read" + permission_base_uri = "/v1.0/process-groups" + + user = UserService.current_user() + new_process_group_list = [] + for process_group in process_groups: + modified_process_group_id = ProcessModelInfo.modify_process_identifier_for_path_param(process_group.id) + uri = f"{permission_base_uri}/{modified_process_group_id}" + has_permission = AuthorizationService.user_has_permission( + user=user, permission=permission_to_check, target_uri=uri + ) + if has_permission: + new_process_group_list.append(process_group) + return new_process_group_list + @classmethod def get_process_group(cls, process_group_id: str, find_direct_nested_items: bool = True) -> ProcessGroup: """Look for a given process_group, and return it.""" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py index 4d945c3f..f30e3bf4 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py @@ -287,9 +287,9 @@ class TestAuthorizationService(BaseTest): client: FlaskClient, with_db_and_bpmn_file_cleanup: None, ) -> None: - """Test_explode_permissions_basic.""" expected_permissions = [ ("/active-users/*", "read"), + ("/process-groups", "read"), ("/process-instances/find-by-id/*", "read"), ("/process-instances/for-me", "create"), ("/process-instances/report-metadata", "read"), @@ -297,6 +297,7 @@ class TestAuthorizationService(BaseTest): ("/process-instances/reports/*", "delete"), ("/process-instances/reports/*", "read"), ("/process-instances/reports/*", "update"), + ("/process-models", "read"), ("/processes", "read"), ("/processes/callers", "read"), ("/service-tasks", "read"), From f1d0cc0c538ead304d7bbc6fdc5bfdf13ac8096f Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 5 May 2023 16:27:31 -0400 Subject: [PATCH 05/34] check if user has access to full process models or groups before checking each one w/ burnettk --- .../services/process_model_service.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index 58191299..ed778607 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -216,11 +216,18 @@ class ProcessModelService(FileSystemService): permission_to_check = "read" permission_base_uri = "/v1.0/process-models" + user = UserService.current_user() if filter_runnable_by_user: permission_to_check = "create" permission_base_uri = "/v1.0/process-instances" - user = UserService.current_user() + # if user has access to uri/* with that permission then there's no reason to check each one individually + has_permission = AuthorizationService.user_has_permission( + user=user, permission=permission_to_check, target_uri=f"{permission_base_uri}/%" + ) + if has_permission: + return process_models + new_process_model_list = [] for process_model in process_models: modified_process_model_id = ProcessModelInfo.modify_process_identifier_for_path_param(process_model.id) @@ -276,8 +283,15 @@ class ProcessModelService(FileSystemService): permission_to_check = "read" permission_base_uri = "/v1.0/process-groups" - user = UserService.current_user() + + # if user has access to uri/* with that permission then there's no reason to check each one individually + has_permission = AuthorizationService.user_has_permission( + user=user, permission=permission_to_check, target_uri=f"{permission_base_uri}/%" + ) + if has_permission: + return process_groups + new_process_group_list = [] for process_group in process_groups: modified_process_group_id = ProcessModelInfo.modify_process_identifier_for_path_param(process_group.id) From e8d4840761262ee276b3991c209d53f1d3f962d8 Mon Sep 17 00:00:00 2001 From: burnettk Date: Sat, 6 May 2023 15:17:48 -0400 Subject: [PATCH 06/34] make logs less chatty and support new localopenid convenience arg when booting backend locally --- spiffworkflow-backend/bin/run_server_locally | 7 +++++- .../spiffworkflow_backend/config/default.py | 5 +++- .../config/permissions/demo.yml | 1 - .../services/logging_service.py | 23 +++++++++++++++++-- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/bin/run_server_locally b/spiffworkflow-backend/bin/run_server_locally index 67443551..7d1f3be9 100755 --- a/spiffworkflow-backend/bin/run_server_locally +++ b/spiffworkflow-backend/bin/run_server_locally @@ -7,10 +7,15 @@ function error_handler() { trap 'error_handler ${LINENO} $?' ERR set -o errtrace -o errexit -o nounset -o pipefail +port="${SPIFFWORKFLOW_BACKEND_PORT:-7000}" + arg="${1:-}" if [[ "$arg" == "acceptance" ]]; then export SPIFFWORKFLOW_BACKEND_LOAD_FIXTURE_DATA=true export SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME=acceptance_tests.yml +elif [[ "$arg" == "localopenid" ]]; then + export SPIFFWORKFLOW_BACKEND_OPEN_ID_SERVER_URL="http://localhost:$port/openid" + export SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME="example.yml" fi if [[ -z "${SPIFFWORKFLOW_BACKEND_ENV:-}" ]]; then @@ -38,5 +43,5 @@ else fi # this line blocks - SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER="${SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER}" FLASK_APP=src/spiffworkflow_backend poetry run flask run -p 7000 + SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER="${SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER}" FLASK_APP=src/spiffworkflow_backend poetry run flask run -p "$port" fi diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py index f6a1a897..8804b3d1 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py @@ -62,13 +62,16 @@ SPIFFWORKFLOW_BACKEND_OPEN_ID_CLIENT_SECRET_KEY = environ.get( default="JXeQExm0JhQPLumgHtIIqf52bDalHz0q", ) # noqa: S105 -# Tenant specific fields is a comma separated list of field names that we will convert to list of strings +# Tenant specific fields is a comma separated list of field names that we will be converted to list of strings # and store in the user table's tenant_specific_field_n columns. You can have up to three items in this # comma-separated list. SPIFFWORKFLOW_BACKEND_OPEN_ID_TENANT_SPECIFIC_FIELDS = environ.get( "SPIFFWORKFLOW_BACKEND_OPEN_ID_TENANT_SPECIFIC_FIELDS" ) +# loggers to use is a comma separated list of logger prefixes that we will be converted to list of strings +SPIFFWORKFLOW_BACKEND_LOGGERS_TO_USE = environ.get("SPIFFWORKFLOW_BACKEND_LOGGERS_TO_USE") + # cryptography or simple-crypt SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB = environ.get( # "SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB", default="cryptography" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml index 8d2008e4..dbded55b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/demo.yml @@ -22,4 +22,3 @@ permissions: users: [] allowed_permissions: [create, read, update, delete] uri: /* - diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py index 3a5ee845..82b858be 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py @@ -129,8 +129,27 @@ def setup_logger(app: Flask) -> None: spiff_logger_filehandler.setFormatter(log_formatter) # these loggers have been deemed too verbose to be useful - garbage_loggers_to_exclude = ["connexion", "flask_cors.extension"] - loggers_to_exclude_from_debug = ["sqlalchemy"] + garbage_loggers_to_exclude = ["connexion", "flask_cors.extension", "flask_cors.core", "sqlalchemy"] + + # if you actually want one of these excluded loggers, there is a config option to turn it on + loggers_to_use = app.config.get("SPIFFWORKFLOW_BACKEND_LOGGERS_TO_USE", []) + if loggers_to_use is None or loggers_to_use == "": + loggers_to_use = [] + else: + loggers_to_use = loggers_to_use.split(",") + for logger_to_use in loggers_to_use: + if logger_to_use in garbage_loggers_to_exclude: + garbage_loggers_to_exclude.remove(logger_to_use) + else: + app.logger.warning( + f"Logger '{logger_to_use}' not found in garbage_loggers_to_exclude. You do not need to add it to" + " SPIFFWORKFLOW_BACKEND_LOGGERS_TO_USE." + ) + + loggers_to_exclude_from_debug = [] + + if "sqlalchemy" not in garbage_loggers_to_exclude: + loggers_to_exclude_from_debug.append("sqlalchemy") # make all loggers act the same for name in logging.root.manager.loggerDict: From dccbf10ddaad9e9629d4320e0846d77d37dba91a Mon Sep 17 00:00:00 2001 From: Madhurya Liyanage Date: Mon, 8 May 2023 16:45:16 +0530 Subject: [PATCH 07/34] Updates related to the changes in the Cancel button and things around that. --- .../pilot/NDR_PP1/consultingfees.cy.js | 29 +- .../cypress/pilot/NDR_PP1/equipment.cy.js | 77 +- .../pilot/NDR_PP1/initiaterequest.cy.js | 340 ++-- .../pilot/NDR_PP1/learninganddev.cy.js | 53 +- .../cypress/pilot/NDR_PP1/otherfees.cy.js | 31 +- .../pilot/NDR_PP1/softwarelicense.cy.js | 1441 +++++++++-------- 6 files changed, 1036 insertions(+), 935 deletions(-) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js index 90a7c144..334b9ee9 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js @@ -13,6 +13,7 @@ const submitWithUser = ( cy.wait(1000); cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); + cy.wait(3000); cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') @@ -53,8 +54,8 @@ const submitWithUser = ( cy.get('.cds--text-area__wrapper').find('#root').clear().type('Providing additional info. It\’s free and easy to post a job. Simply fill in a title, description and budget and competitive bids come within minutes. No job is too big or too small. We\'ve got people for jobs of any size.'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); } else { @@ -182,9 +183,9 @@ describe('Consulting Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, \‘consultant\’ and advisor\’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -311,9 +312,9 @@ describe('Consulting Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -441,9 +442,9 @@ describe('Consulting Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('It\’s free and easy to post a job. Simply fill in a title, description and budget and competitive bids come within minutes. No job is too big or too small. We\'ve got freelancers for jobs of any size or budget across 1800 skills. No job is too complex.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -636,9 +637,9 @@ describe('Consulting Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -804,9 +805,9 @@ describe('Consulting Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -974,9 +975,9 @@ describe('Consulting Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js index a4f8ef3c..ff4ef564 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js @@ -13,6 +13,7 @@ const submitWithUser = ( cy.wait(1000); cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); + cy.wait(3000); cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') @@ -57,8 +58,8 @@ const submitWithUser = ( cy.get('.cds--text-area__wrapper').find('#root').clear().type('Providing additional info. Computer hardware includes the physical parts of a computer, such as the case, central processing unit (CPU), random access memory (RAM), monitor, mouse, keyboard, computer data storage, graphics card, sound card'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); } else { @@ -196,9 +197,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The template for all modern computers is the Von Neumann architecture, detailed in a 1945 paper by Hungarian mathematician John von Neumann. This describes a design architecture for a electronic digital computer with subdivisions of a processing unit'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -322,9 +323,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('When using computer hardware, an upgrade means adding new or additional hardware to a computer that improves its performance, increases its capacity, or adds new features. For example, \nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -443,9 +444,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('A supercomputer is superficially similar to a mainframe but is instead intended for extremely demanding computational tasks. As of November 2021, the fastest supercomputer on the TOP500 supercomputer list is Fugaku, in Japan'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -591,9 +592,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Computer hardware contain dangerous chemicals such as lead, mercury, nickel, and cadmium. According to the EPA these e-wastes have a harmful effect on the environment unless they are disposed properly. \nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -699,9 +700,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, \‘consultant\’ and advisor\’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -807,9 +808,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The template for all modern computers is the Von Neumann architecture, detailed in a 1945 paper by Hungarian mathematician John von Neumann. \nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -933,9 +934,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The template for all modern computers is the Von Neumann architecture, detailed in a 1945 paper by Hungarian mathematician John von Neumann. \nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1055,9 +1056,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The personal computer is one of the most common types of computer due to its versatility and relatively low price. Desktop personal computers have a monitor, a keyboard, a mouse, and a computer case.\nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1176,9 +1177,9 @@ describe('Equipment Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The personal computer is one of the most common types of computer due to its versatility and relatively low price. Desktop personal computers have a monitor, a keyboard, a mouse, and a computer case.\nhttps://en.wikipedia.org/wiki/Computer_hardware'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1394,9 +1395,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1557,9 +1558,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1719,9 +1720,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1909,9 +1910,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2059,9 +2060,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2208,9 +2209,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2375,9 +2376,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2538,9 +2539,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2701,9 +2702,9 @@ describe('Equipment Path - With Files', () => { .attachFile(['png-5mb-2.png']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js index 101d020b..1eb7f770 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js @@ -87,9 +87,9 @@ describe('Initiate a Request - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -179,9 +179,9 @@ describe('Initiate a Request - Without Files', () => { //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -282,15 +282,15 @@ describe('Initiate a Request - Without Files', () => { { timeout: 60000 } ); - cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); + cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S,\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); - cy.contains('Edit the Request').click(); + //cy.contains('Edit the Request').click(); - cy.get('input[value="Edit the Request"]').click(); + //cy.get('input[value="Edit the Request"]').click(); cy.get('button') - .contains(/^Submit$/) + .contains(/^Edit Request$/) .click(); @@ -312,10 +312,10 @@ describe('Initiate a Request - Without Files', () => { 'Task: Review the Request', { timeout: 60000 }); - cy.get('.cds--text-area__wrapper').find('#root').type('EDITING INFO : 2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); + cy.get('.cds--text-area__wrapper').find('#root').type('EDITING INFO'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') .contains(/^Submit$/) @@ -331,8 +331,8 @@ describe('Initiate a Request - Without Files', () => { }); }); - //Save as Draft a Software and License request - Without Files - it.only('Save as Draft a Software and License request', () => { + //Save as Draft a Software and License request 1 - Without Files + it('Save as Draft a Software and License request 1', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -408,9 +408,9 @@ describe('Initiate a Request - Without Files', () => { .contains(/^Save as draft$/) .click(); - cy.get('button') - .contains(/^Return to Home$/) - .click(); + //cy.get('button') + // .contains(/^Return to Home$/) + // .click(); cy.contains('Started by me', { timeout: 60000 }); cy.logout(); @@ -419,98 +419,8 @@ describe('Initiate a Request - Without Files', () => { }); }); - //Cancel a Software and License request 1- Without Files - it('Cancel a Software and License request 1', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('Sware\nA software license is a document that provides legally binding guidelines for the use and distribution of software.\nSoftware licenses typically provide end users with the right to one or more copies of the software without violating copyrights'); - cy.get('#root_criticality').select('High'); - cy.get('#root_period').clear().type('25-11-2025'); - cy.get('body').click(); - cy.get('#root_vendor').clear().type('Microsoft'); - cy.get('#root_payment_method').select('Reimbursement'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - //item 0 - cy.get('#root_item_0_sub_category').select('op_src'); - cy.get('#root_item_0_item_name').clear().type('Open source software is code that is designed to be publicly accessible anyone can see, modify, END'); - cy.get('#root_item_0_qty').clear().type('2'); - cy.get('#root_item_0_currency_type').select('Crypto'); - cy.get('#root_item_0_currency').select('SNT'); - cy.get('#root_item_0_unit_price').type('1915'); - - - cy.get('#root_item > div:nth-child(3) > p > button').click(); - - //item 1 - cy.get('#root_item_1_sub_category').select('lic_and_sub'); - cy.get('#root_item_1_item_name').clear().type('A software license is a document that provides legally binding guidelines for the use and distri END'); - cy.get('#root_item_1_qty').clear().type('1'); - cy.get('#root_item_1_currency_type').select('Fiat'); - cy.get('#root_item_1_currency').select('AED'); - cy.get('#root_item_1_unit_price').type('4500'); - - cy.get('#root-Yes').click(); - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - - - }); - }); - - //Cancel a Software and License request 2- Without Files - it('Cancel a Software and License request 2', () => { + //Save as Draft a Software and License request 2- Without Files + it('Save as Draft a Software and License request 2', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -594,12 +504,98 @@ describe('Initiate a Request - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); - cy.contains('Cancel the Request').click(); + cy.get('button') + .contains(/^Save as draft$/) + .click(); - cy.get('input[value="Cancel the Request"]').click(); + // cy.get('button') + // .contains(/^Return to Home$/) + // .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + + + }); + }); + + //Cancel a Software and License request 1- Without Files + it('Cancel a Software and License request 1', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Sware\nA software license is a document that provides legally binding guidelines for the use and distribution of software.\nSoftware licenses typically provide end users with the right to one or more copies of the software without violating copyrights'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('25-11-2025'); + cy.get('body').click(); + cy.get('#root_vendor').clear().type('Microsoft'); + cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + //item 0 + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source software is code that is designed to be publicly accessible anyone can see, modify, END'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Crypto'); + cy.get('#root_item_0_currency').select('SNT'); + cy.get('#root_item_0_unit_price').type('1915'); + + + cy.get('#root_item > div:nth-child(3) > p > button').click(); + + //item 1 + cy.get('#root_item_1_sub_category').select('lic_and_sub'); + cy.get('#root_item_1_item_name').clear().type('A software license is a document that provides legally binding guidelines for the use and distri END'); + cy.get('#root_item_1_qty').clear().type('1'); + cy.get('#root_item_1_currency_type').select('Fiat'); + cy.get('#root_item_1_currency').select('AED'); + cy.get('#root_item_1_unit_price').type('4500'); + + //cy.get('#root-Yes').click(); cy.get('button') - .contains(/^Submit$/) + .contains(/^Cancel Request$/) .click(); cy.get('button') @@ -610,6 +606,106 @@ describe('Initiate a Request - Without Files', () => { cy.logout(); + }); + }); + + //Cancel a Software and License request 2- Without Files + it.only('Cancel a Software and License request 2', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Sware\nA software license is a document that provides legally binding guidelines for the use and distribution of software.\nSoftware licenses typically provide end users with the right to one or more copies of the software without violating copyrights'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('25-11-2025'); + cy.get('body').click(); + cy.get('#root_vendor').clear().type('Microsoft'); + cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + //item 0 + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source software is code that is designed to be publicly accessible anyone can see, modify, END'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Crypto'); + cy.get('#root_item_0_currency').select('SNT'); + cy.get('#root_item_0_unit_price').type('1915'); + + + cy.get('#root_item > div:nth-child(3) > p > button').click(); + + //item 1 + cy.get('#root_item_1_sub_category').select('lic_and_sub'); + cy.get('#root_item_1_item_name').clear().type('A software license is a document that provides legally binding guidelines for the use and distri END'); + cy.get('#root_item_1_qty').clear().type('1'); + cy.get('#root_item_1_currency_type').select('Fiat'); + cy.get('#root_item_1_currency').select('AED'); + cy.get('#root_item_1_unit_price').type('4500'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); + + //cy.contains('Cancel the Request').click(); + + //cy.get('input[value="Cancel the Request"]').click(); + + cy.get('button') + .contains(/^Cancel Request$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + + }); }); }); @@ -744,9 +840,9 @@ describe('Initiate a Request - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -903,9 +999,9 @@ describe('Initiate a Request - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1051,13 +1147,13 @@ describe('Initiate a Request - With Files', () => { cy.wait(2000); - cy.contains('Edit the Request').click(); + //cy.contains('Edit the Request').click(); - cy.get('input[value="Edit the Request"]').click(); + //cy.get('input[value="Edit the Request"]').click(); cy.get('button') - .contains(/^Submit$/) + .contains(/^Edit Request$/) .click(); @@ -1079,8 +1175,8 @@ describe('Initiate a Request - With Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('EDITING INFO : 2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') .contains(/^Submit$/) @@ -1224,9 +1320,9 @@ describe('Initiate a Request - With Files', () => { cy.wait(2000); - cy.contains('Cancel the Request').click(); + //cy.contains('Cancel the Request').click(); - cy.get('input[value="Cancel the Request"]').click(); + //cy.get('input[value="Cancel the Request"]').click(); cy.get('button') .contains(/^Submit$/) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js index a794344d..887fa635 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js @@ -13,6 +13,7 @@ const submitWithUser = ( cy.wait(1000); cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); + cy.wait(3000); cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') @@ -53,8 +54,8 @@ const submitWithUser = ( cy.get('.cds--text-area__wrapper').find('#root').clear().type('Providing additional info. Learning and development (L&D) is a function within an organization that is responsible for empowering employees\’ growth and developing their knowledge, skills, and capabilities to drive better business performance.'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); } else { @@ -161,9 +162,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('A L&D strategy should be aligned to the organization\’s business strategy and goals with the aim of developing the workforce\’s capability and driving business results.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -269,9 +270,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('The function may be organized centrally, either independently or sitting under human resources (HR); decentralized throughout different business units; or be a hybrid (sometimes referred to as federated) structure.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -376,9 +377,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Current and aspiring talent development professionals can enhance their skills with the various professional education courses offered by ATD Education \nhttps://www.aihr.com/blog/learning-and-development/'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -524,9 +525,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Learning and development is a systematic process to enhance an employee\’s skills, knowledge, and competency, resulting in better performance in a work setting. \nhttps://www.aihr.com/blog/learning-and-development/'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -645,9 +646,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('A L&D strategy should be aligned to the organization\’s business strategy and goals with the aim of developing the workforce\’s capability and driving business results.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -752,9 +753,9 @@ describe('Learning and Development Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Learning and development is a systematic process to enhance an employee\’s skills, knowledge, and competency, resulting in better performance in a work setting. \nhttps://www.aihr.com/blog/learning-and-development/'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -928,9 +929,9 @@ describe('Learning and Development Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1078,9 +1079,9 @@ describe('Learning and Development Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1227,9 +1228,9 @@ describe('Learning and Development Path - With Files', () => { .attachFile(['sampletext.txt']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1418,9 +1419,9 @@ describe('Learning and Development Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1581,9 +1582,9 @@ describe('Learning and Development Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1731,9 +1732,9 @@ describe('Learning and Development Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js index 4375b39c..b49fddf8 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js @@ -13,6 +13,7 @@ const submitWithUser = ( cy.wait(1000); cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); + cy.wait(3000); cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') @@ -53,8 +54,8 @@ const submitWithUser = ( cy.get('.cds--text-area__wrapper').find('#root').clear().type('Providing additional info. Coworking tends to fall into two sides: Those that are real-estate-centric (all about selling desks and offices first) while others are community-centric (focused on building community that happens to also have offices)'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); } else { @@ -173,9 +174,9 @@ describe('Other Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -281,9 +282,9 @@ describe('Other Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -388,9 +389,9 @@ describe('Other Fees Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('It\’s free and easy to post a job. Simply fill in a title, description and budget and competitive bids come within minutes. No job is too big or too small. We\'ve got freelancers for jobs of any size or budget across 1800 skills. No job is too complex.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -571,16 +572,16 @@ describe('Other Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') .contains(/^Submit$/) .click(); - cy.wait(9000); + cy.wait(20000); cy.get('button') .contains(/^Return to Home$/) .click(); @@ -721,9 +722,9 @@ describe('Other Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -871,9 +872,9 @@ describe('Other Fees Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js index c8f04143..c0bbf9f5 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js @@ -13,8 +13,8 @@ const submitWithUser = ( cy.wait(1000); cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); - cy.get('#process-instance-id-input').type(processInstanceId); - cy.wait(1000); + cy.wait(3000); + cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') .contains(/^Submit$/) .click(); @@ -28,7 +28,7 @@ const submitWithUser = ( // approve! if (approvaltype === "approve") { cy.get('#root > label:nth-child(1)').click(); - //cy.get('.cds--text-area__wrapper').find('#root').type(username.concat(' is approving this.')); + cy.get('.cds--text-area__wrapper').find('#root').type(username.concat(' is approving this.')); } else if (approvaltype === "reject") { cy.get('#root > label:nth-child(3)').click(); cy.get('.cds--text-area__wrapper').find('#root').type(username.concat(' is rejecting this.')); @@ -57,8 +57,8 @@ const submitWithUser = ( cy.get('.cds--text-area__wrapper').find('#root').clear().type('Providing additional info. Open source is a term that originally referred to open source software (OSS). Open source software is code that is designed to be publicly accessible—anyone can see, modify, and distribute.'); - cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.contains('Submit the Request').click(); + //cy.get('input[value="Submit the Request"]').click(); } else { @@ -178,9 +178,9 @@ describe('Software and Licenses Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -323,9 +323,9 @@ describe('Software and Licenses Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Software also comes with a license key or product key. The key is used to identify and verify the specific version of the software. It is also used to activate the software device.\nhttps://www.techtarget.com/searchcio/definition/software-license'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -378,7 +378,7 @@ describe('Software and Licenses Path - Without Files', () => { null, "approve" ); - + submitWithUser( infrasmeUsername, infrasmePassword, @@ -472,9 +472,9 @@ describe('Software and Licenses Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Software also comes with a license key or product key. The key is used to identify and verify the specific version of the software. It is also used to activate the software device.\nhttps://www.techtarget.com/searchcio/definition/software-license'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -579,9 +579,9 @@ describe('Software and Licenses Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('They define what users can do with software code they did not write.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -726,9 +726,693 @@ describe('Software and Licenses Path - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('Free and open source software (FOSS) licenses are often referred to as open source. FOSS source code is available to the customer along with the software product. The customer is usually allowed to use the source code to change the software.'); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + + cy.get('button') + .contains(/^Return to Home$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + cy.wait(2000); + + let budgetOwnerUsername = Cypress.env('budgetowner_username'); + let budgetOwnerPassword = Cypress.env('budgetowner_password'); + cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); + cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); + + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + 'Task: Reminder: Check Existing Budget', + "approve" + ); + + let ppgbasmeUsername = Cypress.env('ppgbasme_username'); + let ppgbasmePassword = Cypress.env('ppgbasme_password'); + let securitysmeUsername = Cypress.env('securitysme_username'); + let securitysmePassword = Cypress.env('securitysme_password'); + let infrasmeUsername = Cypress.env('infrasme_username'); + let infrasmePassword = Cypress.env('infrasme_password'); + let legalsmeUsername = Cypress.env('legalsme_username'); + let legalsmePassword = Cypress.env('legalsme_password'); + + submitWithUser( + ppgbasmeUsername, + ppgbasmePassword, + processInstanceId, + null, + "approve" + ); + + submitWithUser( + securitysmeUsername, + securitysmePassword, + processInstanceId, + null, + "approve" + ); + + submitWithUser( + infrasmeUsername, + infrasmePassword, + processInstanceId, + null, + "reject" + ); + + }); + }); + + + }); +}); + +//Software and Licenses Path - Without Files and with only mandatory fields +describe('Software and Licenses Path - Without Files and with only mandatory fields', () => { + + Cypress._.times(1, () => { + + //Everyone approves with CP + it('Everyone approves with CP', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.wait(5000); + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Need to buy a Software'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('24-11-2025'); + cy.get('body').click(); + //cy.get('#root_vendor').clear().type('Embassar'); + //cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source software'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Fiat'); + cy.get('#root_item_0_currency').select('USD'); + cy.get('#root_item_0_unit_price').type('550'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); + + //cy.contains('Submit the Request').click(); + + //cy.get('input[value="Submit the Request"]').click(); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.get('button') + .contains(/^Return to Home$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + cy.wait(2000); + + let budgetOwnerUsername = Cypress.env('budgetowner_username'); + let budgetOwnerPassword = Cypress.env('budgetowner_password'); + cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); + cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); + + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + 'Task: Reminder: Check Existing Budget', + "approve" + ); + + let ppgbasmeUsername = Cypress.env('ppgbasme_username'); + let ppgbasmePassword = Cypress.env('ppgbasme_password'); + let securitysmeUsername = Cypress.env('securitysme_username'); + let securitysmePassword = Cypress.env('securitysme_password'); + let infrasmeUsername = Cypress.env('infrasme_username'); + let infrasmePassword = Cypress.env('infrasme_password'); + let legalsmeUsername = Cypress.env('legalsme_username'); + let legalsmePassword = Cypress.env('legalsme_password'); + + submitWithUser( + ppgbasmeUsername, + ppgbasmePassword, + processInstanceId, + null, + "cpapproved" + ); + + submitWithUser( + legalsmeUsername, + legalsmePassword, + processInstanceId, + null, + "approve" + ); + + }); + }); + + //Everyone approves the request + it('Everyone approves', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.wait(5000); + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('need software'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('20-11-2025'); + cy.get('body').click(); + //cy.get('#root_vendor').clear().type('Embassar'); + //cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('open source'); + cy.get('#root_item_0_qty').clear().type('1'); + cy.get('#root_item_0_currency_type').select('Fiat'); + cy.get('#root_item_0_currency').select('AED'); + cy.get('#root_item_0_unit_price').type('1520'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); + + //cy.contains('Submit the Request').click(); + + //cy.get('input[value="Submit the Request"]').click(); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.get('button') + .contains(/^Return to Home$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + cy.wait(2000); + + let budgetOwnerUsername = Cypress.env('budgetowner_username'); + let budgetOwnerPassword = Cypress.env('budgetowner_password'); + cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); + cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); + + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + 'Task: Reminder: Check Existing Budget', + "approve" + ); + + let ppgbasmeUsername = Cypress.env('ppgbasme_username'); + let ppgbasmePassword = Cypress.env('ppgbasme_password'); + let securitysmeUsername = Cypress.env('securitysme_username'); + let securitysmePassword = Cypress.env('securitysme_password'); + let infrasmeUsername = Cypress.env('infrasme_username'); + let infrasmePassword = Cypress.env('infrasme_password'); + let legalsmeUsername = Cypress.env('legalsme_username'); + let legalsmePassword = Cypress.env('legalsme_password'); + + submitWithUser( + ppgbasmeUsername, + ppgbasmePassword, + processInstanceId, + null, + "approve" + ); + + submitWithUser( + securitysmeUsername, + securitysmePassword, + processInstanceId, + null, + "approve" + ); + + submitWithUser( + infrasmeUsername, + infrasmePassword, + processInstanceId, + 'Task: Update Application Landscape', + "approve" + ); + + submitWithUser( + legalsmeUsername, + legalsmePassword, + processInstanceId, + null, + "approve" + ); + + }); + }); + + //Budget owner rejects the request + it('Budget owner rejects', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.wait(5000); + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Nee license'); + cy.get('#root_criticality').select('Medium'); + cy.get('#root_period').clear().type('20-02-2026'); + cy.get('body').click(); + //cy.get('#root_vendor').clear().type('Subsc LTD'); + //cy.get('#root_payment_method').select('Bank Transfer'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + cy.get('#root_item_0_sub_category').select('lic_and_sub'); + cy.get('#root_item_0_item_name').clear().type('Software development'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Fiat'); + cy.get('#root_item_0_currency').select('EUR'); + cy.get('#root_item_0_unit_price').type('1400'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); + + //cy.contains('Submit the Request').click(); + + //cy.get('input[value="Submit the Request"]').click(); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.get('button') + .contains(/^Return to Home$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + cy.wait(2000); + + let budgetOwnerUsername = Cypress.env('budgetowner_username'); + let budgetOwnerPassword = Cypress.env('budgetowner_password'); + cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); + cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); + + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + null, + "reject" + ); + + }); + }); + + //Budget owner request for additional details + it('Budget owner need more info', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.wait(5000); + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Software needed'); + cy.get('#root_criticality').select('Low'); + cy.get('#root_period').clear().type('05-02-2025'); + cy.get('body').click(); + //cy.get('#root_vendor').clear().type('ABC Licensing Co'); + //cy.get('#root_payment_method').select('Crypto Transfer'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + cy.get('#root_item_0_sub_category').select('lic_and_sub'); + cy.get('#root_item_0_item_name').clear().type('License'); + cy.get('#root_item_0_qty').clear().type('4'); + cy.get('#root_item_0_currency_type').select('Crypto'); + cy.get('#root_item_0_currency').select('SNT'); + cy.get('#root_item_0_unit_price').type('450'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + //cy.get('.cds--text-area__wrapper').find('#root').type('It\’s free and easy to post a job. Simply fill in a title, description and budget and competitive bids come within minutes. No job is too big or too small. We\'ve got freelancers for jobs of any size or budget across 1800 skills. No job is too complex.'); + + //cy.contains('Submit the Request').click(); + + //cy.get('input[value="Submit the Request"]').click(); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.get('button') + .contains(/^Return to Home$/) + .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + cy.wait(2000); + + let budgetOwnerUsername = Cypress.env('budgetowner_username'); + let budgetOwnerPassword = Cypress.env('budgetowner_password'); + cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); + cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); + + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + null, + "needmoreinfo" + ); + + //requestor sending additional info + submitWithUser( + username, + password, + processInstanceId, + null, + "providemoreinfo" + ); + + //budget owner approves second time + submitWithUser( + budgetOwnerUsername, + budgetOwnerPassword, + processInstanceId, + 'Task: Reminder: Check Existing Budget', + "approve" + ); + + let ppgbasmeUsername = Cypress.env('ppgbasme_username'); + let ppgbasmePassword = Cypress.env('ppgbasme_password'); + let legalsmeUsername = Cypress.env('legalsme_username'); + let legalsmePassword = Cypress.env('legalsme_password'); + + submitWithUser( + ppgbasmeUsername, + ppgbasmePassword, + processInstanceId, + null, + "cpapproved" + ); + + submitWithUser( + legalsmeUsername, + legalsmePassword, + processInstanceId, + null, + "approve" + ); + + + }); + }); + + //Infra reject the request + it('Infra rejects', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.wait(5000); + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Software is needed'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('25-11-2025'); + cy.get('body').click(); + // cy.get('#root_vendor').clear().type('Embassar'); + // cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Fiat'); + cy.get('#root_item_0_currency').select('AUD'); + cy.get('#root_item_0_unit_price').type('2416'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); + + //cy.contains('Submit the Request').click(); + + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -803,7 +1487,7 @@ describe('Software and Licenses Path - With Files', () => { Cypress._.times(1, () => { //Everyone approves with CP - it.only('Everyone approves with CP', () => { + it('Everyone approves with CP', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -929,9 +1613,9 @@ describe('Software and Licenses Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1105,9 +1789,9 @@ describe('Software and Licenses Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1297,9 +1981,9 @@ describe('Software and Licenses Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1446,9 +2130,9 @@ describe('Software and Licenses Path - With Files', () => { .attachFile(['png-5mb-2.png']); cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1637,9 +2321,9 @@ describe('Software and Licenses Path - With Files', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -1852,9 +2536,9 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2050,9 +2734,9 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2263,9 +2947,9 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2434,9 +3118,9 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2646,9 +3330,9 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { cy.wait(2000); - cy.contains('Submit the Request').click(); + //cy.contains('Submit the Request').click(); - cy.get('input[value="Submit the Request"]').click(); + //cy.get('input[value="Submit the Request"]').click(); cy.get('button') @@ -2717,686 +3401,3 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { }); }); -//Software and Licenses Path - Without Files and with only mandatory fields -describe.only('Software and Licenses Path - Without Files and with only mandatory fields', () => { - - Cypress._.times(1, () => { - - //Everyone approves with CP - it('Everyone approves with CP', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.wait(5000); - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('Need to buy a Software'); - cy.get('#root_criticality').select('High'); - cy.get('#root_period').clear().type('24-11-2025'); - cy.get('body').click(); - //cy.get('#root_vendor').clear().type('Embassar'); - //cy.get('#root_payment_method').select('Reimbursement'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - cy.get('#root_item_0_sub_category').select('op_src'); - cy.get('#root_item_0_item_name').clear().type('Open source software'); - cy.get('#root_item_0_qty').clear().type('2'); - cy.get('#root_item_0_currency_type').select('Fiat'); - cy.get('#root_item_0_currency').select('USD'); - cy.get('#root_item_0_unit_price').type('550'); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains( - 'Review and provide any supporting information or files for your request.', - { timeout: 60000 } - ); - - //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - - cy.contains('Submit the Request').click(); - - cy.get('input[value="Submit the Request"]').click(); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - cy.wait(2000); - - let budgetOwnerUsername = Cypress.env('budgetowner_username'); - let budgetOwnerPassword = Cypress.env('budgetowner_password'); - cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); - cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); - - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - 'Task: Reminder: Check Existing Budget', - "approve" - ); - - let ppgbasmeUsername = Cypress.env('ppgbasme_username'); - let ppgbasmePassword = Cypress.env('ppgbasme_password'); - let securitysmeUsername = Cypress.env('securitysme_username'); - let securitysmePassword = Cypress.env('securitysme_password'); - let infrasmeUsername = Cypress.env('infrasme_username'); - let infrasmePassword = Cypress.env('infrasme_password'); - let legalsmeUsername = Cypress.env('legalsme_username'); - let legalsmePassword = Cypress.env('legalsme_password'); - - submitWithUser( - ppgbasmeUsername, - ppgbasmePassword, - processInstanceId, - null, - "cpapproved" - ); - - submitWithUser( - legalsmeUsername, - legalsmePassword, - processInstanceId, - null, - "approve" - ); - - }); - }); - - //Everyone approves the request - it('Everyone approves', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.wait(5000); - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('need software'); - cy.get('#root_criticality').select('High'); - cy.get('#root_period').clear().type('20-11-2025'); - cy.get('body').click(); - //cy.get('#root_vendor').clear().type('Embassar'); - //cy.get('#root_payment_method').select('Reimbursement'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - cy.get('#root_item_0_sub_category').select('op_src'); - cy.get('#root_item_0_item_name').clear().type('open source'); - cy.get('#root_item_0_qty').clear().type('1'); - cy.get('#root_item_0_currency_type').select('Fiat'); - cy.get('#root_item_0_currency').select('AED'); - cy.get('#root_item_0_unit_price').type('1520'); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains( - 'Review and provide any supporting information or files for your request.', - { timeout: 60000 } - ); - - //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - - cy.contains('Submit the Request').click(); - - cy.get('input[value="Submit the Request"]').click(); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - cy.wait(2000); - - let budgetOwnerUsername = Cypress.env('budgetowner_username'); - let budgetOwnerPassword = Cypress.env('budgetowner_password'); - cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); - cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); - - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - 'Task: Reminder: Check Existing Budget', - "approve" - ); - - let ppgbasmeUsername = Cypress.env('ppgbasme_username'); - let ppgbasmePassword = Cypress.env('ppgbasme_password'); - let securitysmeUsername = Cypress.env('securitysme_username'); - let securitysmePassword = Cypress.env('securitysme_password'); - let infrasmeUsername = Cypress.env('infrasme_username'); - let infrasmePassword = Cypress.env('infrasme_password'); - let legalsmeUsername = Cypress.env('legalsme_username'); - let legalsmePassword = Cypress.env('legalsme_password'); - - submitWithUser( - ppgbasmeUsername, - ppgbasmePassword, - processInstanceId, - null, - "approve" - ); - - submitWithUser( - securitysmeUsername, - securitysmePassword, - processInstanceId, - null, - "approve" - ); - - submitWithUser( - infrasmeUsername, - infrasmePassword, - processInstanceId, - 'Task: Update Application Landscape', - "approve" - ); - - submitWithUser( - legalsmeUsername, - legalsmePassword, - processInstanceId, - null, - "approve" - ); - - }); - }); - - //Budget owner rejects the request - it('Budget owner rejects', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.wait(5000); - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('Nee license'); - cy.get('#root_criticality').select('Medium'); - cy.get('#root_period').clear().type('20-02-2026'); - cy.get('body').click(); - //cy.get('#root_vendor').clear().type('Subsc LTD'); - //cy.get('#root_payment_method').select('Bank Transfer'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - cy.get('#root_item_0_sub_category').select('lic_and_sub'); - cy.get('#root_item_0_item_name').clear().type('Software development'); - cy.get('#root_item_0_qty').clear().type('2'); - cy.get('#root_item_0_currency_type').select('Fiat'); - cy.get('#root_item_0_currency').select('EUR'); - cy.get('#root_item_0_unit_price').type('1400'); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains( - 'Review and provide any supporting information or files for your request.', - { timeout: 60000 } - ); - - //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - - cy.contains('Submit the Request').click(); - - cy.get('input[value="Submit the Request"]').click(); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - cy.wait(2000); - - let budgetOwnerUsername = Cypress.env('budgetowner_username'); - let budgetOwnerPassword = Cypress.env('budgetowner_password'); - cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); - cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); - - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - null, - "reject" - ); - - }); - }); - - //Budget owner request for additional details - it('Budget owner need more info', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.wait(5000); - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('Software needed'); - cy.get('#root_criticality').select('Low'); - cy.get('#root_period').clear().type('05-02-2025'); - cy.get('body').click(); - //cy.get('#root_vendor').clear().type('ABC Licensing Co'); - //cy.get('#root_payment_method').select('Crypto Transfer'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - cy.get('#root_item_0_sub_category').select('lic_and_sub'); - cy.get('#root_item_0_item_name').clear().type('License'); - cy.get('#root_item_0_qty').clear().type('4'); - cy.get('#root_item_0_currency_type').select('Crypto'); - cy.get('#root_item_0_currency').select('SNT'); - cy.get('#root_item_0_unit_price').type('450'); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains( - 'Review and provide any supporting information or files for your request.', - { timeout: 60000 } - ); - - //cy.get('.cds--text-area__wrapper').find('#root').type('It\’s free and easy to post a job. Simply fill in a title, description and budget and competitive bids come within minutes. No job is too big or too small. We\'ve got freelancers for jobs of any size or budget across 1800 skills. No job is too complex.'); - - cy.contains('Submit the Request').click(); - - cy.get('input[value="Submit the Request"]').click(); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - cy.wait(2000); - - let budgetOwnerUsername = Cypress.env('budgetowner_username'); - let budgetOwnerPassword = Cypress.env('budgetowner_password'); - cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); - cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); - - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - null, - "needmoreinfo" - ); - - //requestor sending additional info - submitWithUser( - username, - password, - processInstanceId, - null, - "providemoreinfo" - ); - - //budget owner approves second time - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - 'Task: Reminder: Check Existing Budget', - "approve" - ); - - let ppgbasmeUsername = Cypress.env('ppgbasme_username'); - let ppgbasmePassword = Cypress.env('ppgbasme_password'); - let legalsmeUsername = Cypress.env('legalsme_username'); - let legalsmePassword = Cypress.env('legalsme_password'); - - submitWithUser( - ppgbasmeUsername, - ppgbasmePassword, - processInstanceId, - null, - "cpapproved" - ); - - submitWithUser( - legalsmeUsername, - legalsmePassword, - processInstanceId, - null, - "approve" - ); - - - }); - }); - - //Infra reject the request - it('Infra rejects', () => { - let username = Cypress.env('requestor_username'); - let password = Cypress.env('requestor_password'); - cy.log('=====username : ' + username); - cy.log('=====password : ' + password); - - cy.login(username, password); - cy.visit('/'); - - cy.contains('Start New +').click(); - cy.contains('Request Goods or Services'); - - cy.runPrimaryBpmnFile(true); - - /* cy.contains('Please select the type of request to start the process.'); - // wait a second to ensure we can click the radio button - - cy.wait(2000); - cy.get('input#root-procurement').click(); - cy.wait(2000); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - */ - - cy.contains( - 'Request Goods or Services', - { timeout: 60000 } - ); - - cy.wait(5000); - cy.url().then((currentUrl) => { - // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" - // extract the digits after /tasks - const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; - cy.log('==###############===processInstanceId : ', processInstanceId); - let projectId = Cypress.env('project_id'); - cy.wait(2000); - cy.get('#root_project').select(projectId); - cy.get('#root_category').select('soft_and_lic'); - cy.get('#root_purpose').clear().type('Software is needed'); - cy.get('#root_criticality').select('High'); - cy.get('#root_period').clear().type('25-11-2025'); - cy.get('body').click(); - // cy.get('#root_vendor').clear().type('Embassar'); - // cy.get('#root_payment_method').select('Reimbursement'); - /* cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains('Task: Enter NDR Items', { timeout: 60000 }); - */ - cy.get('#root_item_0_sub_category').select('op_src'); - cy.get('#root_item_0_item_name').clear().type('Open source'); - cy.get('#root_item_0_qty').clear().type('2'); - cy.get('#root_item_0_currency_type').select('Fiat'); - cy.get('#root_item_0_currency').select('AUD'); - cy.get('#root_item_0_unit_price').type('2416'); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - cy.contains( - 'Review and provide any supporting information or files for your request.', - { timeout: 60000 } - ); - - //cy.get('.cds--text-area__wrapper').find('#root').type('For professionals working in the professional services, ‘consultant’ and advisor’ are often used and fall under common terminology. Consultancy.uk zooms in on this field to get a closer look. \n https://www.consultancy.uk/career/what-is-consulting'); - - cy.contains('Submit the Request').click(); - - cy.get('input[value="Submit the Request"]').click(); - - - cy.get('button') - .contains(/^Submit$/) - .click(); - - - cy.get('button') - .contains(/^Return to Home$/) - .click(); - - cy.contains('Started by me', { timeout: 60000 }); - cy.logout(); - cy.wait(2000); - - let budgetOwnerUsername = Cypress.env('budgetowner_username'); - let budgetOwnerPassword = Cypress.env('budgetowner_password'); - cy.log('=====budgetOwnerUsername : ' + budgetOwnerUsername); - cy.log('=====budgetOwnerPassword : ' + budgetOwnerPassword); - - submitWithUser( - budgetOwnerUsername, - budgetOwnerPassword, - processInstanceId, - 'Task: Reminder: Check Existing Budget', - "approve" - ); - - let ppgbasmeUsername = Cypress.env('ppgbasme_username'); - let ppgbasmePassword = Cypress.env('ppgbasme_password'); - let securitysmeUsername = Cypress.env('securitysme_username'); - let securitysmePassword = Cypress.env('securitysme_password'); - let infrasmeUsername = Cypress.env('infrasme_username'); - let infrasmePassword = Cypress.env('infrasme_password'); - let legalsmeUsername = Cypress.env('legalsme_username'); - let legalsmePassword = Cypress.env('legalsme_password'); - - submitWithUser( - ppgbasmeUsername, - ppgbasmePassword, - processInstanceId, - null, - "approve" - ); - - submitWithUser( - securitysmeUsername, - securitysmePassword, - processInstanceId, - null, - "approve" - ); - - submitWithUser( - infrasmeUsername, - infrasmePassword, - processInstanceId, - null, - "reject" - ); - - }); - }); - - - }); -}); \ No newline at end of file From cc3808aa6fd992a20005d334bb3f24ebed479e26 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 8 May 2023 11:31:57 -0400 Subject: [PATCH 08/34] added tests to make sure users can only list process models and groups that they have access to --- .../integration/test_process_api.py | 77 +++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 9a124f6d..f767bd7c 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -702,7 +702,6 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_process_group_list.""" # add 5 groups for i in range(5): group_id = f"test_process_group_{i}" @@ -997,14 +996,13 @@ class TestProcessApi(BaseTest): assert response.json is not None assert "test_group/random_fact" == response.json["process_model_identifier"] - def test_get_process_groups_when_none( + def test_process_group_list_when_none( self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_get_process_groups_when_none.""" response = client.get( "/v1.0/process-groups", headers=self.logged_in_headers(with_super_admin_user), @@ -1013,14 +1011,13 @@ class TestProcessApi(BaseTest): assert response.json is not None assert response.json["results"] == [] - def test_get_process_groups_when_there_are_some( + def test_process_group_list_when_there_are_some( self, app: Flask, client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - """Test_get_process_groups_when_there_are_some.""" self.create_group_and_model_with_bpmn(client, with_super_admin_user) response = client.get( "/v1.0/process-groups", @@ -1033,6 +1030,76 @@ class TestProcessApi(BaseTest): assert response.json["pagination"]["total"] == 1 assert response.json["pagination"]["pages"] == 1 + def test_process_group_list_when_user_has_resticted_access( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="admin_only", process_model_id='random_fact') + self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="all_users", process_model_id='hello_world') + user_one = self.create_user_with_permission(username="user_one", target_uri='/v1.0/process-groups/all_users:*') + self.add_permissions_to_user(user=user_one, target_uri='/v1.0/process-groups', permission_names=['read']) + + response = client.get( + "/v1.0/process-groups", + headers=self.logged_in_headers(with_super_admin_user), + ) + assert response.status_code == 200 + assert response.json is not None + assert len(response.json["results"]) == 2 + assert response.json["pagination"]["count"] == 2 + assert response.json["pagination"]["total"] == 2 + assert response.json["pagination"]["pages"] == 1 + + response = client.get( + "/v1.0/process-groups", + headers=self.logged_in_headers(user_one), + ) + assert response.status_code == 200 + assert response.json is not None + assert len(response.json["results"]) == 1 + assert response.json['results'][0]['id'] == 'all_users' + assert response.json["pagination"]["count"] == 1 + assert response.json["pagination"]["total"] == 1 + assert response.json["pagination"]["pages"] == 1 + + def test_process_model_list_when_user_has_resticted_access( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="admin_only", process_model_id='random_fact') + self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="all_users", process_model_id='hello_world') + user_one = self.create_user_with_permission(username="user_one", target_uri='/v1.0/process-models/all_users:*') + self.add_permissions_to_user(user=user_one, target_uri='/v1.0/process-models', permission_names=['read']) + + response = client.get( + "/v1.0/process-models?recursive=true", + headers=self.logged_in_headers(with_super_admin_user), + ) + assert response.status_code == 200 + assert response.json is not None + assert len(response.json["results"]) == 2 + assert response.json["pagination"]["count"] == 2 + assert response.json["pagination"]["total"] == 2 + assert response.json["pagination"]["pages"] == 1 + + response = client.get( + "/v1.0/process-models?recursive=true", + headers=self.logged_in_headers(user_one), + ) + assert response.status_code == 200 + assert response.json is not None + assert len(response.json["results"]) == 1 + assert response.json['results'][0]['id'] == 'all_users/hello_world' + assert response.json["pagination"]["count"] == 1 + assert response.json["pagination"]["total"] == 1 + assert response.json["pagination"]["pages"] == 1 + def test_get_process_group_when_found( self, app: Flask, From f10036ce4ab6e511a8eaff1babe0d2be124021be Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 8 May 2023 11:34:45 -0400 Subject: [PATCH 09/34] pyl --- .../integration/test_process_api.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index f767bd7c..cccc32c8 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1037,10 +1037,14 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="admin_only", process_model_id='random_fact') - self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="all_users", process_model_id='hello_world') - user_one = self.create_user_with_permission(username="user_one", target_uri='/v1.0/process-groups/all_users:*') - self.add_permissions_to_user(user=user_one, target_uri='/v1.0/process-groups', permission_names=['read']) + self.create_group_and_model_with_bpmn( + client, with_super_admin_user, process_group_id="admin_only", process_model_id="random_fact" + ) + self.create_group_and_model_with_bpmn( + client, with_super_admin_user, process_group_id="all_users", process_model_id="hello_world" + ) + user_one = self.create_user_with_permission(username="user_one", target_uri="/v1.0/process-groups/all_users:*") + self.add_permissions_to_user(user=user_one, target_uri="/v1.0/process-groups", permission_names=["read"]) response = client.get( "/v1.0/process-groups", @@ -1060,7 +1064,7 @@ class TestProcessApi(BaseTest): assert response.status_code == 200 assert response.json is not None assert len(response.json["results"]) == 1 - assert response.json['results'][0]['id'] == 'all_users' + assert response.json["results"][0]["id"] == "all_users" assert response.json["pagination"]["count"] == 1 assert response.json["pagination"]["total"] == 1 assert response.json["pagination"]["pages"] == 1 @@ -1072,10 +1076,14 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: - self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="admin_only", process_model_id='random_fact') - self.create_group_and_model_with_bpmn(client, with_super_admin_user, process_group_id="all_users", process_model_id='hello_world') - user_one = self.create_user_with_permission(username="user_one", target_uri='/v1.0/process-models/all_users:*') - self.add_permissions_to_user(user=user_one, target_uri='/v1.0/process-models', permission_names=['read']) + self.create_group_and_model_with_bpmn( + client, with_super_admin_user, process_group_id="admin_only", process_model_id="random_fact" + ) + self.create_group_and_model_with_bpmn( + client, with_super_admin_user, process_group_id="all_users", process_model_id="hello_world" + ) + user_one = self.create_user_with_permission(username="user_one", target_uri="/v1.0/process-models/all_users:*") + self.add_permissions_to_user(user=user_one, target_uri="/v1.0/process-models", permission_names=["read"]) response = client.get( "/v1.0/process-models?recursive=true", @@ -1095,7 +1103,7 @@ class TestProcessApi(BaseTest): assert response.status_code == 200 assert response.json is not None assert len(response.json["results"]) == 1 - assert response.json['results'][0]['id'] == 'all_users/hello_world' + assert response.json["results"][0]["id"] == "all_users/hello_world" assert response.json["pagination"]["count"] == 1 assert response.json["pagination"]["total"] == 1 assert response.json["pagination"]["pages"] == 1 From 24ea45b324a3e4fa1b36eff0210428d03617059e Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 8 May 2023 13:20:40 -0400 Subject: [PATCH 10/34] pyl and i am not sure how mypy missed that one typing issue --- .../routes/tasks_controller.py | 2 +- .../integration/test_process_api.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 73aeeb28..e3491cfb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -401,7 +401,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st extensions = TaskService.get_extensions_from_task_model(task_model) return _render_instructions_for_end_user(task_model, extensions) - def render_data(return_type: str, entity: Union[ApiError, Task]) -> str: + def render_data(return_type: str, entity: Union[ApiError, Task, ProcessInstanceModel]) -> str: return_hash: dict = {"type": return_type} return_hash[return_type] = entity return f"data: {current_app.json.dumps(return_hash)} \n\n" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 37c6272d..ab3303c1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1689,11 +1689,11 @@ class TestProcessApi(BaseTest): # The Manual Task should then return a message as well. assert len(results) == 2 # import pdb; pdb.set_trace() - assert json_results[0]['task']["state"] == "READY" - assert json_results[0]['task']["title"] == "Script Task #2" - assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am Script Task 2" - assert json_results[1]['task']["state"] == "READY" - assert json_results[1]['task']["title"] == "Manual Task" + assert json_results[0]["task"]["state"] == "READY" + assert json_results[0]["task"]["title"] == "Script Task #2" + assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am Script Task 2" + assert json_results[1]["task"]["state"] == "READY" + assert json_results[1]["task"]["title"] == "Manual Task" response = client.put( f"/v1.0/tasks/{process_instance_id}/{json_results[1]['task']['id']}", @@ -1706,10 +1706,10 @@ class TestProcessApi(BaseTest): results = list(_dequeued_interstitial_stream(process_instance_id)) json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(results) == 1 - assert json_results[0]['task']["state"] == "READY" - assert json_results[0]['task']["can_complete"] is False - assert json_results[0]['task']["title"] == "Please Approve" - assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am a manual task in another lane" + assert json_results[0]["task"]["state"] == "READY" + assert json_results[0]["task"]["can_complete"] is False + assert json_results[0]["task"]["title"] == "Please Approve" + assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am a manual task in another lane" # Complete task as the finance user. response = client.put( @@ -1723,8 +1723,8 @@ class TestProcessApi(BaseTest): results = list(_dequeued_interstitial_stream(process_instance_id)) json_results = list(map(lambda x: json.loads(x[5:]), results)) # type: ignore assert len(json_results) == 1 - assert json_results[0]['task']["state"] == "COMPLETED" - assert json_results[0]['task']["properties"]["instructionsForEndUser"] == "I am the end task" + assert json_results[0]["task"]["state"] == "COMPLETED" + assert json_results[0]["task"]["properties"]["instructionsForEndUser"] == "I am the end task" def test_process_instance_list_with_default_list( self, From d9db2680855105a715cc93a7a6898ea0246ae819 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 8 May 2023 14:25:49 -0400 Subject: [PATCH 11/34] use a better var name to check perms against w/ burnettk --- .../services/process_model_service.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py index ed778607..1cfc3339 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_model_service.py @@ -2,6 +2,7 @@ import json import os import shutil +import uuid from glob import glob from typing import Any from typing import List @@ -222,8 +223,11 @@ class ProcessModelService(FileSystemService): permission_base_uri = "/v1.0/process-instances" # if user has access to uri/* with that permission then there's no reason to check each one individually + guid_of_non_existent_item_to_check_perms_against = str(uuid.uuid4()) has_permission = AuthorizationService.user_has_permission( - user=user, permission=permission_to_check, target_uri=f"{permission_base_uri}/%" + user=user, + permission=permission_to_check, + target_uri=f"{permission_base_uri}/{guid_of_non_existent_item_to_check_perms_against}", ) if has_permission: return process_models @@ -286,8 +290,11 @@ class ProcessModelService(FileSystemService): user = UserService.current_user() # if user has access to uri/* with that permission then there's no reason to check each one individually + guid_of_non_existent_item_to_check_perms_against = str(uuid.uuid4()) has_permission = AuthorizationService.user_has_permission( - user=user, permission=permission_to_check, target_uri=f"{permission_base_uri}/%" + user=user, + permission=permission_to_check, + target_uri=f"{permission_base_uri}/{guid_of_non_existent_item_to_check_perms_against}", ) if has_permission: return process_groups From c5f15a346f75aa920bb8a8604833fbb58a29ff2e Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 8 May 2023 15:36:57 -0400 Subject: [PATCH 12/34] listen on all interfaces w/ jasquat --- spiffworkflow-backend/bin/run_server_locally | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spiffworkflow-backend/bin/run_server_locally b/spiffworkflow-backend/bin/run_server_locally index 7d1f3be9..7d9ebc09 100755 --- a/spiffworkflow-backend/bin/run_server_locally +++ b/spiffworkflow-backend/bin/run_server_locally @@ -43,5 +43,5 @@ else fi # this line blocks - SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER="${SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER}" FLASK_APP=src/spiffworkflow_backend poetry run flask run -p "$port" + SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER="${SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER}" FLASK_APP=src/spiffworkflow_backend poetry run flask run -p "$port" --host=0.0.0.0 fi From bc5cb048f6ac9501f0d2a8a3baee4d5f6e590050 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 8 May 2023 16:13:30 -0400 Subject: [PATCH 13/34] fix configuration page on safari w/ jasquat --- spiffworkflow-frontend/src/routes/Configuration.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/Configuration.tsx b/spiffworkflow-frontend/src/routes/Configuration.tsx index 6b378162..4a514389 100644 --- a/spiffworkflow-frontend/src/routes/Configuration.tsx +++ b/spiffworkflow-frontend/src/routes/Configuration.tsx @@ -23,10 +23,11 @@ export default function Configuration() { [targetUris.authenticationListPath]: ['GET'], [targetUris.secretListPath]: ['GET'], }; - const { ability } = usePermissionFetcher(permissionRequestData); + const { ability, permissionsLoaded } = usePermissionFetcher( + permissionRequestData + ); useEffect(() => { - console.log('Configuration remove error'); removeError(); let newSelectedTabIndex = 0; if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) { @@ -35,6 +36,13 @@ export default function Configuration() { setSelectedTabIndex(newSelectedTabIndex); }, [location, removeError]); + // wow, if you do not check to see if the permissions are loaded, then in safari, + // you will get {null} inside the which totally explodes carbon (in safari!). + // we *think* that null inside a TabList works fine in all other browsers. + if (!permissionsLoaded) { + return null; + } + return ( <> From 6325c9345aec8cf6726d2da98739b841807b9942 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 8 May 2023 16:24:52 -0400 Subject: [PATCH 14/34] avoid percentage widths, which appear to not do what we want in safari w/ jasquat --- spiffworkflow-frontend/src/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 7f815787..9595fa7d 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -129,8 +129,8 @@ code { } .app-logo { - height: 85%; - width: 85%; + height: 37px; + width: 152px; margin-top: 1em; margin-bottom: 1em; } From ebc10041018ddd3152eb122a429237555010d021 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 8 May 2023 17:03:21 -0400 Subject: [PATCH 15/34] make all browsers act the same way on our site w/ jasquat --- .../src/themes/carbon/SelectWidget/SelectWidget.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx index 7e644055..6f55ef1b 100644 --- a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -56,6 +56,17 @@ function SelectWidget({ errorMessageForField = rawErrors[0]; } + // ok. so in safari, the select widget showed the first option, whereas in chrome it forced you to select an option. + // this change causes chrome to also show the first option. now all browsers are consistent on our site. you no longer have to select anything. + // setting the value prop causes it to show the first option. + // calling onChange actually gets it set in the form data. + if (value === undefined) { + if (enumOptions) { + value = enumOptions[0].value; + onChange(value); + } + } + // maybe use placeholder somehow. it was previously jammed into the helperText field, // but allowing ui:help to grab that spot seems much more appropriate. From 13f51f9f828aa24c9afa6588903d3eb5dea64016 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 8 May 2023 23:04:16 -0400 Subject: [PATCH 16/34] Revert "make all browsers act the same way on our site w/ jasquat" This reverts commit ebc10041018ddd3152eb122a429237555010d021. --- .../src/themes/carbon/SelectWidget/SelectWidget.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx index 6f55ef1b..7e644055 100644 --- a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -56,17 +56,6 @@ function SelectWidget({ errorMessageForField = rawErrors[0]; } - // ok. so in safari, the select widget showed the first option, whereas in chrome it forced you to select an option. - // this change causes chrome to also show the first option. now all browsers are consistent on our site. you no longer have to select anything. - // setting the value prop causes it to show the first option. - // calling onChange actually gets it set in the form data. - if (value === undefined) { - if (enumOptions) { - value = enumOptions[0].value; - onChange(value); - } - } - // maybe use placeholder somehow. it was previously jammed into the helperText field, // but allowing ui:help to grab that spot seems much more appropriate. From fd4d43f44153667e8b12b3b32c8e926ce56566e0 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 9 May 2023 09:30:09 -0400 Subject: [PATCH 17/34] Revert "Revert "make all browsers act the same way on our site w/ jasquat"" This reverts commit 13f51f9f828aa24c9afa6588903d3eb5dea64016. --- .../src/themes/carbon/SelectWidget/SelectWidget.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx index 7e644055..6f55ef1b 100644 --- a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -56,6 +56,17 @@ function SelectWidget({ errorMessageForField = rawErrors[0]; } + // ok. so in safari, the select widget showed the first option, whereas in chrome it forced you to select an option. + // this change causes chrome to also show the first option. now all browsers are consistent on our site. you no longer have to select anything. + // setting the value prop causes it to show the first option. + // calling onChange actually gets it set in the form data. + if (value === undefined) { + if (enumOptions) { + value = enumOptions[0].value; + onChange(value); + } + } + // maybe use placeholder somehow. it was previously jammed into the helperText field, // but allowing ui:help to grab that spot seems much more appropriate. From 62e55562d07032abf88f7ca1aee826f9ecce0d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Soko=C5=82owski?= Date: Tue, 9 May 2023 17:31:54 +0200 Subject: [PATCH 18/34] ci: create version_info.json file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used for exporting version metrics. Signed-off-by: Jakub Sokołowski --- Jenkinsfile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 268239fe..cc4df545 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,3 +1,5 @@ +import groovy.json.JsonBuilder + pipeline { agent { label 'linux' } @@ -45,6 +47,17 @@ pipeline { } stages { + stage('Prep') { + steps { script { + def jobMetaJson = new JsonBuilder([ + git_commit: env.GIT_COMMIT.take(7), + git_branch: env.GIT_BRANCH, + build_id: env.BUILD_ID, + ]).toPrettyString() + sh "echo '${jobMetaJson}' > version_info.json" + } } + } + stage('Build') { steps { script { dir("spiffworkflow-${params.COMPONENT}") { From c77a65bdda3e6ec970126c57c88866f49bc94e15 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 9 May 2023 11:55:59 -0400 Subject: [PATCH 19/34] try adding a blank option for form selects w/ burnettk --- spiffworkflow-frontend/src/routes/TaskShow.tsx | 1 + .../src/themes/carbon/SelectWidget/SelectWidget.tsx | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 3cddc676..e8ec096d 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -267,6 +267,7 @@ export default function TaskShow() { formData: any, errors: any ) => { + console.log('errors', errors); if ('properties' in jsonSchema) { Object.keys(jsonSchema.properties).forEach((propertyKey: string) => { const propertyMetadata = jsonSchema.properties[propertyKey]; diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx index 6f55ef1b..10204145 100644 --- a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -60,11 +60,8 @@ function SelectWidget({ // this change causes chrome to also show the first option. now all browsers are consistent on our site. you no longer have to select anything. // setting the value prop causes it to show the first option. // calling onChange actually gets it set in the form data. - if (value === undefined) { - if (enumOptions) { - value = enumOptions[0].value; - onChange(value); - } + if (enumOptions && enumOptions[0].value !== '') { + enumOptions.unshift({ value: '', label: '' }); } // maybe use placeholder somehow. it was previously jammed into the helperText field, From e0709d0d078742bf2e438a320a50ac38f0f6e558 Mon Sep 17 00:00:00 2001 From: Madhurya Liyanage Date: Tue, 9 May 2023 21:29:36 +0530 Subject: [PATCH 20/34] Enabled only the 'Without Files' describe --- .../cypress/pilot/NDR_PP1/consultingfees.cy.js | 2 +- spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js | 2 +- .../cypress/pilot/NDR_PP1/initiaterequest.cy.js | 4 ++-- .../cypress/pilot/NDR_PP1/learninganddev.cy.js | 4 ++-- spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js | 2 +- .../cypress/pilot/NDR_PP1/softwarelicense.cy.js | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js index 334b9ee9..51c8027f 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js @@ -86,7 +86,7 @@ const submitWithUser = ( }; //Consulting Fees Path - Without Files -describe('Consulting Fees Path - Without Files', () => { +describe.only('Consulting Fees Path - Without Files', () => { Cypress._.times(1, () => { //Budget owner approves the request it('Budget owner approves', () => { diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js index ff4ef564..2930e025 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js @@ -89,7 +89,7 @@ const submitWithUser = ( }; //Equipment Path - Without Files -describe('Equipment Path - Without Files', () => { +describe.only('Equipment Path - Without Files', () => { Cypress._.times(1, () => { //Out of Policy. People Ops Partner Group and Budget owner approves the request diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js index 1eb7f770..63172bde 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js @@ -1,6 +1,6 @@ //Software and License - Without Files -describe('Initiate a Request - Without Files', () => { +describe.only('Initiate a Request - Without Files', () => { Cypress._.times(1, () => { //Submit a Software and License request - Without Files it('Submit a Software and License request', () => { @@ -610,7 +610,7 @@ describe('Initiate a Request - Without Files', () => { }); //Cancel a Software and License request 2- Without Files - it.only('Cancel a Software and License request 2', () => { + it('Cancel a Software and License request 2', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js index 887fa635..98a4941b 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js @@ -85,7 +85,7 @@ const submitWithUser = ( }; //Learning and Development Path - Without Files -describe('Learning and Development Path - Without Files', () => { +describe.only('Learning and Development Path - Without Files', () => { Cypress._.times(1, () => { //People Ops Partner Group approves the request @@ -252,7 +252,7 @@ describe('Learning and Development Path - Without Files', () => { cy.contains('Task: Enter NDR Items', { timeout: 60000 }); */ cy.get('#root_item_0_sub_category').select('books'); - cy.get('#root_item_0_item_name').clear().type('The role of the L&D function has evolved to meet the demands of digital transformation and a modern workforce.'); + cy.get('#root_item_0_item_name').clear().type('The role of the L&D function has evolved to meet the demands of digital transformation and a modern.'); cy.get('#root_item_0_qty').clear().type('5'); cy.get('#root_item_0_currency_type').select('Fiat'); cy.get('#root_item_0_currency').select('EUR'); diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js index b49fddf8..0c47ac27 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js @@ -85,7 +85,7 @@ const submitWithUser = ( cy.wait(2000); }; -describe('Other Fees Path - Without Files', () => { +describe.only('Other Fees Path - Without Files', () => { Cypress._.times(1, () => { //Budget owner approves the request diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js index c0bbf9f5..dbb09350 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js @@ -88,7 +88,7 @@ const submitWithUser = ( }; //Software and Licenses Path - Without Files -describe('Software and Licenses Path - Without Files', () => { +describe.only('Software and Licenses Path - Without Files', () => { Cypress._.times(1, () => { From e38e55cdd8713b8d7599eb1108773a2454571ff2 Mon Sep 17 00:00:00 2001 From: Madhurya Liyanage Date: Tue, 9 May 2023 21:36:54 +0530 Subject: [PATCH 21/34] Updated button paths and added some waits --- spiffworkflow-frontend/cypress/support/commands.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index 5531d7bd..0737e842 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -59,11 +59,13 @@ Cypress.Commands.add('login', (username, password) => { }); Cypress.Commands.add('logout', (selector, ...args) => { - cy.getBySel('logout-button').click(); - + cy.wait(2000); + //cy.getBySel('logout-button').click(); + cy.get('#root > div > header > div.cds--header__global > span:nth-child(3) > button > svg').click(); if (Cypress.env('SPIFFWORKFLOW_FRONTEND_AUTH_WITH_KEYCLOAK') === true) { // otherwise we can click logout, quickly load the next page, and the javascript // doesn't have time to actually sign you out + //cy.wait(4000); cy.contains('Sign in to your account'); } else { cy.get('#spiff-login-button').should('exist'); @@ -104,7 +106,10 @@ Cypress.Commands.add( (expectAutoRedirectToHumanTask = false, returnToProcessModelShow = true) => { // cy.getBySel('start-process-instance').click(); // click on button with text Start - cy.get('button') + //cy.get('button') + //cy.get('#process-model-tile-manage-procurement\\/procurement\\/requisition-order-management\\/new-demand-request-procurement > div > button') + cy.get('#process-model-tile-manage-procurement\\/procurement\\/requisition-order-management\\/request-goods-services > div > button') + //cy.get('#process-model-tile-manage-procurement\\/procurement\\/requisition-order-management\\/raise-new-demand-request > div > button') .contains(/^Start$/) .click(); if (expectAutoRedirectToHumanTask) { From 62eb5ccb6d70e038558776973e15a0d4dd1544d3 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 9 May 2023 12:41:09 -0400 Subject: [PATCH 22/34] make safari act a bit more like other browsers by adding a blank option w/ jasquat --- .../carbon/SelectWidget/SelectWidget.tsx | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx index 10204145..436b301e 100644 --- a/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/SelectWidget/SelectWidget.tsx @@ -19,7 +19,8 @@ function SelectWidget({ placeholder, rawErrors = [], }: WidgetProps) { - const { enumOptions, enumDisabled } = options; + const { enumOptions } = options; + let { enumDisabled } = options; const emptyValue = multiple ? [] : ''; @@ -57,11 +58,33 @@ function SelectWidget({ } // ok. so in safari, the select widget showed the first option, whereas in chrome it forced you to select an option. - // this change causes chrome to also show the first option. now all browsers are consistent on our site. you no longer have to select anything. - // setting the value prop causes it to show the first option. - // calling onChange actually gets it set in the form data. - if (enumOptions && enumOptions[0].value !== '') { - enumOptions.unshift({ value: '', label: '' }); + // this change causes causes safari to act a little bit more like chrome, but it's different because we are actually adding + // an element to the dropdown. + // + // https://stackoverflow.com/a/7944490/6090676 safari detection + let isSafari = false; + const ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf('safari') != -1) { + if (ua.indexOf('chrome') === -1) { + isSafari = true; + } + } + + if (isSafari) { + if (enumOptions && enumOptions[0].value !== '') { + enumOptions.unshift({ + value: '', + label: '', + }); + } + // enumDisabled is a list of values for which the option should be disabled. + // we don't really want users to select the fake empty option we are creating here. + // they cannot select it in chrome, after all. + // google is always right. https://news.ycombinator.com/item?id=35862041 + if (enumDisabled === undefined) { + enumDisabled = []; + } + enumDisabled.push(''); } // maybe use placeholder somehow. it was previously jammed into the helperText field, From a385497c33fce26fd01983227b69dbdd78d05725 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Tue, 9 May 2023 12:55:49 -0400 Subject: [PATCH 23/34] Rename Save as draft to Close, add tooltip (#246) --- .../cypress/pilot/NDR_PP1/initiaterequest.cy.js | 4 ++-- spiffworkflow-frontend/src/routes/TaskShow.tsx | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js index 63172bde..d85e337a 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js @@ -405,7 +405,7 @@ describe.only('Initiate a Request - Without Files', () => { cy.get('#root_item_1_unit_price').type('4500'); cy.get('button') - .contains(/^Save as draft$/) + .contains(/^Close$/) .click(); //cy.get('button') @@ -505,7 +505,7 @@ describe.only('Initiate a Request - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); cy.get('button') - .contains(/^Save as draft$/) + .contains(/^Close$/) .click(); // cy.get('button') diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index e8ec096d..7ceacb4c 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -348,20 +348,21 @@ export default function TaskShow() { if (task.state === 'READY') { let submitButtonText = 'Submit'; - let saveAsDraftButton = null; + let closeButton = null; if (task.typename === 'ManualTask') { submitButtonText = 'Continue'; } else if (task.typename === 'UserTask') { - saveAsDraftButton = ( + closeButton = ( ); } @@ -370,7 +371,7 @@ export default function TaskShow() { - {saveAsDraftButton} + {closeButton} <> {task.signal_buttons.map((signal) => ( + +

{UserService.getPreferredUsername()}

+

{UserService.getUserEmail()}

+
+ +
+ + + ); + const loginAndLogoutAction = () => { if (UserService.isLoggedIn()) { return ( @@ -86,16 +126,7 @@ export default function NavigationBar() { {SPIFF_ENVIRONMENT} ) : null} - - {UserService.getPreferredUsername()} - - - - + {popover} ); } diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 9595fa7d..30c4f4bb 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -114,6 +114,23 @@ h2 { padding-left: 0; } +.cds--btn--ghost.button-link-for-popover { + color: #f4f4f4; + padding-left: 0; +} +.cds--btn--ghost.button-link-for-popover:visited { + color: #f4f4f4; + padding-left: 0; +} +.cds--btn--ghost.button-link-for-popover:hover { + color: #f4f4f4; + padding-left: 0; +} +.cds--btn--ghost.button-link-for-popover:visited:hover { + color: #f4f4f4; + padding-left: 0; +} + .cds--header__global .cds--btn--primary { background-color: #161616 } @@ -200,6 +217,22 @@ h1.with-icons { margin-bottom: 1em; } +#profile-user-button { + margin-right: 8px; +} + +.hot-button:hover { + background-color: #161616; +} + +#profile-user-button .cds--popover--tab-tip__button::after { + content: none; +} + +#profile-user-button .cds--popover--tab-tip.cds--popover--open .cds--popover--tab-tip__button{ + background-color: #161616; +} + .with-top-margin { margin-top: 1em; } @@ -471,49 +504,12 @@ svg.notification-icon { border-radius: 50%; color: #fff; font-weight: bold; +} + +.user-circle-for-list { margin-right: 10px; } -.user-circle.color-1 { - background-color: #8e8e8e; -} - -.user-circle.color-2 { - background-color: #a57c63; -} - -.user-circle.color-3 { - background-color: #8f7c7c; -} - -.user-circle.color-4 { - background-color: #9a927f; -} - -.user-circle.color-5 { - background-color: #5e5d5d; -} - -.user-circle.color-6 { - background-color: #676767; -} - -.user-circle.color-7 { - background-color: #6d7d6d; -} - -.user-circle.color-8 { - background-color: #7a7171; -} - -.user-circle.color-9 { - background-color: #837d63; -} - -.user-circle.color-10 { - background-color: #686a70; -} - /* Randomize color assignment */ .user-circle:nth-child(1) { background-color: #8e8e8e; From 97ca7a4a04717e9749b5e61ef291ca0cc983a5ff Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 9 May 2023 17:38:15 -0400 Subject: [PATCH 27/34] use a toggle tip instead of the popover w/ burnettk --- .../src/components/NavigationBar.tsx | 32 ++++++++++++++++++- spiffworkflow-frontend/src/index.css | 27 ++++++++-------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 94e86961..791dbbc4 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -1,4 +1,7 @@ import { + Toggletip, + ToggletipButton, + ToggletipContent, Popover, PopoverContent, Button, @@ -117,6 +120,33 @@ export default function NavigationBar() { ); + const toggletip = ( +
+ + +
+ {UserService.getPreferredUsername()[0].toUpperCase()} +
+
+ +

+ {UserService.getPreferredUsername()} +

+

{UserService.getUserEmail()}

+
+ +
+
+
+ ); + const loginAndLogoutAction = () => { if (UserService.isLoggedIn()) { return ( @@ -126,7 +156,7 @@ export default function NavigationBar() { {SPIFF_ENVIRONMENT} ) : null} - {popover} + {toggletip} ); } diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 30c4f4bb..f7075b49 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -114,20 +114,9 @@ h2 { padding-left: 0; } -.cds--btn--ghost.button-link-for-popover { - color: #f4f4f4; - padding-left: 0; -} -.cds--btn--ghost.button-link-for-popover:visited { - color: #f4f4f4; - padding-left: 0; -} -.cds--btn--ghost.button-link-for-popover:hover { - color: #f4f4f4; - padding-left: 0; -} -.cds--btn--ghost.button-link-for-popover:visited:hover { +.cds--header__global .cds--btn--primary.button-link { color: #f4f4f4; + background-color: #393939; padding-left: 0; } @@ -217,11 +206,21 @@ h1.with-icons { margin-bottom: 1em; } +.profile-user-toggletip { + background-color: #393939; + color: #f4f4f4; + margin-right: 8px; + margin-top: -20px; +} #profile-user-button { + margin-top: 8px; margin-right: 8px; } +#profile-user-button .cds--popover-content::before { + content: none; +} -.hot-button:hover { +.profile-user-toggle-button:hover { background-color: #161616; } From cada0fc033143cdb7d536af087bdf1a36001a370 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 9 May 2023 22:40:48 -0400 Subject: [PATCH 28/34] placement of user profile nav item and remove caret --- .../src/components/NavigationBar.tsx | 5 ++++- spiffworkflow-frontend/src/index.css | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 791dbbc4..a0984d28 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -152,7 +152,10 @@ export default function NavigationBar() { return ( <> {SPIFF_ENVIRONMENT ? ( - + {SPIFF_ENVIRONMENT} ) : null} diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index f7075b49..2ddf376a 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -212,6 +212,7 @@ h1.with-icons { margin-right: 8px; margin-top: -20px; } + #profile-user-button { margin-top: 8px; margin-right: 8px; @@ -220,6 +221,15 @@ h1.with-icons { content: none; } +#profile-user-button .cds--popover--bottom-right .cds--popover-content { + bottom: 11px; + right: -2px; +} + +#profile-user-button .cds--popover-caret { + display: none; +} + .profile-user-toggle-button:hover { background-color: #161616; } From f5761740eaa4e601bda76c36ddd3357a06a17d38 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 9 May 2023 22:55:56 -0400 Subject: [PATCH 29/34] round corners and add border, and close by default --- .../src/components/NavigationBar.tsx | 36 ++----------------- spiffworkflow-frontend/src/index.css | 2 ++ 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index a0984d28..3789cfff 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -86,43 +86,11 @@ export default function NavigationBar() { return activeKey === menuItemPath; }; - const [openTwo, setOpenTwo] = useState(false); - - const popover = ( -
- - - -

{UserService.getPreferredUsername()}

-

{UserService.getUserEmail()}

-
- -
-
-
- ); + const [openProfileMenu, setOpenProfileMenu] = useState(false); const toggletip = (
- + Date: Wed, 10 May 2023 15:24:44 +0530 Subject: [PATCH 30/34] Updated some wait values --- .../pilot/NDR_PP1/consultingfees.cy.js | 24 +++++----- .../cypress/pilot/NDR_PP1/equipment.cy.js | 36 +++++++-------- .../pilot/NDR_PP1/learninganddev.cy.js | 24 +++++----- .../cypress/pilot/NDR_PP1/otherfees.cy.js | 24 +++++----- .../pilot/NDR_PP1/softwarelicense.cy.js | 44 +++++++++---------- 5 files changed, 76 insertions(+), 76 deletions(-) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js index 51c8027f..cea152e8 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/consultingfees.cy.js @@ -121,8 +121,8 @@ describe.only('Consulting Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -250,8 +250,8 @@ describe.only('Consulting Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -379,8 +379,8 @@ describe.only('Consulting Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -533,8 +533,8 @@ describe('Consulting Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -701,8 +701,8 @@ describe('Consulting Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -872,8 +872,8 @@ describe('Consulting Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js index 2930e025..bfa2ad50 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/equipment.cy.js @@ -125,7 +125,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -280,7 +280,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -403,7 +403,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -551,7 +551,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -659,7 +659,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -767,7 +767,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -893,7 +893,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1015,7 +1015,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1136,7 +1136,7 @@ describe.only('Equipment Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1282,7 +1282,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1476,7 +1476,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1639,7 +1639,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1828,7 +1828,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1978,7 +1978,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2128,7 +2128,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2295,7 +2295,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2458,7 +2458,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2620,7 +2620,7 @@ describe('Equipment Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js index 98a4941b..6d543c7c 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/learninganddev.cy.js @@ -121,7 +121,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -229,7 +229,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -336,7 +336,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -462,7 +462,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -605,7 +605,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -712,7 +712,7 @@ describe.only('Learning and Development Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -846,7 +846,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -996,7 +996,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1146,7 +1146,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1314,7 +1314,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1499,7 +1499,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1649,7 +1649,7 @@ describe('Learning and Development Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js index 0c47ac27..bc64aa73 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/otherfees.cy.js @@ -121,8 +121,8 @@ describe.only('Other Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -241,8 +241,8 @@ describe.only('Other Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -348,8 +348,8 @@ describe.only('Other Fees Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -479,8 +479,8 @@ describe('Other Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -639,8 +639,8 @@ describe('Other Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; @@ -789,8 +789,8 @@ describe('Other Fees Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); - cy.url().then((currentUrl) => { + //cy.wait(5000); + cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js index dbb09350..a54299a3 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/softwarelicense.cy.js @@ -14,7 +14,7 @@ const submitWithUser = ( cy.log('=======visit find by id : '); cy.visit('/admin/process-instances/find-by-id'); cy.wait(3000); - cy.get('#process-instance-id-input').type(processInstanceId); + cy.get('#process-instance-id-input').type(processInstanceId); cy.get('button') .contains(/^Submit$/) .click(); @@ -125,7 +125,7 @@ describe.only('Software and Licenses Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(2000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -270,7 +270,7 @@ describe.only('Software and Licenses Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -378,7 +378,7 @@ describe.only('Software and Licenses Path - Without Files', () => { null, "approve" ); - + submitWithUser( infrasmeUsername, infrasmePassword, @@ -431,7 +431,7 @@ describe.only('Software and Licenses Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -538,7 +538,7 @@ describe.only('Software and Licenses Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -685,7 +685,7 @@ describe.only('Software and Licenses Path - Without Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -835,7 +835,7 @@ describe('Software and Licenses Path - Without Files and with only mandatory fi { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -967,7 +967,7 @@ describe('Software and Licenses Path - Without Files and with only mandatory fi { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1115,7 +1115,7 @@ describe('Software and Licenses Path - Without Files and with only mandatory fi { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1222,7 +1222,7 @@ describe('Software and Licenses Path - Without Files and with only mandatory fi { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1369,7 +1369,7 @@ describe('Software and Licenses Path - Without Files and with only mandatory fi { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1519,7 +1519,7 @@ describe('Software and Licenses Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1706,7 +1706,7 @@ describe('Software and Licenses Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -1898,7 +1898,7 @@ describe('Software and Licenses Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2048,7 +2048,7 @@ describe('Software and Licenses Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2238,7 +2238,7 @@ describe('Software and Licenses Path - With Files', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2430,7 +2430,7 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2629,7 +2629,7 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -2843,7 +2843,7 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -3014,7 +3014,7 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks @@ -3226,7 +3226,7 @@ describe('Software and Licenses Path - With Files and Multiple items', () => { { timeout: 60000 } ); - cy.wait(5000); + //cy.wait(5000); cy.url().then((currentUrl) => { // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" // extract the digits after /tasks From 8650d0f97d086aa7436aa7026aacbc57bff98f19 Mon Sep 17 00:00:00 2001 From: Madhurya Liyanage Date: Wed, 10 May 2023 15:25:09 +0530 Subject: [PATCH 31/34] Save as draft changed to Close --- .../pilot/NDR_PP1/initiaterequest.cy.js | 246 +++++++++++++++++- 1 file changed, 233 insertions(+), 13 deletions(-) diff --git a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js index 63172bde..7bb837d3 100644 --- a/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/NDR_PP1/initiaterequest.cy.js @@ -331,8 +331,8 @@ describe.only('Initiate a Request - Without Files', () => { }); }); - //Save as Draft a Software and License request 1 - Without Files - it('Save as Draft a Software and License request 1', () => { + //Close a Software and License request 1 - Without Files + it('Close a Software and License request 1', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -405,7 +405,7 @@ describe.only('Initiate a Request - Without Files', () => { cy.get('#root_item_1_unit_price').type('4500'); cy.get('button') - .contains(/^Save as draft$/) + .contains(/^Close$/) .click(); //cy.get('button') @@ -419,8 +419,97 @@ describe.only('Initiate a Request - Without Files', () => { }); }); - //Save as Draft a Software and License request 2- Without Files - it('Save as Draft a Software and License request 2', () => { + //Close a Software and License request 2 - Without Files + it('Close a Software and License request 2', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Sware\nA software license is a document that provides legally binding guidelines for the use and distribution of software.\nSoftware licenses typically provide end users with the right to one or more copies of the software without violating copyrights'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('25-11-2025'); + cy.get('body').click(); + cy.get('#root_vendor').clear().type('Microsoft'); + cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + //item 0 + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source software is code that is designed to be publicly accessible anyone can see, modify, END'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Crypto'); + cy.get('#root_item_0_currency').select('SNT'); + cy.get('#root_item_0_unit_price').type('1915'); + + + cy.get('#root_item > div:nth-child(3) > p > button').click(); + + //item 1 + cy.get('#root_item_1_sub_category').select('lic_and_sub'); + cy.get('#root_item_1_item_name').clear().type('A software license is a document that provides legally binding guidelines for the use and distri END'); + //cy.get('#root_item_1_qty').clear().type('1'); + //cy.get('#root_item_1_currency_type').select('Fiat'); + //cy.get('#root_item_1_currency').select('AED'); + //cy.get('#root_item_1_unit_price').type('4500'); + + cy.get('button') + .contains(/^Close$/) + .click(); + + //cy.get('button') + // .contains(/^Return to Home$/) + // .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + + + }); + }); + + + //Close a Software and License request 3 - Without Files + it('Close a Software and License request 3', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -505,12 +594,12 @@ describe.only('Initiate a Request - Without Files', () => { cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); cy.get('button') - .contains(/^Save as draft$/) + .contains(/^Close$/) .click(); - // cy.get('button') - // .contains(/^Return to Home$/) - // .click(); + // cy.get('button') + // .contains(/^Return to Home$/) + // .click(); cy.contains('Started by me', { timeout: 60000 }); cy.logout(); @@ -701,7 +790,7 @@ describe.only('Initiate a Request - Without Files', () => { cy.get('button') .contains(/^Cancel Request$/) .click(); - + cy.contains('Started by me', { timeout: 60000 }); cy.logout(); @@ -715,7 +804,7 @@ describe.only('Initiate a Request - Without Files', () => { describe('Initiate a Request - With Files', () => { Cypress._.times(1, () => { //Submit a Software and License request - Without Files - it('Submit a Software and License request', () => { + it('Submit a Software and License request - With Files', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -1020,7 +1109,7 @@ describe('Initiate a Request - With Files', () => { }); //Edit a Software and License request - With Files - it('Edit a Software and License request', () => { + it('Edit a Software and License request - With Files', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); @@ -1192,8 +1281,139 @@ describe('Initiate a Request - With Files', () => { }); }); + //Close a Software and License request 1 - With Files + it('Close a Software and License request 1 - With Files', () => { + let username = Cypress.env('requestor_username'); + let password = Cypress.env('requestor_password'); + cy.log('=====username : ' + username); + cy.log('=====password : ' + password); + + cy.login(username, password); + cy.visit('/'); + + cy.contains('Start New +').click(); + cy.contains('Request Goods or Services'); + + cy.runPrimaryBpmnFile(true); + + /* cy.contains('Please select the type of request to start the process.'); + // wait a second to ensure we can click the radio button + + cy.wait(2000); + cy.get('input#root-procurement').click(); + cy.wait(2000); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + */ + + cy.contains( + 'Request Goods or Services', + { timeout: 60000 } + ); + + cy.url().then((currentUrl) => { + // if url is "/tasks/8/d37c2f0f-016a-4066-b669-e0925b759560" + // extract the digits after /tasks + const processInstanceId = currentUrl.match(/(?<=\/tasks\/)\d+/)[0]; + cy.log('==###############===processInstanceId : ', processInstanceId); + let projectId = Cypress.env('project_id'); + cy.wait(2000); + cy.get('#root_project').select(projectId); + cy.get('#root_category').select('soft_and_lic'); + cy.get('#root_purpose').clear().type('Sware\nA software license is a document that provides legally binding guidelines for the use and distribution of software.\nSoftware licenses typically provide end users with the right to one or more copies of the software without violating copyrights'); + cy.get('#root_criticality').select('High'); + cy.get('#root_period').clear().type('25-11-2025'); + cy.get('body').click(); + cy.get('#root_vendor').clear().type('Microsoft'); + cy.get('#root_payment_method').select('Reimbursement'); + /* cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); + */ + //item 0 + cy.get('#root_item_0_sub_category').select('op_src'); + cy.get('#root_item_0_item_name').clear().type('Open source software is code that is designed to be publicly accessible anyone can see, modify, END'); + cy.get('#root_item_0_qty').clear().type('2'); + cy.get('#root_item_0_currency_type').select('Crypto'); + cy.get('#root_item_0_currency').select('SNT'); + cy.get('#root_item_0_unit_price').type('1915'); + + + cy.get('#root_item > div:nth-child(3) > p > button').click(); + + //item 1 + cy.get('#root_item_1_sub_category').select('lic_and_sub'); + cy.get('#root_item_1_item_name').clear().type('A software license is a document that provides legally binding guidelines for the use and distri END'); + cy.get('#root_item_1_qty').clear().type('1'); + cy.get('#root_item_1_currency_type').select('Fiat'); + cy.get('#root_item_1_currency').select('AED'); + cy.get('#root_item_1_unit_price').type('4500'); + + + cy.get('button') + .contains(/^Submit$/) + .click(); + + cy.contains( + 'Review and provide any supporting information or files for your request.', + { timeout: 60000 } + ); + + cy.get('.cds--text-area__wrapper').find('#root').type('2021 Newest HP 17.3 inch FHD Laptop, AMD Ryzen 5 5500U 6core(Beat i7-1160G7, up to 4.0GHz),16GB RAM, 1TB PCIe SSD, Bluetooth 4.2, WiFi, HDMI, USB-A&C, Windows 10 S, w/Ghost Manta Accessories, Silver\nhttps://www.amazon.com/HP-i7-11G7-Bluetooth-Windows'); + + cy.get('#root > div:nth-child(3) > p > button').click(); + + cy.get("input[type=file]") + .attachFile(['lorem-ipsum.pdf']); + cy.wait(1000); + + cy.get('#root > div:nth-child(3) > p > button').click(); + cy.wait(1000); + + cy.get('#root > div.row.array-item-list > div:nth-child(2) > div > div.cds--sm\\:col-span-1.cds--md\\:col-span-1.cds--lg\\:col-span-1.cds--css-grid-column > div > div > button.btn.btn-default.array-item-move-up > svg').click(); + cy.wait(1000); + cy.get("input[type=file]") + .attachFile(['png-5mb-1.png']); + cy.wait(1000); + + cy.get('#root > div:nth-child(3) > p > button').click(); + cy.wait(1000); + + cy.get('#root > div.row.array-item-list > div:nth-child(3) > div > div.cds--sm\\:col-span-1.cds--md\\:col-span-1.cds--lg\\:col-span-1.cds--css-grid-column > div > div > button.btn.btn-default.array-item-move-up > svg').click(); + cy.wait(1000); + cy.get('#root > div.row.array-item-list > div:nth-child(2) > div > div.cds--sm\\:col-span-1.cds--md\\:col-span-1.cds--lg\\:col-span-1.cds--css-grid-column > div > div > button.btn.btn-default.array-item-move-up > svg').click(); + cy.wait(1000); + + cy.get("input[type=file]") + .attachFile(['Free_Test_Data_1MB_PDF.pdf']); + + cy.wait(2000); + + + cy.get('button') + .contains(/^Close$/) + .click(); + + cy.wait(3000); + + // cy.get('button') + // .contains(/^Return to Home$/) + // .click(); + + cy.contains('Started by me', { timeout: 60000 }); + cy.logout(); + + + }); + }); + //Cancel a Software and License request - With Files - it('Cancel a Software and License request', () => { + it('Cancel a Software and License request - With Files ', () => { let username = Cypress.env('requestor_username'); let password = Cypress.env('requestor_password'); cy.log('=====username : ' + username); From 4ebc2a78bc1630dccbb27523bb990c51791b505d Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 10 May 2023 09:33:21 -0400 Subject: [PATCH 32/34] updated some css class names and moved the profile toggle tip over a couple pixels --- .../src/components/NavigationBar.tsx | 21 +++++++------------ spiffworkflow-frontend/src/index.css | 18 ++++++++-------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 3789cfff..5ac96034 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -2,14 +2,9 @@ import { Toggletip, ToggletipButton, ToggletipContent, - Popover, - PopoverContent, Button, - RadioButtonGroup, - RadioButton, Header, HeaderContainer, - HeaderMenu, HeaderMenuButton, SkipToContent, SideNav, @@ -23,7 +18,7 @@ import { // @ts-ignore } from '@carbon/react'; // @ts-ignore -import { Settings, Logout, Login } from '@carbon/icons-react'; +import { Logout, Login } from '@carbon/icons-react'; import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { Can } from '@casl/react'; @@ -86,21 +81,19 @@ export default function NavigationBar() { return activeKey === menuItemPath; }; - const [openProfileMenu, setOpenProfileMenu] = useState(false); - - const toggletip = ( -
- + const profileToggletip = ( +
+
{UserService.getPreferredUsername()[0].toUpperCase()}
- +

{UserService.getPreferredUsername()}

@@ -127,7 +120,7 @@ export default function NavigationBar() { {SPIFF_ENVIRONMENT} ) : null} - {toggletip} + {profileToggletip} ); } diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index 730b8848..00bd2d8c 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -206,41 +206,41 @@ h1.with-icons { margin-bottom: 1em; } -.profile-user-toggletip { +.user-profile-toggletip-content { background-color: #393939; color: #f4f4f4; margin-right: 8px; margin-top: -20px; } -#profile-user-button { +#user-profile-toggletip { margin-top: 8px; - margin-right: 8px; + margin-right: 12px; } -#profile-user-button .cds--popover-content::before { +#user-profile-toggletip .cds--popover-content::before { content: none; } -#profile-user-button .cds--popover--bottom-right .cds--popover-content { +#user-profile-toggletip .cds--popover--bottom-right .cds--popover-content { bottom: 11px; right: -2px; border-radius: 5px; border: 3px solid black; } -#profile-user-button .cds--popover-caret { +#user-profile-toggletip .cds--popover-caret { display: none; } -.profile-user-toggle-button:hover { +.user-profile-toggletip-button:hover { background-color: #161616; } -#profile-user-button .cds--popover--tab-tip__button::after { +#user-profile-toggletip .cds--popover--tab-tip__button::after { content: none; } -#profile-user-button .cds--popover--tab-tip.cds--popover--open .cds--popover--tab-tip__button{ +#user-profile-toggletip .cds--popover--tab-tip.cds--popover--open .cds--popover--tab-tip__button{ background-color: #161616; } From de3358a2767f7a6f133a89454fe89db25a8c575b Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 10 May 2023 10:13:02 -0400 Subject: [PATCH 33/34] only show task instruction text if process instance is active and display the view button if instance is suspended --- .../components/ProcessInstanceListTable.tsx | 36 +++++++++++-------- .../src/routes/ProcessInterstitial.tsx | 34 +++++++++--------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 84c7bc96..a7f70e6d 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -1578,32 +1578,38 @@ export default function ProcessInstanceListTable({ headers.push('Action'); } - const rows = processInstances.map((row: ProcessInstance) => { + const rows = processInstances.map((processInstance: ProcessInstance) => { const currentRow = reportColumns().map((column: ReportColumn) => { - return formattedColumn(row, column); + return formattedColumn(processInstance, column); }); if (showActionsColumn) { let buttonElement = null; const interstitialUrl = `/process/${modifyProcessIdentifierForPathParam( - row.process_model_identifier - )}/${row.id}/interstitial`; + processInstance.process_model_identifier + )}/${processInstance.id}/interstitial`; const regex = new RegExp(`\\b(${preferredUsername}|${userEmail})\\b`); let hasAccessToCompleteTask = false; if ( canCompleteAllTasks || - (row.potential_owner_usernames || '').match(regex) + (processInstance.potential_owner_usernames || '').match(regex) ) { hasAccessToCompleteTask = true; } + let buttonText = 'View'; + let buttonKind = 'ghost'; + if ( + processInstance.status !== 'suspended' && + hasAccessToCompleteTask && + processInstance.task_id + ) { + buttonText = 'Go'; + buttonKind = 'secondary'; + } + buttonElement = ( - ); currentRow.push({buttonElement}); @@ -1611,18 +1617,18 @@ export default function ProcessInstanceListTable({ const rowStyle = { cursor: 'pointer' }; const modifiedModelId = modifyProcessIdentifierForPathParam( - row.process_model_identifier + processInstance.process_model_identifier ); const navigateToProcessInstance = () => { navigate( - `${processInstanceShowPathPrefix}/${modifiedModelId}/${row.id}` + `${processInstanceShowPathPrefix}/${modifiedModelId}/${processInstance.id}` ); }; return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions diff --git a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx index 1998c924..270a00d2 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInterstitial.tsx @@ -147,22 +147,24 @@ export default function ProcessInterstitial() { } const userMessage = (myTask: ProcessInstanceTask) => { - if (!myTask.can_complete && userTasks.includes(myTask.type)) { - return ( - <> -

Waiting on Someone Else

-

- This next task is assigned to a different person or team. There is - no action for you to take at this time. -

- - ); - } - if (shouldRedirect(myTask)) { - return
Redirecting you to the next task now ...
; - } - if (myTask.error_message) { - return
{myTask.error_message}
; + if (!processInstance || processInstance.status === 'completed') { + if (!myTask.can_complete && userTasks.includes(myTask.type)) { + return ( + <> +

Waiting on Someone Else

+

+ This next task is assigned to a different person or team. There is + no action for you to take at this time. +

+ + ); + } + if (shouldRedirect(myTask)) { + return
Redirecting you to the next task now ...
; + } + if (myTask.error_message) { + return
{myTask.error_message}
; + } } let message = From a96a3f5ac2f2424e30e1c75b6fcf3b2e516a377c Mon Sep 17 00:00:00 2001 From: burnettk Date: Wed, 10 May 2023 10:17:12 -0400 Subject: [PATCH 34/34] fix typo --- .../src/spiffworkflow_backend/routes/tasks_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index e3491cfb..b2ac9fa2 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -435,7 +435,7 @@ def _interstitial_stream(process_instance: ProcessInstanceModel) -> Generator[st processor.save() # Fixme - maybe find a way not to do this on every loop? except WorkflowTaskException as wfe: api_error = ApiError.from_workflow_exception( - "engine_steps_error", "Failed complete an automated task.", exp=wfe + "engine_steps_error", "Failed to complete an automated task.", exp=wfe ) yield render_data("error", api_error) return