diff --git a/axonivy-express-test/.classpath b/axonivy-express-test/.classpath new file mode 100644 index 0000000..106dc80 --- /dev/null +++ b/axonivy-express-test/.classpath @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/axonivy-express-test/.gitignore b/axonivy-express-test/.gitignore new file mode 100644 index 0000000..9b0d458 --- /dev/null +++ b/axonivy-express-test/.gitignore @@ -0,0 +1,19 @@ +# general +Thumbs.db +.DS_Store +*~ +*.log + +# java +*.class +hs_err_pid* + +# maven +target/ +lib/mvn-deps/ + +# ivy +classes/ +src_dataClasses/ +src_wsproc/ +logs/ diff --git a/axonivy-express-test/.project b/axonivy-express-test/.project new file mode 100644 index 0000000..f1fb2cc --- /dev/null +++ b/axonivy-express-test/.project @@ -0,0 +1,53 @@ + + + axonivy-express-test + + + + + + ch.ivyteam.ivy.designer.dataClasses.ui.ivyDataClassBuilder + + + + + ch.ivyteam.ivy.designer.process.ui.ivyWebServiceProcessClassBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + ch.ivyteam.ivy.dialog.form.build.ivyDialogFormBuilder + + + + + ch.ivyteam.ivy.designer.ide.ivyModelValidationBuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + ch.ivyteam.ivy.project.IvyProjectNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.jem.beaninfo.BeanInfoNature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/axonivy-express-test/.settings/ch.ivyteam.ivy.designer.prefs b/axonivy-express-test/.settings/ch.ivyteam.ivy.designer.prefs new file mode 100644 index 0000000..f7bebd8 --- /dev/null +++ b/axonivy-express-test/.settings/ch.ivyteam.ivy.designer.prefs @@ -0,0 +1,4 @@ +ch.ivyteam.ivy.designer.preferences.DataClassPreferencePage\:DEFAULT_NAMESPACE=com.axonivy.utils.axonivyexpress.test +ch.ivyteam.ivy.project.preferences\:PRIMEFACES_VERSION=13 +ch.ivyteam.ivy.project.preferences\:PROJECT_VERSION=120001 +eclipse.preferences.version=1 diff --git a/axonivy-express-test/.settings/org.eclipse.core.resources.prefs b/axonivy-express-test/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/axonivy-express-test/.settings/org.eclipse.jdt.core.prefs b/axonivy-express-test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..23fa13b --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/axonivy-express-test/.settings/org.eclipse.wst.common.component b/axonivy-express-test/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..9cea4c0 --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.wst.common.component @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml new file mode 100644 index 0000000..9b4b9fc --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.xml b/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..c64310d --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/axonivy-express-test/.settings/org.eclipse.wst.css.core.prefs b/axonivy-express-test/.settings/org.eclipse.wst.css.core.prefs new file mode 100644 index 0000000..5ddc6bd --- /dev/null +++ b/axonivy-express-test/.settings/org.eclipse.wst.css.core.prefs @@ -0,0 +1,2 @@ +css-profile/=org.eclipse.wst.css.core.cssprofile.css3 +eclipse.preferences.version=1 diff --git a/axonivy-express-test/config/custom-fields.yaml b/axonivy-express-test/config/custom-fields.yaml new file mode 100644 index 0000000..fbe76a7 --- /dev/null +++ b/axonivy-express-test/config/custom-fields.yaml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/12.0.0/custom-fields.json +# +# == Custom Fields Information == +# +# You can define here your project custom fields. +# Have a look at our documentation for more information. +# +CustomFields: +# Tasks: +# MyTaskCustomField: +# Label: My task custom field +# Description: This new task custom field can be used to ... +# Type: STRING +# Cases: +# MyCaseCustomField: +# Label: My case custom field +# Description: This new case custom field can be used to ... +# Type: STRING +# Starts: +# MyStartCustomField: +# Label: My start custom field +# Description: This new start custom field can be used to ... diff --git a/axonivy-express-test/config/databases.yaml b/axonivy-express-test/config/databases.yaml new file mode 100644 index 0000000..657dabd --- /dev/null +++ b/axonivy-express-test/config/databases.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/12.0.0/databases.json +Databases: diff --git a/axonivy-express-test/config/overrides.any b/axonivy-express-test/config/overrides.any new file mode 100644 index 0000000..f59ec20 --- /dev/null +++ b/axonivy-express-test/config/overrides.any @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/axonivy-express-test/config/persistence.xml b/axonivy-express-test/config/persistence.xml new file mode 100644 index 0000000..d6b96d7 --- /dev/null +++ b/axonivy-express-test/config/persistence.xml @@ -0,0 +1,2 @@ + + diff --git a/axonivy-express-test/config/rest-clients.yaml b/axonivy-express-test/config/rest-clients.yaml new file mode 100644 index 0000000..a277cb1 --- /dev/null +++ b/axonivy-express-test/config/rest-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/12.0.0/rest-clients.json +RestClients: diff --git a/axonivy-express-test/config/roles.xml b/axonivy-express-test/config/roles.xml new file mode 100644 index 0000000..802642f --- /dev/null +++ b/axonivy-express-test/config/roles.xml @@ -0,0 +1,8 @@ + + + Everybody + + TestRole + TestRole + + diff --git a/axonivy-express-test/config/users.xml b/axonivy-express-test/config/users.xml new file mode 100644 index 0000000..b913358 --- /dev/null +++ b/axonivy-express-test/config/users.xml @@ -0,0 +1,13 @@ + + + + express + express + Express User + + + testUser + testUser + Test User + + diff --git a/axonivy-express-test/config/variables.yaml b/axonivy-express-test/config/variables.yaml new file mode 100644 index 0000000..e96d3ef --- /dev/null +++ b/axonivy-express-test/config/variables.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/12.0.0/variables.json +Variables: diff --git a/axonivy-express-test/config/webservice-clients.yaml b/axonivy-express-test/config/webservice-clients.yaml new file mode 100644 index 0000000..5e614cf --- /dev/null +++ b/axonivy-express-test/config/webservice-clients.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://json-schema.axonivy.com/app/12.0.0/webservice-clients.json +WebServiceClients: diff --git a/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/Data.d.json b/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/Data.d.json new file mode 100644 index 0000000..b0fa67d --- /dev/null +++ b/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/Data.d.json @@ -0,0 +1,6 @@ +{ + "$schema" : "https://json-schema.axonivy.com/data-class/12.0.0/data-class.json", + "simpleName" : "Data", + "namespace" : "com.axonivy.utils.axonivyexpress.test", + "isBusinessCaseData" : false +} \ No newline at end of file diff --git a/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/HelperData.d.json b/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/HelperData.d.json new file mode 100644 index 0000000..2344f43 --- /dev/null +++ b/axonivy-express-test/dataclasses/com/axonivy/utils/axonivyexpress/test/HelperData.d.json @@ -0,0 +1,6 @@ +{ + "$schema" : "https://json-schema.axonivy.com/data-class/12.0.0/data-class.json", + "simpleName" : "HelperData", + "namespace" : "com.axonivy.utils.axonivyexpress.test", + "isBusinessCaseData" : false +} \ No newline at end of file diff --git a/axonivy-express-test/pom.xml b/axonivy-express-test/pom.xml new file mode 100644 index 0000000..e7a8cc2 --- /dev/null +++ b/axonivy-express-test/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + com.axonivy.utils.axonivyexpress + axonivy-express-test + 12.0.0-SNAPSHOT + iar-integration-test + + UTF-8 + + + + com.axonivy.utils.axonivyexpress + axonivy-express + ${project.version} + iar + + + com.axonivy.ivy.webtest + web-tester + 12.0.0 + test + + + + + + always + + sonatype + https://oss.sonatype.org/content/repositories/snapshots + + + + + + always + + sonatype + https://oss.sonatype.org/content/repositories/snapshots + + + + src_test + + + + maven-deploy-plugin + 3.0.0-M1 + + true + + + + + + + com.axonivy.ivy.ci + project-build-plugin + 12.0.0 + true + + UTF-8 + false + + + + maven-surefire-plugin + 3.0.0-M4 + + + default-test + test + + true + + + + selenium.web.tests + integration-test + + test + + + + + true + -Dtest.engine.url=${test.engine.url} + -Dtest.engine.app=axonivyexpresstest + + ${test.engine.url} + + + + + + diff --git a/axonivy-express-test/processes/Start Processes/Helper.p.json b/axonivy-express-test/processes/Start Processes/Helper.p.json new file mode 100644 index 0000000..2df75d0 --- /dev/null +++ b/axonivy-express-test/processes/Start Processes/Helper.p.json @@ -0,0 +1,43 @@ +{ + "$schema" : "https://json-schema.axonivy.com/process/12.0.0/process.json", + "id" : "1943EF9CC7A1B392", + "config" : { + "data" : "com.axonivy.utils.axonivyexpress.test.HelperData" + }, + "elements" : [ { + "id" : "f0", + "type" : "RequestStart", + "name" : "cleanData", + "config" : { + "signature" : "cleanData" + }, + "visual" : { + "at" : { "x" : 96, "y" : 64 } + }, + "connect" : [ + { "id" : "f2", "to" : "f3" } + ] + }, { + "id" : "f1", + "type" : "TaskEnd", + "visual" : { + "at" : { "x" : 368, "y" : 64 }, + "labelOffset" : { "x" : 13, "y" : 33 } + } + }, { + "id" : "f3", + "type" : "Script", + "config" : { + "output" : { + "code" : "ivy.var.set(\"Portal.Processes.ExpressProcesses\", \"\");" + }, + "sudo" : true + }, + "visual" : { + "at" : { "x" : 240, "y" : 64 } + }, + "connect" : [ + { "id" : "f4", "to" : "f1", "color" : "default" } + ] + } ] +} \ No newline at end of file diff --git a/axonivy-express-test/resources/testFile/express-test.json b/axonivy-express-test/resources/testFile/express-test.json new file mode 100644 index 0000000..3d9173a --- /dev/null +++ b/axonivy-express-test/resources/testFile/express-test.json @@ -0,0 +1,125 @@ +{ + "version": 1, + "expressWorkflow": [{ + "expressProcess": { + "id": "f5da82a8ed4f437d95b477e9746d9a75", + "processName": "Leave request creation", + "processDescription": "Leave request creation description", + "processType": "AHWF", + "processPermissions": ["#admin"], + "processOwner": "#admin", + "isUseDefaultUI": false, + "processFolder": "e172808e-edd2-4457-8169-f6a48c70bc43", + "readyToExecute": true, + "processCoOwners": ["#admin"], + "isAbleToEdit": true + }, + "expressTaskDefinitions": [{ + "id": "7f0cac61d68346babbebe1e703c578cb", + "processID": "f5da82a8ed4f437d95b477e9746d9a75", + "type": "USER_TASK", + "responsibles": ["#express"], + "subject": "Express Task 1", + "description": "", + "taskPosition": 1, + "untilDays": 1 + } + ], + "expressFormElements": [{ + "id": "b9d14eba257c40b8b3e628d5defa5479", + "processID": "f5da82a8ed4f437d95b477e9746d9a75", + "elementID": "Test label2019-12-27 11:08:16", + "taskPosition": 1, + "label": "Test Label", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + } + ] + }, { + "expressProcess": { + "id": "c3939e7508144f94a03dc71c8054570c", + "processName": "Quality Report", + "processDescription": "Quality Report description", + "processType": "AHWF", + "processPermissions": ["#admin"], + "processOwner": "#admin", + "isUseDefaultUI": false, + "processFolder": "e172808e-edd2-4457-8169-f6a48c70bc43", + "readyToExecute": true, + "processCoOwners": ["#admin"], + "isAbleToEdit": true + }, + "expressTaskDefinitions": [{ + "id": "209f65e4e7344d2ca7d46e0cb39f70b6", + "processID": "c3939e7508144f94a03dc71c8054570c", + "type": "USER_TASK", + "responsibles": ["#express"], + "subject": "Express Task 1", + "description": "", + "taskPosition": 1, + "untilDays": 1 + } + ], + "expressFormElements": [{ + "id": "4a23be65f8bf4dbb97f0d126a146bfb0", + "processID": "c3939e7508144f94a03dc71c8054570c", + "elementID": "Test label2019-12-27 11:08:16", + "taskPosition": 1, + "label": "Test Label", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + } + ] + }, { + "expressProcess": { + "id": "c8166c0a53144adc9eaa1df9c2902f04", + "processName": "Express Test 1", + "processDescription": "Express Test 1", + "processType": "AHWF", + "processPermissions": ["#admin"], + "processOwner": "#admin", + "isUseDefaultUI": false, + "processFolder": "e172808e-edd2-4457-8169-f6a48c70bc43", + "readyToExecute": true, + "processCoOwners": ["#admin"], + "isAbleToEdit": true + }, + "expressTaskDefinitions": [{ + "id": "69cd5fe6730842d394e9025b2b58b56d", + "processID": "c8166c0a53144adc9eaa1df9c2902f04", + "type": "USER_TASK", + "responsibles": ["#admin"], + "subject": "Express Task 1", + "description": "", + "taskPosition": 1, + "untilDays": 1 + } + ], + "expressFormElements": [{ + "id": "269bbeb00a7143c18753e3dc87a273b8", + "processID": "c8166c0a53144adc9eaa1df9c2902f04", + "elementID": "Test label2019-12-27 11:08:16", + "taskPosition": 1, + "label": "Test Label", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/axonivy-express-test/resources/testFile/express-wf-request-resource.json b/axonivy-express-test/resources/testFile/express-wf-request-resource.json new file mode 100644 index 0000000..45fe4e6 --- /dev/null +++ b/axonivy-express-test/resources/testFile/express-wf-request-resource.json @@ -0,0 +1,150 @@ +{ + "version": 1, + "expressWorkflow": [{ + "expressProcess": { + "id": "d623ed55776a4d70a94f38383c9ef9e0", + "processName": "Request new Resources - Express process", + "processDescription": "Express process test ", + "processType": "AHWF", + "processPermissions": ["#demo", "#david"], + "processOwner": "#admin", + "isUseDefaultUI": false, + "processFolder": "f647d845-6f04-48c8-82df-5db1ec7da1a4", + "readyToExecute": true, + "processCoOwners": ["Everybody"], + "isAbleToEdit": true + }, + "expressTaskDefinitions": [{ + "id": "ee61f6e8907b456786e343b6e864e4e0", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "type": "USER_TASK", + "responsibles": ["#demo", "#david"], + "subject": "Promote new resource", + "description": "Request new resource - Task 1", + "taskPosition": 1, + "untilDays": 1 + }, { + "id": "10006b755bb74e6b8636182d064f7150", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "type": "USER_TASK_WITH_EMAIL", + "responsibles": ["Everybody"], + "subject": "Send email to HR", + "description": "Send email to HR for review - Task 2", + "taskPosition": 2, + "untilDays": 1 + }, { + "id": "5fd26253cb0046e7a3141f1cd3ffbca6", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "type": "USER_TASK", + "responsibles": ["#admin"], + "subject": "HR review", + "description": "Wait HR review - Task 3", + "taskPosition": 3, + "untilDays": 2 + }, { + "id": "8fdee4750b864260a5878cbac12e1510", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "type": "APPROVAL", + "responsibles": ["#admin", "Everybody"], + "subject": "Approval Request", + "description": "Approval Request", + "taskPosition": 4, + "untilDays": 1 + } + ], + "expressFormElements": [{ + "id": "ec21202fd937424094f4831dd3caacd8", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Applicant name2020-07-07 09:29:29", + "taskPosition": 1, + "label": "Applicant name", + "required": true, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "e4537a5f9f4c44d9b99a99868808f7dc", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Email2020-07-07 09:29:43", + "taskPosition": 1, + "label": "Email", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "LEFTPANEL", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "dfef3f165b6049bb91a92534ceb80ea7", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Address2020-07-07 09:29:35", + "taskPosition": 1, + "label": "Address", + "required": true, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "RIGHTPANEL", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "904ee50d831a42438254d382af788c0d", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Welcome2020-07-07 09:30:25", + "taskPosition": 2, + "label": "Welcome", + "required": true, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "3d69886d2eb0466fb2ee1888f1d35aed", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Start date2020-07-07 09:30:37", + "taskPosition": 2, + "label": "Start date", + "required": true, + "intSetting": 0, + "elementType": "InputFieldDate", + "optionStrs": [""], + "elementPosition": "LEFTPANEL", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "9b911a68e81246bb91216f557c4e9280", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Comment2020-07-07 09:31:05", + "taskPosition": 3, + "label": "Comment", + "required": true, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "LEFTPANEL", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "840d8b5224e541e8b1afc26e4da04fc0", + "processID": "d623ed55776a4d70a94f38383c9ef9e0", + "elementID": "Approval date2020-07-07 09:31:15", + "taskPosition": 3, + "label": "Approval date", + "required": true, + "intSetting": 0, + "elementType": "InputFieldDate", + "optionStrs": [""], + "elementPosition": "RIGHTPANEL", + "indexInPanel": 0, + "counter": 0 + } + ] + } + ] +} diff --git a/axonivy-express-test/resources/testFile/express-wf-with-disabled-user.json b/axonivy-express-test/resources/testFile/express-wf-with-disabled-user.json new file mode 100644 index 0000000..837fcba --- /dev/null +++ b/axonivy-express-test/resources/testFile/express-wf-with-disabled-user.json @@ -0,0 +1,67 @@ +{ + "version": 1, + "expressWorkflow": [{ + "expressProcess": { + "id": "1aa339cba44148c8a7215b840b0911b4", + "processName": "Test disabled user", + "processDescription": "Test disabled user", + "processType": "AHWF", + "processPermissions": ["#visibility_test_user", "#demo"], + "processOwner": "#admin", + "isUseDefaultUI": false, + "processFolder": "e26cfb17-4fdd-405c-9aed-4dbfdd7eefe1", + "readyToExecute": true, + "processCoOwners": ["#admin", "#visibility_test_user"], + "isAbleToEdit": true + }, + "expressTaskDefinitions": [{ + "id": "3a059f99084d488a99a8992157589d1c", + "processID": "1aa339cba44148c8a7215b840b0911b4", + "type": "USER_TASK", + "responsibles": ["#visibility_test_user", "#demo"], + "subject": "Task 1 - Test disabled user", + "description": "", + "taskPosition": 1, + "untilDays": 1 + }, { + "id": "4491316e17e947be86ce02ed1e91ba9a", + "processID": "1aa339cba44148c8a7215b840b0911b4", + "type": "USER_TASK", + "responsibles": ["#visibility_test_user"], + "subject": "Task 2 - Test disabled user", + "description": "", + "taskPosition": 2, + "untilDays": 1 + } + ], + "expressFormElements": [{ + "id": "b8dba0d41b1a4290b9f125a29fa2f4f5", + "processID": "1aa339cba44148c8a7215b840b0911b4", + "elementID": "Hello2020-06-01 04:45:15", + "taskPosition": 1, + "label": "Hello", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "LEFTPANEL", + "indexInPanel": 0, + "counter": 0 + }, { + "id": "fb18bb0bf7f2400b949f244fea1bbfc0", + "processID": "1aa339cba44148c8a7215b840b0911b4", + "elementID": "Test 22020-06-01 04:45:27", + "taskPosition": 2, + "label": "Test 2", + "required": false, + "intSetting": 0, + "elementType": "InputFieldText", + "optionStrs": [""], + "elementPosition": "HEADER", + "indexInPanel": 0, + "counter": 0 + } + ] + } + ] +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ExpressResponsible.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ExpressResponsible.java new file mode 100644 index 0000000..f62ff22 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ExpressResponsible.java @@ -0,0 +1,34 @@ +package com.axonivy.utils.axonivyexpress.test.common; + +public class ExpressResponsible { + + private String responsibleName; + private boolean isGroup; + + public ExpressResponsible() { + } + + + public ExpressResponsible(String responsibleName, boolean isGroup) { + super(); + this.responsibleName = responsibleName; + this.isGroup = isGroup; + } + + public String getResponsibleName() { + return responsibleName; + } + + public boolean getIsGroup() { + return isGroup; + } + + public void setResponsibleName(String responsibleName) { + this.responsibleName = responsibleName; + } + + public void setIsGroup(boolean isGroup) { + this.isGroup = isGroup; + } + +} \ No newline at end of file diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/FileHelper.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/FileHelper.java new file mode 100644 index 0000000..2eec726 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/FileHelper.java @@ -0,0 +1,8 @@ +package com.axonivy.utils.axonivyexpress.test.common; + +public class FileHelper { + public static String getAbsolutePathToTestFile(String fileName) { + return System.getProperty("user.dir") + "\\resources\\testFile\\" + + fileName; + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ResizeUtils.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ResizeUtils.java new file mode 100644 index 0000000..e992d76 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/ResizeUtils.java @@ -0,0 +1,12 @@ +package com.axonivy.utils.axonivyexpress.test.common; + +import org.openqa.selenium.Dimension; + +import com.codeborne.selenide.WebDriverRunner; + +public class ResizeUtils { + + public static void resizeBrowser(Dimension size) { + WebDriverRunner.getWebDriver().manage().window().setSize(size); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/WaitHelper.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/WaitHelper.java new file mode 100644 index 0000000..67b2f96 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/common/WaitHelper.java @@ -0,0 +1,97 @@ +package com.axonivy.utils.axonivyexpress.test.common; + +import static com.codeborne.selenide.Condition.exist; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; +import static java.time.Duration.ZERO; + +import java.time.Duration; +import java.util.function.Supplier; + +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import com.codeborne.selenide.CollectionCondition; +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.WebDriverRunner; + +public final class WaitHelper { + + protected static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(45); + + public static void waitForNavigation(Runnable navigationAcion) { + String viewState = $( + "input[name='javax.faces.ViewState'][id$='javax.faces.ViewState:1']") + .getAttribute("value"); + navigationAcion.run(); + $$("input[value='" + viewState + "']").shouldHave( + CollectionCondition.sizeLessThanOrEqual(0), DEFAULT_TIMEOUT); + } + + public static void waitForIFrameAvailable(WebDriver driver, String frameId) { + try { + wait(driver) + .until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(frameId)); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } + } + + private static WebDriverWait wait(WebDriver driver) { + return new WebDriverWait(driver, DEFAULT_TIMEOUT); + } + + public static void assertTrueWithWait(Supplier supplier) { + try { + wait(WebDriverRunner.getWebDriver()).until(webDriver -> supplier.get()); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } + } + + public static void waitForPresenceOfElementLocatedInFrame( + String cssSelector) { + try { + wait(WebDriverRunner.getWebDriver()).until(ExpectedConditions + .presenceOfElementLocated(By.cssSelector(cssSelector))); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } + } + + /** + * Some UI are the same before and after AJAX, use this method only in that + * scenario. Ask the team if using this + */ + public static void waitForActionComplete(String cssSelector, + Runnable action) { + ((JavascriptExecutor) WebDriverRunner.getWebDriver()) + .executeScript("$('" + cssSelector.replace("\\", "\\\\") + + "').css('background-color', 'rgb(250, 0, 0)')"); + $(cssSelector).getCssValue("background-color"); + $(cssSelector) + .shouldHave(Condition.cssValue("background-color", "rgb(250, 0, 0)")); + action.run(); + $(cssSelector).shouldNotHave( + Condition.cssValue("background-color", "rgb(250, 0, 0)")); + } + + /** + * Use this instead of {@code Assertions} methods so that Selenide would take + * screenshots if errors. This is a workaround because we cannot + * use @ExtendWith({ScreenShooterExtension.class}) with + * `WebDriverRunner.getWebDriver().quit();` in `@AfterEach` + */ + public static void assertTrue(boolean condition) { + if (!condition) { + $("ASSERTION FAILED, CHECK STACK TRACE from BaseTest.assertTrue") + .shouldBe(exist, ZERO); + } + } +} \ No newline at end of file diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/BasePage.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/BasePage.java new file mode 100644 index 0000000..b94d812 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/BasePage.java @@ -0,0 +1,62 @@ +package com.axonivy.utils.axonivyexpress.test.page; + +import static com.codeborne.selenide.Condition.appear; +import static com.codeborne.selenide.Condition.disappear; +import static com.codeborne.selenide.Selenide.$; + +import java.time.Duration; + +import org.openqa.selenium.By; + +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.SelenideElement; +import com.codeborne.selenide.WebElementCondition; + +public abstract class BasePage { + protected static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); + public static final String CLASS_PROPERTY = "class"; + + protected BasePage() { + waitPageLoaded(); + } + + public void waitPageLoaded() { + $(getLoadedLocator()).shouldBe(appear, DEFAULT_TIMEOUT); + } + + /** + * This abstract method is used to determine identity of a page. + * + * @return A unique CSS selector for the particular page. + */ + protected abstract String getLoadedLocator(); + + protected WebElementCondition getClickableCondition() { + return Condition.and("should be clickable", Condition.visible, Condition.exist); + } + + public SelenideElement findElementById(String selector) { + return $(String.format("[id$='%s']", selector)).shouldBe(appear, + DEFAULT_TIMEOUT); + } + + public void waitForElementClickableThenClick(SelenideElement element) { + element.shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + } + + public void waitForElementClickableThenClick(By by) { + $(by).shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + } + + public void waitForElementDisplayed(By element, boolean expected) { + if (expected) { + $(element).shouldBe(appear, DEFAULT_TIMEOUT); + } else { + $(element).shouldBe(disappear, DEFAULT_TIMEOUT); + } + } + + public boolean isElementPresent(By element) { + return $(element).is(Condition.visible); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressManagementPage.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressManagementPage.java new file mode 100644 index 0000000..430c95a --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressManagementPage.java @@ -0,0 +1,113 @@ +package com.axonivy.utils.axonivyexpress.test.page; + +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; + +import org.openqa.selenium.NoSuchElementException; + +import com.axonivy.utils.axonivyexpress.test.common.FileHelper; +import com.codeborne.selenide.CollectionCondition; +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.SelenideElement; + +public class ExpressManagementPage extends BasePage { + + @Override + protected String getLoadedLocator() { + return "form[id='express-form']"; + } + + public void uploadExpressJsonFile(String fileName) { + openImportDialog(); + selectJSONFile(FileHelper.getAbsolutePathToTestFile(fileName)); + clickOnDeployExpress(); + closeImportDialog(); + } + + private void selectJSONFile(String pathToFile) { + $("*[id$=':express-process-upload_input']").sendKeys(pathToFile); + $$(".ui-fileupload-upload").shouldBe(CollectionCondition.size(1), + DEFAULT_TIMEOUT); + } + + private void openImportDialog() { + $("[id='express-form:import-button']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='import-express-dialog']").shouldBe(Condition.visible, + DEFAULT_TIMEOUT); + } + + private void closeImportDialog() { + $("[id='import-express-dialog']").find("[id='close-import-express']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='import-express-dialog']").shouldBe(Condition.disappear, + DEFAULT_TIMEOUT); + } + + + public void clickOnDeployExpress() { + $("[id='import-express-dialog']").$(".ui-fileupload-upload") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='import-express-dialog']").find("pre.express-import-result") + .shouldBe(Condition.appear, DEFAULT_TIMEOUT); + } + + public boolean hasExpressProcessWithName(String name) { + $("[id='express-form:express-process-table']").shouldBe(Condition.visible, + DEFAULT_TIMEOUT); + try { + SelenideElement processNameElem = $( + "[id='express-form:express-process-table']") + .findAll("tr.ui-widget-content .express-name").asDynamicIterable() + .stream().filter(cell -> cell.getText().contentEquals(name)) + .findFirst().get(); + return processNameElem != null; + } catch (NoSuchElementException e) { + return false; + } + } + + private SelenideElement openMenuByIndex(int index) { + String menuQuery = String.format( + "[id='express-form:express-process-table:%d:action-button']", index); + $("[id='express-form:express-process-table']") + .shouldBe(Condition.visible, DEFAULT_TIMEOUT).find(menuQuery) + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT) + .click(); + + String menuPanelQuery = String.format( + "[id='express-form:express-process-table:%d:action-menu']", index); + return $(menuPanelQuery).shouldBe(Condition.appear, DEFAULT_TIMEOUT); + } + + public void delete(int index) { + openMenuByIndex(index).find("a[id$=':delete']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + + $("[id='express-form:remove-button']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + + $("[id='express-form:remove-button']") + .shouldBe(Condition.disappear, DEFAULT_TIMEOUT); + } + + public int countRows() { + $("[id='express-form:express-process-table']").shouldBe(Condition.visible, + DEFAULT_TIMEOUT); + return $("[id='express-form:express-process-table']") + .findAll("tr.ui-widget-content .express-name").size(); + } + + public WorkflowDefinitionPage edit(int index) { + openMenuByIndex(index).find("a[id$=':edit']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + + return new WorkflowDefinitionPage(); + } + + public WorkflowDefinitionPage create() { + $("[id='express-form:create-button'") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + return new WorkflowDefinitionPage(); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressTaskPage.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressTaskPage.java new file mode 100644 index 0000000..205aa96 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/ExpressTaskPage.java @@ -0,0 +1,44 @@ +package com.axonivy.utils.axonivyexpress.test.page; + +import static com.codeborne.selenide.Condition.appear; +import static com.codeborne.selenide.Selenide.$; + +import org.openqa.selenium.By; + +import com.codeborne.selenide.SelenideElement; + +public class ExpressTaskPage extends BasePage { + @Override + protected String getLoadedLocator() { + return "[id='form:dynaform-fieldset']"; + } + + public SelenideElement findExpressTask() { + return $(".js-task-header-container").shouldBe(appear, DEFAULT_TIMEOUT) + .$("div[id='task-template-title']"); + } + + public void waitForExpressFieldSetDisplay() { + $(".express-fieldset").shouldBe(appear, DEFAULT_TIMEOUT); + } + + public void enterRequiredInputFieldByLabel(String label, String data) { + $(String.format("input[data-p-rmsg*='%s']", label)) + .shouldBe(appear, DEFAULT_TIMEOUT).sendKeys(data); + } + + public void finish() { + $("[id='form:ok-btn']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT) + .click(); + } + + public boolean isDocumentTableVisible() { + return isElementPresent( + By.xpath("//div[contains(@id, 'fileUploadComponent:document-table')]")); + } + + public boolean isDocumentUploadButtonVisible() { + return isElementPresent(By + .xpath("//div[contains(@id, 'fileUploadComponent:document-upload')]")); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/FormDefinitionPage.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/FormDefinitionPage.java new file mode 100644 index 0000000..701edca --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/FormDefinitionPage.java @@ -0,0 +1,306 @@ +package com.axonivy.utils.axonivyexpress.test.page; + +import static com.codeborne.selenide.Condition.appear; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; + +import java.util.Random; + +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Action; +import org.openqa.selenium.interactions.Actions; + +import com.axonivy.utils.axonivyexpress.test.common.WaitHelper; +import com.codeborne.selenide.CollectionCondition; +import com.codeborne.selenide.Condition; +import com.codeborne.selenide.WebDriverRunner; + +public class FormDefinitionPage extends BasePage { + + private static final String LEFT_POSITION = "leftpanel"; + private static final String RIGHT_POSITION = "rightpanel"; + private static final String HEADER_POSITION = "header"; + private static final String FOOTER_POSITION = "footer"; + private static final String[] POSITIONS = { LEFT_POSITION, HEADER_POSITION, + FOOTER_POSITION }; + + @Override + protected String getLoadedLocator() { + return "[id$='defined-task-container']"; + } + + public ExpressManagementPage clickFinish() { + $("[id='finish-button']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT) + .click(); + return new ExpressManagementPage(); + } + + public void createUploadComponent(String label) { + clickOnFormCreationTabIndex(5); + $("input[id$='form:create-tabs:file-upload-label']").sendKeys(label); + $("[id='form:create-tabs:add-upload-file-btn']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("input[id$='form:create-tabs:file-upload-label']") + .shouldBe(Condition.empty); + } + + public void moveAllElementToDragAndDrogPanel() { + int size = $$( + By.xpath("//div[@id='form:available-form-elements_content']/ul/li")) + .size(); + long startIndex = size - 1; + + for (long i = startIndex; i >= 0; i--) { + String panelSelector = "[id='" + + String.format("form:available-form-elements:%d:pnl", i) + "']"; + $(panelSelector).shouldBe(appear, DEFAULT_TIMEOUT); + + if (i == startIndex) { + moveFormElementToPanel(i, RIGHT_POSITION); + } else { + moveFormElementToPanel(i, getRandomPosition()); + } + $$(panelSelector).shouldBe(CollectionCondition.empty); + } + } + + public void countElementPrepareToDrag(int size) { + $$(By.xpath("//div[@id='form:available-form-elements_content']/ul/li")) + .shouldBe(CollectionCondition.size(size), DEFAULT_TIMEOUT); + } + + private void moveFormElementToPanel(long index, String position) { + // TODO Need to be fixed - Workaround for scroll-bar issue + JavascriptExecutor jse = (JavascriptExecutor) WebDriverRunner + .getWebDriver(); + jse.executeScript("window.scrollTo(0, document.body.scrollHeight);"); + + var formElementSelector = String + .format("[id$='form:available-form-elements:%d:pnl_content']", index); + // If elements is FileUpload, move to footer + if (formElementIsFileUpload( + $(formElementSelector).shouldBe(appear, DEFAULT_TIMEOUT))) { + position = FOOTER_POSITION; + } + + var panelSelector = String + .format("[id='form:selected-form-elements-%s-panel']", position); + + Actions builder = new Actions(WebDriverRunner.getWebDriver()); + Action moveProcessSequence = builder + .dragAndDrop($(formElementSelector), $(panelSelector)).build(); + moveProcessSequence.perform(); + } + + private boolean formElementIsFileUpload(WebElement formElement) { + WebElement icon = formElement.findElement(By.tagName("img")); + return icon.getAttribute("src").contains("FileUpload"); + } + + private String getRandomPosition() { + int idx = new Random().nextInt(POSITIONS.length); + return (POSITIONS[idx]); + } + + public void nextStep() { + $("[id='next-button']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT) + .click(); + } + + public void waitForEmailEditorDisplayed() { + $("[id='form:information-email:email-container']").shouldBe(appear, + DEFAULT_TIMEOUT); + } + + public WebElement getPageElement() { + return $(".portal-layout-container").shouldBe(appear, DEFAULT_TIMEOUT); + } + + public void executeWorkflow() { + waitForElementClickableThenClick(By.id("execute-button")); + waitForElementDisplayed(By.id("form:dynaform-fieldset"), true); + } + + public void createTextInputField(String label, int inputFieldTypeIndex, + boolean isRequired) { + waitForElementClickableThenClick( + By.xpath("//*[@id='form:create-tabs']/ul/li[@role='tab'][1]")); + waitForElementDisplayed(By.id("form:create-tabs:create-input-field-tab"), + true); + $(By.id("form:create-tabs:input-field-label")).sendKeys(label); + chooseInputFieldType(inputFieldTypeIndex); + if (isRequired) { + waitForElementClickableThenClick( + By.cssSelector("div[id='form:create-tabs:input-field-required']")); + } + waitForElementClickableThenClick( + By.id("form:create-tabs:add-input-text-btn")); + } + + private void chooseInputFieldType(int inputTypeIndex) { + waitForElementClickableThenClick( + By.id("form:create-tabs:input-field-type_label")); + waitForElementDisplayed(By.id("form:create-tabs:input-field-type_panel"), + true); + waitForElementClickableThenClick(By.id( + String.format("form:create-tabs:input-field-type_%d", inputTypeIndex))); + } + + public void finishWorkflow() { + waitForElementClickableThenClick(By.id("finish-button")); + new ExpressManagementPage(); + } + + public ExpressManagementPage save() { + waitForElementClickableThenClick(By.id("save-button")); + return new ExpressManagementPage(); + } + + public ExpressManagementPage cancel() { + waitForElementClickableThenClick(By.id("cancel-button")); + waitForElementDisplayed(By.id("yes-button"), true); + waitForElementClickableThenClick(By.id("yes-button")); + return new ExpressManagementPage(); + } + + public void createRadioButtonField(String label, int numberOfOption) { + clickOnFormCreationTabIndex(4); + waitForElementDisplayed(By.id("form:create-tabs:one-radio-label"), true); + addRadioOptions(numberOfOption); + $("input[id$='form:create-tabs:one-radio-label']").sendKeys(label); + waitForElementClickableThenClick(By.id("form:create-tabs:add-radio-btn")); + $("input[id$='form:create-tabs:one-radio-label']") + .shouldBe(Condition.empty); + } + + private void addRadioOptions(int numberOfOptions) { + for (int i = 1; i <= numberOfOptions; i++) { + waitForElementClickableThenClick( + By.id("form:create-tabs:one-radio-options:add-radio-option-btn")); + $(By.xpath(String.format( + "//*[@id='form:create-tabs:one-radio-options_data']/tr[%d]/td/input", + i))).sendKeys("Radio " + i); + } + } + + public void createCheckboxField(String label, int numberOfSelection) { + clickOnFormCreationTabIndex(3); + waitForElementDisplayed(By.id("form:create-tabs:many-checkbox-options"), + true); + $("input[id='form:create-tabs:many-checkbox-label']").sendKeys(label); + addCheckboxOptions(numberOfSelection); + waitForElementClickableThenClick( + By.id("form:create-tabs:add-checkbox-btn")); + $("input[id='form:create-tabs:many-checkbox-label']") + .shouldBe(Condition.empty); + } + + private void addCheckboxOptions(int numberOfSelection) { + for (int i = 1; i <= numberOfSelection; i++) { + waitForElementClickableThenClick(By.id( + "form:create-tabs:many-checkbox-options:add-checkbox-option-btn")); + $(By.xpath(String.format( + "//*[@id='form:create-tabs:many-checkbox-options_data']/tr[%d]/td/input", + i))).sendKeys("Option " + i); + } + } + + public void createTextAreaField(String label, boolean isRequired) { + clickOnFormCreationTabIndex(2); + waitForElementDisplayed(By.id("form:create-tabs:create-input-area-tab"), + true); + $(By.id("form:create-tabs:input-area-label")).sendKeys(label); + if (isRequired) { + waitForElementClickableThenClick( + By.cssSelector("div[id='form:create-tabs:input-area-required']")); + } + waitForElementClickableThenClick( + By.id("form:create-tabs:add-text-area-btn")); + $(By.id("form:create-tabs:input-area-label")).shouldBe(Condition.empty); + } + + public void createCheckboxFieldWithDataProvider(String label) { + fillDataForCheckboxProvider(label); + waitForElementClickableThenClick( + By.id("form:create-tabs:add-checkbox-btn")); + $(By.id("form:create-tabs:many-checkbox-label")).shouldBe(Condition.empty); + } + + public void fillDataForCheckboxProvider(String label) { + clickOnFormCreationTabIndex(3); + waitForElementDisplayed(By.id("form:create-tabs:create-many-checkbox-tab"), + true); + waitForElementClickableThenClick( + By.id("form:create-tabs:DataProvider_label")); + waitForElementClickableThenClick( + By.xpath("//*[@data-label='TestDataProviderForPortalExpress']")); + waitForElementDisplayed(By.cssSelector("div[id$='value-checkbox-label']"), + false); + waitForElementDisplayed(By.id("form:create-tabs:many-checkbox-label"), + true); + $(By.id("form:create-tabs:many-checkbox-label")).sendKeys(label); + } + + public int countNumberOfElementsInPreviewDialog() { + waitForElementClickableThenClick(By.id("form:show-preview-button")); + waitForElementDisplayed(By.id("form:preview-dialog"), true); + WebElement previewDialog = findElementById("form:preview-dialog"); + int numberOfInput = previewDialog + .findElements(By.xpath("//table[@id='form:dyna-form']//input")).size(); + int numberOfTextArea = previewDialog + .findElements(By.xpath("//table[@id='form:dyna-form']//textarea")) + .size(); + int numberOfUploadFile = previewDialog + .findElements(By + .xpath("//div[contains(@id,'fileUploadComponent:document-table')]")) + .size(); + return numberOfInput + numberOfTextArea + numberOfUploadFile; + } + + public int countNumberOfSteps() { + return $("div[id='defined-task-container']").findAll("button").size(); + } + + public void inputMailRecipient(String content) { + findElementById("form:information-email:email-recipients") + .sendKeys(content); + } + + public void inputMailSubject(String content) { + findElementById("form:information-email:email-subject").sendKeys(content); + } + + public void inputMailContent(String content) { + ((JavascriptExecutor) WebDriverRunner.getWebDriver()).executeScript( + "document.querySelector(\"input[name='form:information-email:email-content_input'\").value='" + + content + "';"); + } + + public void executeWorkflowAndWaitForUserTaskWithEmailDisplay() { + waitForElementClickableThenClick(By.id("execute-button")); + waitForElementDisplayed(By.id("task-form:task-view:dyna-form-fieldset"), + true); + } + + private void clickOnFormCreationTabIndex(int tabIndex) { + WaitHelper.waitForActionComplete("#form\\:create-tabs ul", + () -> waitForElementClickableThenClick( + By.xpath("//*[@id='form:create-tabs']/ul/li[@role='tab'][" + + tabIndex + "]"))); + } + + public void switchToCheckBoxTab() { + waitForElementDisplayed( + By.xpath("//*[@id='form:create-tabs']/ul/li[@role='tab'][3]"), true); + $(By.xpath("//*[@id='form:create-tabs']/ul/li[@role='tab'][3]")) + .shouldBe(getClickableCondition()).click(); + WaitHelper.assertTrueWithWait(() -> { + var checkboxTab = $( + By.xpath("//*[@id='form:create-tabs']/ul/li[@role='tab'][3]")); + return checkboxTab.getAttribute(CLASS_PROPERTY) + .contains("ui-state-active"); + }); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/WorkflowDefinitionPage.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/WorkflowDefinitionPage.java new file mode 100644 index 0000000..c71e509 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/page/WorkflowDefinitionPage.java @@ -0,0 +1,199 @@ +package com.axonivy.utils.axonivyexpress.test.page; + +import static com.codeborne.selenide.Condition.appear; +import static com.codeborne.selenide.Condition.disappear; +import static com.codeborne.selenide.Selenide.$; + +import java.util.List; + +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebElement; + +import com.axonivy.utils.axonivyexpress.test.common.ExpressResponsible; +import com.axonivy.utils.axonivyexpress.test.common.ResizeUtils; +import com.codeborne.selenide.Condition; + +public class WorkflowDefinitionPage extends BasePage { + + @Override + protected String getLoadedLocator() { + return "fieldset[id='form:process-setting-fieldset']"; + } + + public void changeName(String newName) { + $("[id='form:process-name']").shouldBe(Condition.appear, DEFAULT_TIMEOUT) + .clear(); + $("[id='form:process-name']").shouldBe(Condition.appear, DEFAULT_TIMEOUT) + .sendKeys(newName); + } + + public void save() { + $("[id='form:save']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT) + .click(); + } + + public FormDefinitionPage proceedToFormDefinitionPage() { + save(); + return new FormDefinitionPage(); + } + + public void createDefaultTask(int taskIndex, String taskName, List responsibles) { + if (taskName != null) { + $("[id='" + String.format("form:defined-tasks-list:%d:default-task-name", taskIndex) + "']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).sendKeys(taskName); + } + $("[id='" + String.format("form:defined-tasks-list:%d:default-task-responsible-link", taskIndex) + "']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + addResponsible(responsibles); + } + + private void addResponsible(List responsibles) { + $("[id='choose-responsible-dialog']").shouldBe(appear, DEFAULT_TIMEOUT); + for (ExpressResponsible responsible : responsibles) { + chooseResponsible(responsible.getResponsibleName(), responsible.getIsGroup()); + } + $("[id='assignee-selection-form:save-assignee-button']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='choose-responsible-dialog']").shouldBe(disappear, DEFAULT_TIMEOUT); + } + + private void chooseResponsible(String responsible, boolean isGroup) { + if (isGroup) { + $("label[for='assignee-selection-form:assignee-type:1']").shouldBe(appear, DEFAULT_TIMEOUT).click(); + $("[id='assignee-selection-form:role-selection-component:role-selection_input']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).clear(); + $("[id='assignee-selection-form:role-selection-component:role-selection_input']").sendKeys(responsible); + $("[id='assignee-selection-form:role-selection-component:role-selection_panel']").shouldBe(appear, + DEFAULT_TIMEOUT); + $("span[id='assignee-selection-form:role-selection-component:role-selection_panel'] .gravatar") + .shouldBe(appear, DEFAULT_TIMEOUT).click(); + } else { + $("[id='assignee-selection-form:user-selection-component:user-selection_input']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).clear(); + $("[id='assignee-selection-form:user-selection-component:user-selection_input']").sendKeys(responsible); + $("[id='assignee-selection-form:user-selection-component:user-selection_panel']").shouldBe(appear, + DEFAULT_TIMEOUT); + $("span[id='assignee-selection-form:user-selection-component:user-selection_panel'] .gravatar") + .shouldBe(appear, DEFAULT_TIMEOUT).click(); + } + + $("[id='assignee-selection-form:add-assignee-button']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $(".assignee-name-col").shouldBe(appear, DEFAULT_TIMEOUT); + } + + public void addNewTask(int currentTaskIndex) { + $("[id='" + String.format("form:defined-tasks-list:%d:add-step-button", currentTaskIndex) + "']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='" + String.format("form:defined-tasks-list:%d:process-flow-field", currentTaskIndex + 1) + "']") + .shouldBe(appear, DEFAULT_TIMEOUT); + } + + public void clickSave() { + $("[id='form:save']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + } + + public FormDefinitionPage goToFormDefinition() { + goToFormDefinitionDefaultResolution(); + ResizeUtils.resizeBrowser(new Dimension(2560, 1440)); + return new FormDefinitionPage(); + } + + public FormDefinitionPage goToFormDefinitionDefaultResolution() { + clickSave(); + return new FormDefinitionPage(); + } + + public WebElement getDefineTaskStep(int stepIndex) { + String defineTaskStepId = String.format(":defined-tasks-list:%s:process-flow-field", stepIndex); + return $("[id$='" + defineTaskStepId + "']").shouldBe(appear, DEFAULT_TIMEOUT); + } + + public void fillProcessProperties(boolean isAdhocWF, boolean isCreateOwn, String processName, + String processDescription) { + if (isAdhocWF) { + $("div[id='form:process-type']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("span[id='form:process-type-group']").$(".switch-active").shouldBe(Condition.text("One time"), DEFAULT_TIMEOUT); + } + + if (!isCreateOwn) { + $("div[id='form:user-interface-type']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + agreeToDeleteAllDefineTasks(); + + } + $("[id='form:process-name']").shouldBe(appear, DEFAULT_TIMEOUT).sendKeys(processName); + $("[id='form:process-description']").shouldBe(appear, DEFAULT_TIMEOUT).sendKeys(processDescription); + } + + private void agreeToDeleteAllDefineTasks() { + $("[id='delete-all-defined-tasks-warning']").shouldBe(appear, DEFAULT_TIMEOUT); + $("[id='delete-all-defined-tasks-warning-ok']").shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='delete-all-defined-tasks-warning']").shouldBe(disappear, DEFAULT_TIMEOUT); + } + + public void createTask(int taskIndex, int typeIndex, String taskName, String taskDescription, + List responsibles) { + final String TASK_NAME_FORMAT = "input[id$='%d:task-name']"; + final int INFORMATION_EMAIL_INDEX = 2; + + chooseTaskType(taskIndex, typeIndex); + if (typeIndex != INFORMATION_EMAIL_INDEX) { + $("[id='" + String.format("form:defined-tasks-list:%d:task-responsible-link", taskIndex) + "']") + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $("[id='choose-responsible-dialog']").shouldBe(appear, DEFAULT_TIMEOUT); + addResponsible(responsibles); + + $(String.format(TASK_NAME_FORMAT, taskIndex)).shouldBe(appear, DEFAULT_TIMEOUT).sendKeys(taskName); + $(String.format("input[id$='%d:task-description']", taskIndex)).shouldBe(appear, DEFAULT_TIMEOUT) + .sendKeys(taskDescription); + } + } + + private void chooseTaskType(int taskIndex, int typeIndex) { + if (typeIndex == 0) { + // If the selected task type is already task type? ignore click on the drop-down + return; + } + + final String TASK_TYPE_FORMAT = "li[id$=':%d:task-type_%d']"; + final String TASK_TYPE_LABEL_FORMAT = "[id$=':%d:task-type_label']"; + + $(String.format(TASK_TYPE_LABEL_FORMAT, taskIndex)).shouldBe(appear, DEFAULT_TIMEOUT) + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + $(String.format("[id$=':%d:task-type_panel']", taskIndex)).shouldBe(appear, DEFAULT_TIMEOUT); + $(String.format(TASK_TYPE_FORMAT, taskIndex, typeIndex)).shouldBe(appear, DEFAULT_TIMEOUT) + .shouldBe(getClickableCondition(), DEFAULT_TIMEOUT).click(); + } + + public void waitUntilExpressProcessDisplay() { + $("[id='form:process-setting-fieldset']").shouldBe(appear, DEFAULT_TIMEOUT); + } + + public String getProcessName() { + return findElementById("form:process-name").getAttribute("value"); + } + + public String getProcessOwnerNames() { + return findElementById("form:process-owner-link").getText(); + } + + public String getAbleToStartNames() { + return getResponsiblesOfTask(0); + } + + public String getResponsiblesOfTask(int taskIndex) { + return findElementById(String.format("form:defined-tasks-list:%d:task-responsible-link", taskIndex)).getText(); + } + + public void executeDirectly() { + waitForElementClickableThenClick($("[id$='form:save']")); + } + + public ExpressManagementPage cancelWorkflowDefinition() { + waitForElementClickableThenClick($("[id='form:cancel-workflow-button']")); + return new ExpressManagementPage(); + } + + public void fillProcessOwners(List responsibles) { + waitForElementClickableThenClick($("[id='form:process-owner-link']")); + addResponsible(responsibles); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/BaseTest.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/BaseTest.java new file mode 100644 index 0000000..874cb16 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/BaseTest.java @@ -0,0 +1,28 @@ +package com.axonivy.utils.axonivyexpress.test.webtest; + +import static com.codeborne.selenide.Selenide.open; + +import org.junit.jupiter.api.AfterEach; + +import com.axonivy.ivy.webtest.engine.EngineUrl; +import com.axonivy.ivy.webtest.engine.WebAppFixture; +import com.axonivy.utils.axonivyexpress.test.page.ExpressManagementPage; + +public abstract class BaseTest { + + @AfterEach + public void clean() { + open(EngineUrl.createProcessUrl( + "/axonivy-express-test/1943EF9CC7A1B392/cleanData.ivp")); + } + + protected ExpressManagementPage navigateToExpressManagementPage() { + open(EngineUrl.createProcessUrl( + "/axonivy-express/17326FC2F133FBEA/expressManagement.ivp")); + return new ExpressManagementPage(); + } + + protected void loginAsDeveloper(WebAppFixture fixture) { + fixture.login("Developer", "Developer"); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressManagementTest.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressManagementTest.java new file mode 100644 index 0000000..12cee48 --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressManagementTest.java @@ -0,0 +1,46 @@ +package com.axonivy.utils.axonivyexpress.test.webtest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.axonivy.ivy.webtest.IvyWebTest; +import com.axonivy.ivy.webtest.engine.WebAppFixture; +import com.axonivy.utils.axonivyexpress.test.page.ExpressManagementPage; +import com.axonivy.utils.axonivyexpress.test.page.WorkflowDefinitionPage; + +@IvyWebTest +public class ExpressManagementTest extends BaseTest { + + @BeforeEach + public void setup(WebAppFixture fixture) { + fixture.login("express", "express"); + navigateToExpressManagementPage(); + ExpressManagementPage page = new ExpressManagementPage(); + page.uploadExpressJsonFile("express-test.json"); + } + + @Test + public void testImportProcess() { + ExpressManagementPage page = new ExpressManagementPage(); + assertTrue(page.hasExpressProcessWithName("Express Test 1")); + } + + @Test + public void testDeleteProcess() { + ExpressManagementPage page = new ExpressManagementPage(); + page.delete(0); + assertEquals(2, page.countRows()); + } + + @Test + public void testEditProcess() { + ExpressManagementPage page = new ExpressManagementPage(); + WorkflowDefinitionPage definitionPage = page.edit(1); + definitionPage.changeName("Test Name"); + page = definitionPage.proceedToFormDefinitionPage().clickFinish(); + assertTrue(page.hasExpressProcessWithName("Test Name")); + } +} diff --git a/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressProcessTest.java b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressProcessTest.java new file mode 100644 index 0000000..a625a8d --- /dev/null +++ b/axonivy-express-test/src_test/com/axonivy/utils/axonivyexpress/test/webtest/ExpressProcessTest.java @@ -0,0 +1,101 @@ +package com.axonivy.utils.axonivyexpress.test.webtest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.axonivy.ivy.webtest.IvyWebTest; +import com.axonivy.ivy.webtest.engine.WebAppFixture; +import com.axonivy.utils.axonivyexpress.test.common.ExpressResponsible; +import com.axonivy.utils.axonivyexpress.test.page.ExpressManagementPage; +import com.axonivy.utils.axonivyexpress.test.page.ExpressTaskPage; +import com.axonivy.utils.axonivyexpress.test.page.FormDefinitionPage; +import com.axonivy.utils.axonivyexpress.test.page.WorkflowDefinitionPage; + +@IvyWebTest +public class ExpressProcessTest extends BaseTest { + private static final int USER_TASK_INDEX = 0; + private static final int APPROVAL_INDEX = 3; + + private static final int INPUT_TEXT_TYPE_INDEX = 0; + private static final int INPUT_NUMBER_TYPE_INDEX = 1; + + @BeforeEach + public void setup(WebAppFixture fixture) { + fixture.login("express", "express"); + navigateToExpressManagementPage(); + } + + @Test + public void testOneTimeWorkflow() { + ExpressManagementPage page = new ExpressManagementPage(); + WorkflowDefinitionPage expressProcessPage = page.create(); + expressProcessPage.fillProcessProperties(true, true, "Test approval", + "Test description"); + FormDefinitionPage formDefinition = configureExpressProcessWhenMultiApproval( + expressProcessPage); + formDefinition.executeWorkflow(); + executeExpressProcessWhenMultiApproval(); + page = navigateToExpressManagementPage(); + assertEquals(0, page.countRows()); + } + + @Test + public void testCreateThenExecuteWorkflow() { + ExpressManagementPage page = new ExpressManagementPage(); + WorkflowDefinitionPage expressProcessPage = page.create(); + expressProcessPage.fillProcessProperties(false, true, "Test approval", + "Test description"); + FormDefinitionPage formDefinition = configureExpressProcessWhenMultiApproval( + expressProcessPage); + formDefinition.finishWorkflow(); + page = new ExpressManagementPage(); + assertTrue(page.hasExpressProcessWithName("Test approval")); + } + + public ExpressResponsible setExpressResponsible(String userName, + boolean isGroup) { + ExpressResponsible user = new ExpressResponsible(); + user.setResponsibleName(userName); + user.setIsGroup(isGroup); + return user; + } + + private FormDefinitionPage configureExpressProcessWhenMultiApproval( + WorkflowDefinitionPage expressProcessPage) { + ExpressResponsible responsible1 = setExpressResponsible( + "express", false); + ExpressResponsible responsible2 = setExpressResponsible( + "testUser", false); + + expressProcessPage.createTask(0, USER_TASK_INDEX, "Task 1", + "Task 1 description", Arrays.asList(responsible1, responsible2)); + + expressProcessPage.addNewTask(0); + expressProcessPage.createTask(1, APPROVAL_INDEX, "Task 2", + "Task 2 description", Arrays.asList(responsible2)); + + expressProcessPage.addNewTask(1); + expressProcessPage.createTask(2, APPROVAL_INDEX, "Task 3", + "Task 3 description", Arrays.asList(responsible1, responsible2)); + FormDefinitionPage formDefinition = expressProcessPage + .goToFormDefinition(); + formDefinition.createTextInputField("Input Text 1", INPUT_TEXT_TYPE_INDEX, + false); + formDefinition.createTextInputField("Input Number 2", + INPUT_NUMBER_TYPE_INDEX, false); + formDefinition.countElementPrepareToDrag(2); + formDefinition.moveAllElementToDragAndDrogPanel(); + formDefinition.countElementPrepareToDrag(0); + return formDefinition; + } + + private void executeExpressProcessWhenMultiApproval() { + ExpressTaskPage expressTaskPage = new ExpressTaskPage(); + expressTaskPage.finish(); + } +}