Merge remote-tracking branch 'origin/main' into bug/improve_reset_to_previous_task

This commit is contained in:
jasquat 2023-05-10 10:57:55 -04:00
commit 3932435482
29 changed files with 1727 additions and 1088 deletions

13
Jenkinsfile vendored
View File

@ -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}") {

View File

@ -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" --host=0.0.0.0
fi

View File

@ -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"

View File

@ -0,0 +1,24 @@
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: /*

View File

@ -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):

View File

@ -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

View File

@ -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,

View File

@ -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 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,
@ -400,6 +401,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, ProcessInstanceModel]) -> str:
return_hash: dict = {"type": return_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,23 +417,27 @@ 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:
# 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")
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 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 +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 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 +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 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 +482,9 @@ 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)
with ProcessInstanceQueueService.dequeued(process_instance):
yield from _interstitial_stream(process_instance)

View File

@ -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"))

View File

@ -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:

View File

@ -2,6 +2,7 @@
import json
import os
import shutil
import uuid
from glob import glob
from typing import Any
from typing import List
@ -187,9 +188,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 +204,45 @@ 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"
user = UserService.current_user()
if filter_runnable_by_user:
permission_to_check = "create"
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}/{guid_of_non_existent_item_to_check_perms_against}",
)
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)
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 +278,38 @@ 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()
# 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}/{guid_of_non_existent_item_to_check_perms_against}",
)
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)
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."""

View File

@ -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,84 @@ 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,
@ -1688,14 +1763,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 +1781,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 +1798,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,

View File

@ -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"),

View File

@ -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 {
@ -85,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', () => {
@ -120,8 +121,8 @@ describe('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
@ -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')
@ -249,8 +250,8 @@ describe('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];
@ -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')
@ -378,8 +379,8 @@ describe('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];
@ -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')
@ -532,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];
@ -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')
@ -700,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];
@ -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')
@ -871,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];
@ -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')

View File

@ -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 {
@ -88,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
@ -124,7 +125,7 @@ describe('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
@ -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')
@ -279,7 +280,7 @@ describe('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
@ -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')
@ -402,7 +403,7 @@ describe('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
@ -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')
@ -550,7 +551,7 @@ describe('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
@ -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')
@ -658,7 +659,7 @@ describe('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
@ -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')
@ -766,7 +767,7 @@ describe('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
@ -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')
@ -892,7 +893,7 @@ describe('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
@ -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')
@ -1014,7 +1015,7 @@ describe('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
@ -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')
@ -1135,7 +1136,7 @@ describe('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
@ -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')
@ -1281,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
@ -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')
@ -1475,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
@ -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')
@ -1638,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
@ -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')
@ -1827,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
@ -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')
@ -1977,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
@ -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')
@ -2127,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
@ -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')
@ -2294,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
@ -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')
@ -2457,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
@ -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')
@ -2619,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
@ -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')

View File

@ -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', () => {
@ -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', () => {
//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,13 +405,202 @@ describe('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')
// .contains(/^Return to Home$/)
// .click();
cy.contains('Started by me', { timeout: 60000 });
cy.logout();
});
});
//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(/^Return to Home$/)
.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);
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('button')
.contains(/^Close$/)
.click();
// cy.get('button')
// .contains(/^Return to Home$/)
// .click();
cy.contains('Started by me', { timeout: 60000 });
cy.logout();
@ -492,10 +681,10 @@ describe('Initiate a Request - Without Files', () => {
cy.get('#root_item_1_currency').select('AED');
cy.get('#root_item_1_unit_price').type('4500');
cy.get('#root-Yes').click();
//cy.get('#root-Yes').click();
cy.get('button')
.contains(/^Submit$/)
.contains(/^Cancel Request$/)
.click();
cy.get('button')
@ -594,16 +783,12 @@ 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.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$/)
.click();
cy.get('button')
.contains(/^Return to Home$/)
.contains(/^Cancel Request$/)
.click();
cy.contains('Started by me', { timeout: 60000 });
@ -619,7 +804,7 @@ describe('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);
@ -744,9 +929,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 +1088,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')
@ -924,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);
@ -1051,13 +1236,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 +1264,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$/)
@ -1096,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);
@ -1224,9 +1540,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$/)

View File

@ -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 {
@ -84,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
@ -120,7 +121,7 @@ describe('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
@ -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')
@ -228,7 +229,7 @@ describe('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
@ -251,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');
@ -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')
@ -335,7 +336,7 @@ describe('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
@ -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')
@ -461,7 +462,7 @@ describe('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
@ -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')
@ -604,7 +605,7 @@ describe('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
@ -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')
@ -711,7 +712,7 @@ describe('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
@ -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')
@ -845,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
@ -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')
@ -995,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
@ -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')
@ -1145,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
@ -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')
@ -1313,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
@ -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')
@ -1498,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
@ -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')
@ -1648,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
@ -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')

View File

@ -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 {
@ -84,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
@ -120,8 +121,8 @@ describe('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];
@ -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')
@ -240,8 +241,8 @@ describe('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];
@ -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')
@ -347,8 +348,8 @@ describe('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];
@ -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')
@ -478,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];
@ -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();
@ -638,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];
@ -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')
@ -788,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];
@ -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')

View File

@ -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) {

View File

@ -42,7 +42,7 @@ export default function ActiveUsers() {
return (
<div
title={`${activeUser.username} is also viewing this page`}
className="user-circle"
className="user-circle user-circle-for-list"
>
{activeUser.username.charAt(0).toUpperCase()}
</div>

View File

@ -1,4 +1,8 @@
import {
Toggletip,
ToggletipButton,
ToggletipContent,
Button,
Header,
HeaderContainer,
HeaderMenuButton,
@ -77,25 +81,46 @@ export default function NavigationBar() {
return activeKey === menuItemPath;
};
const profileToggletip = (
<div style={{ display: 'flex' }} id="user-profile-toggletip">
<Toggletip isTabTip align="bottom-right">
<ToggletipButton
aria-label="User Actions"
className="user-profile-toggletip-button"
type="button"
>
<div className="user-circle">
{UserService.getPreferredUsername()[0].toUpperCase()}
</div>
</ToggletipButton>
<ToggletipContent className="user-profile-toggletip-content">
<p>
<strong>{UserService.getPreferredUsername()}</strong>
</p>
<p>{UserService.getUserEmail()}</p>
<hr />
<Button className="button-link" onClick={handleLogout}>
<Logout />
&nbsp;&nbsp;Sign out
</Button>
</ToggletipContent>
</Toggletip>
</div>
);
const loginAndLogoutAction = () => {
if (UserService.isLoggedIn()) {
return (
<>
{SPIFF_ENVIRONMENT ? (
<HeaderGlobalAction className="spiff-environment-header-text unclickable-text">
<HeaderGlobalAction
title={`The current SpiffWorkflow environment is: ${SPIFF_ENVIRONMENT}`}
className="spiff-environment-header-text unclickable-text"
>
{SPIFF_ENVIRONMENT}
</HeaderGlobalAction>
) : null}
<HeaderGlobalAction className="username-header-text unclickable-text">
{UserService.getPreferredUsername()}
</HeaderGlobalAction>
<HeaderGlobalAction
aria-label="Logout"
onClick={handleLogout}
data-qa="logout-button"
>
<Logout />
</HeaderGlobalAction>
{profileToggletip}
</>
);
}

View File

@ -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 = (
<Button
kind={
hasAccessToCompleteTask && row.task_id ? 'secondary' : 'ghost'
}
href={interstitialUrl}
>
{hasAccessToCompleteTask && row.task_id ? 'Go' : 'View'}
<Button kind={buttonKind} href={interstitialUrl}>
{buttonText}
</Button>
);
currentRow.push(<td>{buttonElement}</td>);
@ -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
<tr
style={rowStyle}
key={row.id}
key={processInstance.id}
onClick={navigateToProcessInstance}
onKeyDown={navigateToProcessInstance}
>

View File

@ -114,6 +114,12 @@ h2 {
padding-left: 0;
}
.cds--header__global .cds--btn--primary.button-link {
color: #f4f4f4;
background-color: #393939;
padding-left: 0;
}
.cds--header__global .cds--btn--primary {
background-color: #161616
}
@ -129,8 +135,8 @@ code {
}
.app-logo {
height: 85%;
width: 85%;
height: 37px;
width: 152px;
margin-top: 1em;
margin-bottom: 1em;
}
@ -200,6 +206,44 @@ h1.with-icons {
margin-bottom: 1em;
}
.user-profile-toggletip-content {
background-color: #393939;
color: #f4f4f4;
margin-right: 8px;
margin-top: -20px;
}
#user-profile-toggletip {
margin-top: 8px;
margin-right: 12px;
}
#user-profile-toggletip .cds--popover-content::before {
content: none;
}
#user-profile-toggletip .cds--popover--bottom-right .cds--popover-content {
bottom: 11px;
right: -2px;
border-radius: 5px;
border: 3px solid black;
}
#user-profile-toggletip .cds--popover-caret {
display: none;
}
.user-profile-toggletip-button:hover {
background-color: #161616;
}
#user-profile-toggletip .cds--popover--tab-tip__button::after {
content: none;
}
#user-profile-toggletip .cds--popover--tab-tip.cds--popover--open .cds--popover--tab-tip__button{
background-color: #161616;
}
.with-top-margin {
margin-top: 1em;
}
@ -471,49 +515,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;

View File

@ -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;
}

View File

@ -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 <TabList> which totally explodes carbon (in safari!).
// we *think* that null inside a TabList works fine in all other browsers.
if (!permissionsLoaded) {
return null;
}
return (
<>
<Tabs selectedIndex={selectedTabIndex}>

View File

@ -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<any[]>([]);
const [lastTask, setLastTask] = useState<any>(null);
const [processInstance, setProcessInstance] =
useState<ProcessInstance | null>(null);
const [state, setState] = useState<string>('RUNNING');
const params = useParams();
const navigate = useNavigate();
@ -32,11 +34,13 @@ export default function ProcessInterstitial() {
headers: getBasicHeaders(),
onmessage(ev) {
const retValue = JSON.parse(ev.data);
if ('error_code' in retValue) {
addError(retValue);
} else {
setData((prevData) => [retValue, ...prevData]);
setLastTask(retValue);
if (retValue.type === 'error') {
addError(retValue.error);
} else if (retValue.type === 'task') {
setData((prevData) => [retValue.task, ...prevData]);
setLastTask(retValue.task);
} else if (retValue.type === 'unrunnable_instance') {
setProcessInstance(retValue.unrunnable_instance);
}
},
onclose() {
@ -49,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(() => {
@ -68,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';
}
@ -135,30 +147,35 @@ export default function ProcessInterstitial() {
}
const userMessage = (myTask: ProcessInstanceTask) => {
if (!myTask.can_complete && userTasks.includes(myTask.type)) {
return (
<>
<h4 className="heading-compact-01">Waiting on Someone Else</h4>
<p>
This next task is assigned to a different person or team. There is
no action for you to take at this time.
</p>
</>
);
if (!processInstance || processInstance.status === 'completed') {
if (!myTask.can_complete && userTasks.includes(myTask.type)) {
return (
<>
<h4 className="heading-compact-01">Waiting on Someone Else</h4>
<p>
This next task is assigned to a different person or team. There is
no action for you to take at this time.
</p>
</>
);
}
if (shouldRedirect(myTask)) {
return <div>Redirecting you to the next task now ...</div>;
}
if (myTask.error_message) {
return <div>{myTask.error_message}</div>;
}
}
if (shouldRedirect(myTask)) {
return <div>Redirecting you to the next task now ...</div>;
}
if (myTask.error_message) {
return <div>{myTask.error_message}</div>;
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 (
<div>
<InstructionsForEndUser
task={myTask}
defaultMessage="There are no additional instructions or information for this task."
/>
<InstructionsForEndUser task={myTask} defaultMessage={message} />
</div>
);
};

View File

@ -176,6 +176,9 @@ export default function TaskShow() {
if (disabled) {
return;
}
if (!formObject) {
navigate(`/tasks`);
}
let queryParams = '';
if (submitType === FormSubmitType.Draft) {
queryParams = '?save_as_draft=true';
@ -347,20 +350,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 = (
<Button
id="save-as-draft-button"
id="close-button"
disabled={disabled}
kind="secondary"
title="Save changes without submitting."
onClick={() =>
handleFormSubmit(currentFormObject, null, FormSubmitType.Draft)
}
>
Save as draft
Close
</Button>
);
}
@ -369,7 +373,7 @@ export default function TaskShow() {
<Button type="submit" id="submit-button" disabled={disabled}>
{submitButtonText}
</Button>
{saveAsDraftButton}
{closeButton}
<>
{task.signal_buttons.map((signal) => (
<Button

View File

@ -19,7 +19,8 @@ function SelectWidget({
placeholder,
rawErrors = [],
}: WidgetProps) {
const { enumOptions, enumDisabled } = options;
const { enumOptions } = options;
let { enumDisabled } = options;
const emptyValue = multiple ? [] : '';
@ -56,6 +57,36 @@ 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 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,
// but allowing ui:help to grab that spot seems much more appropriate.