From 1684b53a72f942234d6fb798b8f99fbf254894b9 Mon Sep 17 00:00:00 2001 From: andresmr Date: Fri, 24 Jan 2025 07:26:25 +0100 Subject: [PATCH 1/4] ci: [ANDROAPP-6713] add mocked request for reserved values --- .../java/org/dhis2/OrientationHelper.kt | 13 +++ .../mockwebserver/MockWebServerRobot.kt | 11 +- .../usescases/searchte/robot/FilterRobot.kt | 63 +++++++++-- .../teidashboard/TeiDashboardTest.kt | 106 +++++++++++++++++- ...cked_entity_attribute_reserved_values.json | 10 ++ ...ked_entity_attribute_reserved_values.json} | 0 6 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 app/src/androidTest/java/org/dhis2/OrientationHelper.kt create mode 100644 app/src/dhisUITesting/assets/mocks/teidashboard/tb_identifier_tracked_entity_attribute_reserved_values.json rename app/src/dhisUITesting/assets/mocks/teidashboard/{tracked_entity_attribute_reserved_values.json => unique_id_tracked_entity_attribute_reserved_values.json} (100%) diff --git a/app/src/androidTest/java/org/dhis2/OrientationHelper.kt b/app/src/androidTest/java/org/dhis2/OrientationHelper.kt new file mode 100644 index 0000000000..d00a04ce8c --- /dev/null +++ b/app/src/androidTest/java/org/dhis2/OrientationHelper.kt @@ -0,0 +1,13 @@ +package org.dhis2 + +import android.content.Context +import android.content.res.Configuration +import androidx.test.platform.app.InstrumentationRegistry + +object OrientationHelper { + fun isLandscape(): Boolean { + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + val orientation = context.resources.configuration.orientation + return orientation == Configuration.ORIENTATION_LANDSCAPE + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/dhis2/common/mockwebserver/MockWebServerRobot.kt b/app/src/androidTest/java/org/dhis2/common/mockwebserver/MockWebServerRobot.kt index 641cfed1b4..2332b70e45 100644 --- a/app/src/androidTest/java/org/dhis2/common/mockwebserver/MockWebServerRobot.kt +++ b/app/src/androidTest/java/org/dhis2/common/mockwebserver/MockWebServerRobot.kt @@ -17,10 +17,15 @@ class MockWebServerRobot(private val dhis2MockServer: Dhis2MockServer) { } companion object { - const val API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH = + const val API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH = "/api/trackedEntityAttributes/lZGmxYbs97q/generateAndReserve?.*" - const val API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE = - "mocks/teidashboard/tracked_entity_attribute_reserved_values.json" + const val API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE = + "mocks/teidashboard/unique_id_tracked_entity_attribute_reserved_values.json" + const val API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH = + "/api/trackedEntityAttributes/xs8A6tQJY0s/generateAndReserve?.*" + const val API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE = + "mocks/teidashboard/tb_identifier_tracked_entity_attribute_reserved_values.json" + const val API_TRACKED_ENTITY_PATH = "/api/tracker/trackedEntities?.*" const val API_TRACKED_ENTITY_EMPTY_RESPONSE = "mocks/teilist/tracked_entity_empty_response.json" diff --git a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/FilterRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/FilterRobot.kt index 1b88b3671e..7d610821d8 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/FilterRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/searchte/robot/FilterRobot.kt @@ -1,6 +1,5 @@ package org.dhis2.usescases.searchte.robot -import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onAllNodesWithText @@ -44,8 +43,9 @@ class FilterRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { fun clickOnFilterBy(filter: String) { onView(withId(R.id.filterRecyclerLayout)) - .perform(actionOnItem(hasDescendant(withText(filter)), click()) - ) + .perform( + actionOnItem(hasDescendant(withText(filter)), click()) + ) } fun clickOnFilterActiveOption() { @@ -64,12 +64,22 @@ class FilterRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { fun closeFilterRowAtField(filter: String) { onView(withId(R.id.filterRecyclerLayout)) - .perform(actionOnItem(hasDescendant(withText(filter)), clickChildViewWithId(R.id.filterArrow))) + .perform( + actionOnItem( + hasDescendant(withText(filter)), + clickChildViewWithId(R.id.filterArrow) + ) + ) } fun clickOnSortByField(fieldFilter: String) { onView(withId(R.id.filterRecyclerLayout)) - .perform(actionOnItem(hasDescendant(withText(fieldFilter)), clickChildViewWithId(R.id.sortingIcon))) + .perform( + actionOnItem( + hasDescendant(withText(fieldFilter)), + clickChildViewWithId(R.id.sortingIcon) + ) + ) } fun typeOrgUnitField(orgUnit: String) { @@ -88,13 +98,26 @@ class FilterRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { } fun chooseDate(year: Int, monthOfYear: Int, dayOfMonth: Int) { - onView(withId(R.id.datePicker)).perform(PickerActions.setDate(year, monthOfYear, dayOfMonth)) + onView(withId(R.id.datePicker)).perform( + PickerActions.setDate( + year, + monthOfYear, + dayOfMonth + ) + ) onView(withId(R.id.acceptBtn)).perform(click()) } fun checkTEIWithOrgUnit(orgUnit: String) { onView(withId(R.id.scrollView)) - .check(matches(allElementsWithHolderTypeHave(SearchTEViewHolder::class.java,hasDescendant(withText(orgUnit))))) + .check( + matches( + allElementsWithHolderTypeHave( + SearchTEViewHolder::class.java, + hasDescendant(withText(orgUnit)) + ) + ) + ) } fun checkTEINotSync() { @@ -103,16 +126,34 @@ class FilterRobot(val composeTestRule: ComposeTestRule) : BaseRobot() { fun checkFilterCounter(filterCount: String) { waitForView(withId(R.id.filterCounter)) - onView(allOf(withId(R.id.filterCounter), isDisplayed(), withParent(withId(R.id.mainToolbar)))) + onView( + allOf( + withId(R.id.filterCounter), + isDisplayed(), + withParent(withId(R.id.mainToolbar)) + ) + ) .check(matches(withChild(withText(filterCount)))) } fun checkCountAtFilter(filter: String, count: String) { onView(withId(R.id.filterRecyclerLayout)) - .check(matches(hasItem(allOf(hasDescendant(withText(filter)), hasDescendant(withText(count)))))) + .check( + matches( + hasItem( + allOf( + hasDescendant(withText(filter)), + hasDescendant(withText(count)) + ) + ) + ) + ) } fun checkTeiAreCompleted() { - composeTestRule.onAllNodesWithText("Enrollment completed", true).assertCountEquals(4) + val nodes = composeTestRule.onAllNodesWithText("Enrollment completed", true) + assert(nodes.fetchSemanticsNodes().size >= 3) { + "Expected at least 3 nodes, but found ${nodes.fetchSemanticsNodes().size}" + } } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt index 3eb5428022..4c37b169eb 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/teidashboard/TeiDashboardTest.kt @@ -4,9 +4,12 @@ import android.annotation.SuppressLint import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import dhis2.org.analytics.charts.data.ChartType +import org.dhis2.OrientationHelper import org.dhis2.R -import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH -import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE +import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH +import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE +import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH +import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE import org.dhis2.lazyActivityScenarioRule import org.dhis2.usescases.BaseTest import org.dhis2.usescases.orgunitselector.orgUnitSelectorRobot @@ -21,6 +24,7 @@ import org.dhis2.usescases.teidashboard.robot.indicatorsRobot import org.dhis2.usescases.teidashboard.robot.noteRobot import org.dhis2.usescases.teidashboard.robot.teiDashboardRobot import org.hisp.dhis.android.core.mockwebserver.ResponseController +import org.junit.Assume import org.junit.Ignore import org.junit.Rule import org.junit.Test @@ -47,6 +51,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldSuccessfullyCreateANoteWhenClickCreateNote() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + setupCredentials() prepareTeiCompletedProgrammeAndLaunchActivity(rule) @@ -65,6 +76,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldNotCreateANoteWhenClickClear() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + prepareTeiCompletedProgrammeAndLaunchActivity(rule) teiDashboardRobot(composeTestRule) { @@ -82,6 +100,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldOpenNotesDetailsWhenClickOnNote() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + prepareTeiWithExistingNoteAndLaunchActivity(rule) teiDashboardRobot(composeTestRule) { @@ -111,6 +136,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldShowInactiveProgramWhenClickDeactivate() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + prepareTeiOpenedProgrammeAndLaunchActivity(rule) teiDashboardRobot(composeTestRule) { @@ -126,6 +158,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldCompleteProgramWhenClickComplete() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + prepareTeiOpenedForCompleteProgrammeAndLaunchActivity(rule) teiDashboardRobot(composeTestRule) { @@ -153,6 +192,19 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldMakeAReferral() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + prepareTeiOpenedForReferralProgrammeAndLaunchActivity(rule) teiDashboardRobot(composeTestRule) { @@ -168,6 +220,19 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldSuccessfullyScheduleAnEvent() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + val currentDate = LocalDate.now() val formatter = DateTimeFormatter.ofPattern("ddMMyyyy") val formattedCurrentDate = currentDate.format(formatter) @@ -188,6 +253,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldOpenEventAndSaveSuccessfully() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + setupCredentials() prepareTeiOpenedProgrammeAndLaunchActivity(rule) @@ -219,16 +291,20 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldShowTEIDetailsWhenClickOnSeeDetails() { + //This test is only valid for portrait mode given that landscape is showing details + //So the test is skipped for landscape + Assume.assumeFalse(OrientationHelper.isLandscape()) + //Adding mock response for API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH //The TEI does not have value assigned for unique ID, as it is autogenerated, it tries to //generate one but there are no reserved values in the database so it performs a new request. mockWebServerRobot.addResponse( method = ResponseController.GET, - path = API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, - sdkResource = API_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, responseCode = 200, + ) - ) prepareTeiCompletedProgrammeAndLaunchActivity(rule) val enrollmentFullDetails = createExpectedEnrollmentInformation() @@ -282,6 +358,13 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldEnrollToOtherProgramWhenClickOnProgramEnrollments() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + val womanProgram = "MNCH / PNC (Adult Woman)" val personAttribute = context.getString(R.string.enrollment_single_section_label).replace("%s", "Person") @@ -330,6 +413,19 @@ class TeiDashboardTest : BaseTest() { @Test fun shouldShowAnalytics() { + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_TB_IDENTIFIER_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + mockWebServerRobot.addResponse( + method = ResponseController.GET, + path = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_PATH, + sdkResource = API_UNIQUE_ID_TRACKED_ENTITY_ATTRIBUTES_RESERVED_VALUES_RESPONSE, + responseCode = 200, + ) + val chartName = "Daily-TB smear microscopy number of specimen" setupCredentials() prepareTeiForAnalyticsAndLaunchActivity(rule) diff --git a/app/src/dhisUITesting/assets/mocks/teidashboard/tb_identifier_tracked_entity_attribute_reserved_values.json b/app/src/dhisUITesting/assets/mocks/teidashboard/tb_identifier_tracked_entity_attribute_reserved_values.json new file mode 100644 index 0000000000..d1873ae491 --- /dev/null +++ b/app/src/dhisUITesting/assets/mocks/teidashboard/tb_identifier_tracked_entity_attribute_reserved_values.json @@ -0,0 +1,10 @@ +[ + { + "ownerObject": "TRACKEDENTITYATTRIBUTE", + "ownerUid": "xs8A6tQJY0s", + "key": "RANDOM(###)", + "value": "046", + "created": "2018-04-26T14:54:53.344", + "expiryDate": "2100-06-25T14:54:53.344" + } +] \ No newline at end of file diff --git a/app/src/dhisUITesting/assets/mocks/teidashboard/tracked_entity_attribute_reserved_values.json b/app/src/dhisUITesting/assets/mocks/teidashboard/unique_id_tracked_entity_attribute_reserved_values.json similarity index 100% rename from app/src/dhisUITesting/assets/mocks/teidashboard/tracked_entity_attribute_reserved_values.json rename to app/src/dhisUITesting/assets/mocks/teidashboard/unique_id_tracked_entity_attribute_reserved_values.json From af12b9605cc10bf818e13b9b2006e354e8b5a56b Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 27 Jan 2025 12:39:57 +0100 Subject: [PATCH 2/4] ci: [ANDROAPP-6713] Add test in landscape in Jenkinsfile --- Jenkinsfile | 22 +++++++ scripts/browserstackJenkinsLandscape.sh | 81 +++++++++++++++++++++++++ scripts/config_jenkins.init | 4 +- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 scripts/browserstackJenkinsLandscape.sh diff --git a/Jenkinsfile b/Jenkinsfile index 969b719aff..a4494940b3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -110,6 +110,28 @@ pipeline { } } } + + + stage('Run UI Tests in Landscape') { + environment { + BROWSERSTACK = credentials('android-browserstack') + app_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/dhisUITesting -iname "*.apk"') + test_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/androidTest -iname "*.apk"') + app_apk_path = "${env.WORKSPACE}/${app_apk}" + test_apk_path = "${env.WORKSPACE}/${test_apk}" + buildTag = "${env.GIT_BRANCH}" + } + steps { + dir("${env.WORKSPACE}/scripts"){ + script { + echo 'Browserstack deployment and running tests' + sh 'chmod +x browserstackJenkinsLandscape.sh' + sh './browserstackJenkinsLandscape.sh' + } + } + } + } + } } stage('JaCoCo report') { diff --git a/scripts/browserstackJenkinsLandscape.sh b/scripts/browserstackJenkinsLandscape.sh new file mode 100644 index 0000000000..1b7eb3acf1 --- /dev/null +++ b/scripts/browserstackJenkinsLandscape.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -ex +source config_jenkins.init + +# Upload app and testing apk +echo "Uploading app APK to Browserstack..." +upload_app_response="$(curl -u $BROWSERSTACK_USR:$BROWSERSTACK_PSW -X POST https://api-cloud.browserstack.com/app-automate/upload -F file=@$app_apk_path)" +app_url=$(echo "$upload_app_response" | jq .app_url) + +echo "Uploading test APK to Browserstack..." +upload_test_response="$(curl -u $BROWSERSTACK_USR:$BROWSERSTACK_PSW -X POST https://api-cloud.browserstack.com/app-automate/espresso/test-suite -F file=@$test_apk_path)" +test_url=$(echo "$upload_test_response" | jq .test_url) + +# Prepare json and run tests +echo "Starting execution of espresso tests..." +shards=$(jq -n \ + --arg number_of_shards "$browserstack_number_of_parallel_executions" \ + '{numberOfShards: $number_of_shards}') + +json=$(jq -n \ + --argjson app_url $app_url \ + --argjson test_url $test_url \ + --argjson devices ["$browserstack_device_list_landscape"] \ + --argjson class ["$browserstack_class"] \ + --arg logs "$browserstack_device_logs" \ + --arg video "$browserstack_video" \ + --arg loc "$browserstack_local" \ + --arg locId "$browserstack_local_identifier" \ + --arg gpsLocation "$browserstack_gps_location" \ + --arg language "$browserstack_language" \ + --arg locale "$browserstack_locale" \ + --arg deviceLogs "$browserstack_deviceLogs" \ + --arg allowDeviceMockServer "$browserstack_allowDeviceMockServer" \ + --argjson shards "$shards" \ + --arg buildTag "$buildTag" \ + --arg deviceOrientation "$browserstack_deviceOrientation" \ + '{devices: $devices, app: $app_url, testSuite: $test_url, class: $class, logs: $logs, video: $video, local: $loc, localIdentifier: $locId, gpsLocation: $gpsLocation, language: $language, locale: $locale, deviceLogs: $deviceLogs, allowDeviceMockServer: $allowDeviceMockServer, shards: $shards, buildTag: $buildTag, deviceOrientation: $deviceOrientation}') + +test_execution_response="$(curl -X POST https://api-cloud.browserstack.com/app-automate/espresso/v2/build -d \ "$json" -H "Content-Type: application/json" -u "$BROWSERSTACK_USR:$BROWSERSTACK_PSW")" + +# Get build +build_id=$(echo "$test_execution_response" | jq -r .build_id) +echo "build id running: $build_id" + +# Monitor build status +build_status="running" +sleep $build_time_average +echo "Monitoring build status started...." + +while [[ $build_status = "running" ]]; +do + # Get build status + build_status_response="$(curl -u "$BROWSERSTACK_USR:$BROWSERSTACK_PSW" -X GET "https://api-cloud.browserstack.com/app-automate/espresso/builds/$build_id")" + build_status=$(echo "$build_status_response" | jq -r .status) + echo "current build status: $build_status" + + # Sleep until next poll + sleep $polling_interval +done + +# Export test reports to bitrise +test_reports_url="https://app-automate.browserstack.com/dashboard/v2/builds/$build_id" + +# weird behavior from Browserstack api, you can have "done" status with failed tests +# "devices" only show one device result which is inconsistance +# then "device_status" is checked +if [[ $build_status = "failed" || $build_status = "error" ]]; +then + echo "Browserstack build failed, please check the execution of your tests $test_reports_url" + exit 1 +else + device_status=$(echo "$build_status_response" | jq -r '.device_statuses.error | to_entries[].value') + if [[ $device_status = "Failed" ]]; # for this Failed Browserstack used bloq mayus + then + echo "Browserstack build failed, please check the execution of your tests $test_reports_url" + exit 1 + else + echo "Browserstack build passed, please check the execution of your tests $test_reports_url" + exit 0 + fi +fi diff --git a/scripts/config_jenkins.init b/scripts/config_jenkins.init index 90a8fc3e45..2e017a435e 100644 --- a/scripts/config_jenkins.init +++ b/scripts/config_jenkins.init @@ -1,6 +1,7 @@ build_time_average=660 polling_interval=10 browserstack_device_list="\"Samsung Galaxy S10-9.0\"" +browserstack_device_list_landscape="\"Samsung Galaxy Tab S6-9.0\"" browserstack_video=true browserstack_local=false browserstack_gps_location="40.730610,-73.935242" @@ -10,4 +11,5 @@ browserstack_deviceLogs=true browserstack_number_of_parallel_executions=2 bitrise_max_parallel_builds=3 browserstack_class="\"org.dhis2.usescases.UseCaseTestsSuite\",\"org.dhis2.usescases.FlowTestsSuite\"" -browserstack_allowDeviceMockServer=true \ No newline at end of file +browserstack_allowDeviceMockServer=true +browserstack_deviceOrientation="landscape" \ No newline at end of file From c0149074a12c18adc91726b8c1d143e9d1c23169 Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 27 Jan 2025 13:47:11 +0100 Subject: [PATCH 3/4] ci: [ANDROAPP-6713] run landscape tests on for scheduled jobs --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a4494940b3..4c7cd0f52b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -111,7 +111,7 @@ pipeline { } } - + if (JOB_NAME.startsWith('android-multibranch-PUSH')) { stage('Run UI Tests in Landscape') { environment { BROWSERSTACK = credentials('android-browserstack') @@ -131,7 +131,7 @@ pipeline { } } } - + } } } stage('JaCoCo report') { From 85b6196efab7cdec531185c766714414a44af6ba Mon Sep 17 00:00:00 2001 From: andresmr Date: Mon, 27 Jan 2025 15:48:05 +0100 Subject: [PATCH 4/4] ci: [ANDROAPP-6713] run landscape tests on for scheduled jobs --- Jenkinsfile | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4c7cd0f52b..92d5cadb42 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -110,24 +110,26 @@ pipeline { } } } - - if (JOB_NAME.startsWith('android-multibranch-PUSH')) { - stage('Run UI Tests in Landscape') { - environment { - BROWSERSTACK = credentials('android-browserstack') - app_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/dhisUITesting -iname "*.apk"') - test_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/androidTest -iname "*.apk"') - app_apk_path = "${env.WORKSPACE}/${app_apk}" - test_apk_path = "${env.WORKSPACE}/${test_apk}" - buildTag = "${env.GIT_BRANCH}" + stage('Run UI Tests in Landscape') { + when { + expression { + return JOB_NAME.startsWith('android-multibranch-PUSH') } - steps { - dir("${env.WORKSPACE}/scripts"){ - script { - echo 'Browserstack deployment and running tests' - sh 'chmod +x browserstackJenkinsLandscape.sh' - sh './browserstackJenkinsLandscape.sh' - } + } + environment { + BROWSERSTACK = credentials('android-browserstack') + app_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/dhisUITesting -iname "*.apk"') + test_apk = sh(returnStdout: true, script: 'find app/build/outputs/apk/androidTest -iname "*.apk"') + app_apk_path = "${env.WORKSPACE}/${app_apk}" + test_apk_path = "${env.WORKSPACE}/${test_apk}" + buildTag = "${env.GIT_BRANCH}" + } + steps { + dir("${env.WORKSPACE}/scripts"){ + script { + echo 'Browserstack deployment and running tests' + sh 'chmod +x browserstackJenkinsLandscape.sh' + sh './browserstackJenkinsLandscape.sh' } } }