Apdex Automation Tests

Automated test execution for creating telemetry data and calculating the Apdex scores

Configuration

This documentation will guide you on how to setup and configure automation to run performance tests for your CHT Applications.

Prerequisites

Before continuing with the steps below, ensure:

  1. You have a cht instance deployed and running either locally or globally.

  2. You have some pre-existing users and data already loaded on the app. Use the test-data-generator tool to achieve this.

  3. If you already have the CHT Android app installed, set the appPath value (in the capabilities section of the settings file) to an empty string.

  4. If you do not have the CHT Android app installed on your mobile device, download the preferred apk version and then set the appPath value to the absolute path of the apk file.

  5. Finally, ensure you have done the following installations on your machine:

  • Install NodeJS and Java JDK then ensure JAVA_HOME path is correctly set up.
    export JAVA_HOME=$(/usr/libexec/java_home)
    
  • Install and Set-up Android studio and the adb tool to enable you run adb commands.
    • Add the Android SDK directory to your system’s ANDROID_HOME environment variable.
     export ANDROID_HOME="/Users/yourpath/Library/Android/sdk/"
     export PATH=$ANDROID_HOME/platform-tools:$PATH
     export PATH=$ANDROID_HOME/tools:$PATH
    
    • To set up an Android Virtual Device (AVD), open Android Studio, click on More Actions > Virtual Device Manager, and then proceed with the virtual device creation by selecting the hardware and system image.
  • Install appium and appium doctor.
npm install -g appium@next
npm install -g appium-doctor
  • Install appium driver - appium driver install uiautomator2

Steps (running the tests)

  1. Enable the developer mode in your phone and enable the USB Debugger mode.

    • Ensure your device does not have a lock screen PIN/Passcode.
  2. Connect your phone to the computer using the appropriate device cable or you can follow these steps to connect your device using Wi-Fi.

    • Run the adb devices command to confirm that your device is listed among the attached devices.
  3. Create a settings file or reuse one of the provided sample settings files.

    Expand to see settings file structure
    ```
    {
      "iterations": 1,
      "instanceURL": "<instance url>",
      "hasPrivacyPolicy": false,
      "capabilities": [
        {
          "platformVersion": "<android version>",
          "deviceName": "<phone model>",
          "appPath": "<path to cht android apk>",
          "noReset": false
        }
      ],
    
      "skip": {
        "login": false,
    
        "loadContactList": false,
        "loadChwArea": false,
        "loadHousehold": false,
        "loadPatient": false,
        "loadMessageList": false,
        "loadTaskList": false,
        "loadAnalytics": false,
        "loadReportList": false,
        "searchContact": false,
        "searchReport": false,
        "submitTask": true,
        "createPatient": true,
        "submitPatientReport": true
      },
    
      "users": [
        {
          "type": "offline",
          "role": "chw",
          "username": "<username>",
          "password": "<password>"
        }
      ],
    
      "pages": {
        "contactList": {
          "navigation": [
            { "selector": "//*[@text=\"Reports\"]" },
            {
              "selector": "//*[@text=\"People\"]",
              "asserts": [ { "selector": "//*[contains(@text, \"West Miltonside\")]" } ]
            }
          ],
          "search": {
            "value": "Rafael Windler",
            "asserts": [
              { "selector": "//*[@text=\"Rafael Windler\"]" },
              {
                "scrollDown": 5,
                "selector": "//*[contains(@text, \"No more people\")]"
              }
            ],
            "postTestPath": [
              { "selector": "//*[@text=\"Reports\"]" }
            ]
          }
        },
        "chwArea": {
          "navigation": [
            {
              "selector": "//*[contains(@text, \"West Miltonside\")]",
              "asserts": [
                { "selector": "//*[contains(@text, \"External ID\")]" },
                { "selector": "//*[contains(@text, \"Rafael Windler\")]" },
                { "selector": "//*[contains(@text, \"Households\")]" },
                { "selector": "//*[contains(@text, \"Abilene\")]" }
              ]
            }
          ],
          "postTestPath": [ { "selector": "//*[@text=\"Back\"]" } ]
        },
        "household": {
          "navigation": [
            { "selector": "//*[contains(@text, \"West Miltonside\")]" },
            {
              "selector": "//*[contains(@text, \"Abilene\")]",
              "asserts": [
                { "selector": "//*[contains(@text, \"Phone\")]" },
                { "selector": "//*[contains(@text, \"Bill Pouros\")]" },
                {
                  "scrollDown": 2,
                  "selector": "//*[text()[contains(.,\"Danger sign follow up\")]"
                },
                { "selector": "//*[text()[contains(.,\"Due today\")]" },
                { "selector": "//*[text()[contains(.,\"Pregnancy danger sign\")]" }
              ]
            }
          ]
        },
        "patient": {
          "navigation": [
            {
              "selector": "//*[contains(@text, \"Irene Shields\")]",
              "asserts": [
                { "selector": "//*[contains(@text, \"37 years\")]" },
                {
                  "scrollDown": 1,
                  "selector": "//*[contains(@text, \"Danger sign follow up\")]" },
                { "selector": "//*[contains(@text, \"Pregnancy danger sign\")]" }
              ]
            }
          ],
          "postTestPath": [ { "selector": "//*[@text=\"Back\"]" } ]
        },
        "messageList": {
          "navigation": [
            {
              "selector": "//*[@text=\"Messages\"]",
              "asserts": [ { "selector": "//*[contains(@text, \"No messages found\")]" } ]
            }
          ]
        },
        "reportList": {
          "navigation": [
            {
              "selector": "//*[@text=\"Reports\"]",
              "asserts": [ { "selector": "//*[contains(@text, \"Pregnancy danger sign\")]" } ]
            }
          ],
          "search": {
            "value": "Alexandra Lemke",
            "asserts": [
              {
                "selector": "//*[contains(@text, \"No reports found\")]"
              }
            ],
            "postTestPath": [
              { "selector": "//*[@text=\"Tasks\"]" }
            ]
          }
        },
        "taskList": {
          "navigation": [
            {
              "selector": "//*[@text=\"Tasks\"]",
              "asserts": [ { "selector": "//*[contains(@text, \"Danger sign follow up\")]" } ]
            }
          ]
        },
        "targets": {
          "navigation": [
            {
              "selector": "//*[@text=\"Targets\"]",
              "asserts": [ { "selector": "//*[contains(@text, \"New pregnancies\")]" } ]
            }
          ]
        }
      },
    
      "forms": {
        "patientReport": {
          "navigation": [{
            "scrollDown": 3,
            "selector": "//*[@text=\"Pregnancy\"]"
          }],
          "pages": [
            {
              "asserts": [
                { "selector": "//*[contains(@text, \"Select patient\")]" } // Wait for form to load
              ],
              "fields": [
                {
                  "selector": "<Mandatory. XPath selector of radio buttons, text boxes (to open keyboard), etc.>",
                  // Optional. Use this when you need to type in the phone's keyboard, find the keycodes here: https://developer.android.com/reference/android/view/KeyEvent
                  "keycodes": "<Array of numbers (keycodes)>",  
                  // Optional. Use this when you want an equivalent of element.setValue(myValue)
                  "value": "<String. Field value>",
                  // Optional. Use this when you want to select an option from a dropdown. The dropdown will open with the "selector" property
                  "dropdownOption": "<String. XPath selector to a dropdown option and clicks on it.>",
                  // Optional. Number of scrolls to reach the field
                  "scrollDown": 1,
                  "scrollUp": 1
                }
              ],
              // Optional. Number of scrolls to reach the page's buttons
              "scrollDown": 2,
              "scrollUp": 1
            },
            { // Add this to assert the form summary page.
              "asserts": [
                { "selector": "//*[@text=\"Pregnancy in danger\"]" },
                { "selector": "//*[contains(@text, \"Refer patient to clinic\")]" }
              ],
              // Optional. Number of scrolls to reach the page's buttons
              "scrollDown": 2,
              "scrollUp": 3
            }
          ],
          "postSubmitAsserts": [ // Add to assert result after form is submitted.
            { "select": "//android.widget.TextView[contains(@text, \"Submitted by Paula\")]" }
          ],
          "postTestPath": [ { "selector": "//*[@text=\"Back\"]" } ]
        }
      }
    }
    
    ```
    
  1. Set the environment variable APDEX_TEST_SETTINGS with the path of your settings file (apdex-settings.json). For example, you can use the following command but make sure to replace the path with your actual settings file location:
    export APDEX_TEST_SETTINGS=/Users/pepe/Documents/apdex-settings.json
    
    • Ensure the apdex-settings.json file has been updated with the correct instance url, login credentials and assertion texts (which correspond to the data in your cht instance) for page navigation, forms and other app interactions.
    • Under the skip section of the settings file, set true for the tests you want to skip and false for those you want to execute.
    • Update the fields for platformVersion and deviceName to match the value for your device.
      • Find the android version (platformVersion) by running adb shell getprop | grep ro.build.version.release.
      • Find the device name (deviceName) by running adb shell getprop | grep ro.product.model.
  2. Ensure all dependencies have been properly installed - run npm ci from the root directory.
  3. Run npm run apdex-test from the root directory to execute the selected tests.

Settings file

The settings file enables the navigation and interaction between pages, forms, and components by providing the required selectors, commands and assertion texts in the appropriate fields to be used during test execution.

PropertyTypeDescriptionMandatory
iterationsNumberTimes to run the test casesYes
instanceURLStringInstance urlYes
hasPrivacyPolicyBooleanWhether it has privacy policies to acceptYes
capabilitiesObject[]Configures Appium to use your device for testingYes
capabilities.platformVersionStringAndroid version. E.g. “13”. Find the android version by running adb shell getprop grep ro.build.version.releaseYes
capabilities.deviceNameStringDevice name. E.g. “Neon Ray Ultra S”. Find the device name by running adb shell getprop grep ro.product.modelYes
capabilities.appPathStringPath to CHT Android APK. E.g. “/Users/john/Downloads/cht-android-v1.4.0-unbranded-armeabi-v7a-release.apk”Yes
capabilities.noResetBooleanDefault false. When set false, it deletes the app cache and storage data. If set to true, the app will start again in the last page it was before, review your configuration to take that start point.No
skip.loginBooleanDefault false. Skip login, and it’s expected that the user has already login previous running the automation tests.No
skip.loadContactListBooleanDefault false. Skip test for loading the contact list.No
skip.loadChwAreaBooleanDefault false. Skip test for loading the CHT Area.No
skip.loadHouseholdBooleanDefault false. Skip test for loading a household.No
skip.loadPatientBooleanDefault false. Skip test for loading a patient.No
skip.searchContactBooleanDefault false. Skip test for searching patient.No
skip.loadTaskListBooleanDefault false. Skip test for loading the task list.No
skip.submitTaskBooleanDefault false. Skip test for submitting a task.No
skip.loadTargetsBooleanDefault false. Skip test for loading the targets page.No
skip.loadReportListBooleanDefault false. Skip test for loading the report list.No
skip.searchReportBooleanDefault false. Skip test for searching a report.No
skip.createPatientBooleanDefault false. Skip test for creating a patient.No
skip.submitPatientReportBooleanDefault false. Skip test for submiting a report for a patient.No
usersObject[]User to login and use for testingYes
users.typeStringUse: “offline” or “online”Yes
users.roleStringUse: “chw”Yes
users.usernameStringUsernameYes
users.passwordStringPasswordYes
commonElementsObjectOverrides selectors of system elementsNo
commonElements.fabStringXPath selector to elementNo
commonElements.fabListTitleStringXPath selector to elementNo
commonElements.formSubmitStringForm’s submit button labelNo
commonElements.formNextStringForm’s next button labelNo
commonElements.relaunchAppAssertStringXPath selector to elementNo
commonElements.searchIconStringXPath selector to elementNo
pagesObjectObject containing the definition of the pages that the automation tests use. See Pages section below for more details.Yes
pages.contactListObjectDefinition for Contact List page. See Pages section below for more details.Yes
pages.chwAreaObjectDefinition for Contact CHW Area page. See Pages section below for more details.Yes
pages.householdObjectDefinition for Contact Household page. See Pages section below for more details.Yes
pages.patientObjectDefinition for Contact Patient page. See Pages section below for more details.Yes
pages.messageListObjectDefinition for Message List page. See Pages section below for more details.Yes
pages.reportListObjectDefinition for Report List page. See Pages section below for more details.Yes
pages.taskListObjectDefinition for Task List page. See Pages section below for more details.Yes
pages.targetsObjectDefinition for Targets page. See Pages section below for more details.Yes
formsObjectDefinition for app forms or contact forms that the automation tests use. See Forms section below for more details.Yes
forms.patientReportObjectDefinition for an app form that is submitted on the Patient page. See Forms section below for more details.Yes
forms.patientContactObjectDefinition for a contact form that is used to create patients. Submitted from the Household page. See Forms section below for more details.Yes

Pages

Object containing the definition of the pages to load and assert during these automation tests.

PropertyTypeDescriptionMandatory
navigationObject[]Click path to navigate to that page. See Navigation section below for more details.Yes
searchObjectContains the configuration to filter data by using the search feature from the pageNo
search.valueStringSearch termNo
search.assertsObject[]Elements to assert once it has finished searching in the current page. Use to make sure all elements in the page have finished rendering. See Asserts section for more details.No
search.postTestPathObject[]Click path to navigate after the test has finished searching and the asserts has finished.No
postTestPathObject[]Click path to navigate after the test has finished. E.g. clicking on the back button to go back to the main list. It uses the same Navigation structure, see the Navigation section below for more details.No
relaunchAppBooleanSet true, to close and open the app before navigating to this page. This can make tests slower as the app needs to load the assets and data again.No

Forms

Object containing the definition of forms to load, fill fields and asserts outcomes.

PropertyTypeDescriptionMandatory
navigationObject[]Click path to navigate to that page. See Navigation section below for more details.Yes
useFABBooleanWhether to click on the floating action button to open the form before proceeding with the tests.Yes
pages.assertsObject[]Elements to assert once it has finished navigating to the current page. Use to make sure all elements in the page have finished rendering. See Asserts section for more details.Yes
pages.fieldsObject[]Fields to fill-up. See Fields section below for more details.Yes
pages.scrollDownNumberTimes to scroll down to reach to the page buttons.No
pages.scrollUpNumberTimes to scroll up in the page.No
postSubmitAssertsObject[]Elements to assert once it has finished submitting the form. Use to make sure the form was submitted correctly. Same as Asserts, see Asserts section for more details.Yes
postTestPathObject[]Click path to navigate after the test has finished. E.g. clicking on the back button to go back to the main list. It uses the same Navigation structure, see the Navigation section below for more details.No

Fields

Object containing the information to fill up fields.

PropertyTypeDescriptionMandatory
selectorStringXPath selector to element and clicks on it. Use it to select radio buttons or checkboxes; to activate a input text to open a keyboard; or to open a dropdown.Yes
keycodesNumber[]Array of keycodes. Use it when you need to type in the phone’s keyboard, find the keycodes here: https://developer.android.com/reference/android/view/KeyEventNo
dropdownOptionStringXPath selector to a dropdown option and clicks on it.No
valueString or NumberUse this to set the value directly in the input instead of using the keyboard. Like when using element.setValue().No
scrollDownNumberTimes to scroll down to reach to the element specified in the “selector”.No
scrollUpNumberTimes to scroll up to reach to the element specified in the “selector”.No
idStringWhen having too many fields, use the id to label your fields and help you understand better the settings file.No

Object containing the click path to navigate to a page.

PropertyTypeDescriptionMandatory
selectorStringXPath selector to element and clicks on it.Yes
assertsObject[]Elements to assert once it has finished navigating. Use to make sure all elements in the page have finished rendering. See Asserts section for more details.Yes
scrollDownNumberTimes to scroll down to reach to the element specified in the “selector”.No
scrollUpNumberTimes to scroll up to reach to the element specified in the “selector”.No

Asserts

Elements to assert that are displayed in the screen.

PropertyTypeDescriptionMandatory
selectorStringXPath selector to element and clicks on it.Yes
scrollDownNumberTimes to scroll down to reach to the element specified in the “selector”.No
scrollUpNumberTimes to scroll up to reach to the element specified in the “selector”.No

Tips

  • Take time to understand the forms you are testing:

    • Are fields appearing dynamically?
    • Are there field’s labels being updated automatically and removing the previous selection?
  • Assert for elements in the screen before interacting with them, to ensure they are ready.

  • Test how many scrolls you need by plugging the phone in the computer and run these adb commands:

    • Scroll down: adb shell input swipe 500 1000 300 300
    • Scroll up: adb shell input swipe 300 300 500 1000
    • For example, if you need to run the scroll down command 3 times, then you add 3 as the value for scrollDown like this: "scrollDown": 3,
  • In some cases, it’s necessary to unfocus a selected element, trigger a click in a label. For example:

{
  "id": "age_field_label",
  "selector": "//*[contains(@text, \"What is your age\")]"
},
  • XPATH selectors
    • Avoid XPATH selector with special characters like single quote, asterisks.
    • Find an element containing a text anywhere on the screen: "//*[text()[contains(.,\"Eric Patt\")]"
    • Use Appium Inspector to help you find the XPath selectors. Sometimes it produces very long selectors but you can find a way to make them shorter.
      • If it fails to start after setting up with capabilities, try running appium server with the appium command in the terminal before starting the Appium Inspector.