From f2115a569efeb17555db46c835a0a81b6989bb38 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Sep 2025 15:03:29 +0530 Subject: [PATCH 01/14] QA-4901 Data dictionary functionality and powerbi integration functionality --- .gitignore | 13 +- Features/DataDictionary/README.md | 57 + Features/DataDictionary/__init__.py | 0 Features/DataDictionary/requires.txt | 14 + Features/DataDictionary/settings-sample.cfg | 12 + Features/DataDictionary/settings.cfg | 14 + Features/DataDictionary/testCases/__init__.py | 0 Features/DataDictionary/testCases/conftest.py | 73 ++ Features/DataDictionary/testCases/report.html | 1091 +++++++++++++++++ Features/DataDictionary/testCases/test.py | 326 +++++ Features/DataDictionary/testPages/__init__.py | 0 .../DataDictionary/testPages/data/__init__.py | 0 .../testPages/data/data_dictionary_page.py | 513 ++++++++ .../DataDictionary/userInputs/__init__.py | 0 .../DataDictionary/userInputs/case_dd.xlsx | Bin 0 -> 8684 bytes .../userInputs/import_file.xlsx | Bin 0 -> 4974 bytes .../DataDictionary/userInputs/user_inputs.py | 32 + .../Powerbi_integration_exports/README.md | 57 + .../Powerbi_integration_exports/__init__.py | 0 .../Powerbi_integration_exports/requires.txt | 14 + .../settings-sample.cfg | 12 + .../Powerbi_integration_exports/settings.cfg | 14 + .../testCases/__init__.py | 0 .../testCases/conftest.py | 73 ++ .../testCases/report.html | 1091 +++++++++++++++++ .../testCases/test.py | 188 +++ .../testPages/__init__.py | 0 .../testPages/data/__init__.py | 0 .../testPages/data/power_bi_page.py | 274 +++++ .../userInputs/__init__.py | 0 .../userInputs/user_inputs.py | 28 + 31 files changed, 3895 insertions(+), 1 deletion(-) create mode 100644 Features/DataDictionary/README.md create mode 100644 Features/DataDictionary/__init__.py create mode 100644 Features/DataDictionary/requires.txt create mode 100644 Features/DataDictionary/settings-sample.cfg create mode 100644 Features/DataDictionary/settings.cfg create mode 100644 Features/DataDictionary/testCases/__init__.py create mode 100644 Features/DataDictionary/testCases/conftest.py create mode 100644 Features/DataDictionary/testCases/report.html create mode 100644 Features/DataDictionary/testCases/test.py create mode 100644 Features/DataDictionary/testPages/__init__.py create mode 100644 Features/DataDictionary/testPages/data/__init__.py create mode 100644 Features/DataDictionary/testPages/data/data_dictionary_page.py create mode 100644 Features/DataDictionary/userInputs/__init__.py create mode 100644 Features/DataDictionary/userInputs/case_dd.xlsx create mode 100644 Features/DataDictionary/userInputs/import_file.xlsx create mode 100644 Features/DataDictionary/userInputs/user_inputs.py create mode 100644 Features/Powerbi_integration_exports/README.md create mode 100644 Features/Powerbi_integration_exports/__init__.py create mode 100644 Features/Powerbi_integration_exports/requires.txt create mode 100644 Features/Powerbi_integration_exports/settings-sample.cfg create mode 100644 Features/Powerbi_integration_exports/settings.cfg create mode 100644 Features/Powerbi_integration_exports/testCases/__init__.py create mode 100644 Features/Powerbi_integration_exports/testCases/conftest.py create mode 100644 Features/Powerbi_integration_exports/testCases/report.html create mode 100644 Features/Powerbi_integration_exports/testCases/test.py create mode 100644 Features/Powerbi_integration_exports/testPages/__init__.py create mode 100644 Features/Powerbi_integration_exports/testPages/data/__init__.py create mode 100644 Features/Powerbi_integration_exports/testPages/data/power_bi_page.py create mode 100644 Features/Powerbi_integration_exports/userInputs/__init__.py create mode 100644 Features/Powerbi_integration_exports/userInputs/user_inputs.py diff --git a/.gitignore b/.gitignore index c37ae6e9b..e2e6d73a9 100644 --- a/.gitignore +++ b/.gitignore @@ -277,7 +277,18 @@ LocustScripts/update-scripts/project-config/co-carecoordination-perf/mobile_work /POCs/PercyWebApps/settings.cfg /POCs/VisualComparison/settings.cfg + +#Ignoring compliled files of DataDictionary +Features/DataDictionary/settings.cfg +Features/DataDictionary/report.html +Features/DataDictionary/test_cases/report.html + +#Ignoring compliled files of DataDictionary +Features/Powerbi_integration_exports/settings.cfg +Features/Powerbi_integration_exports/report.html +Features/Powerbi_integration_exports/test_cases/report.html + USH_Apps/WeeklyBHACleanup/testCases/report.html USH_Apps/WeeklyBHACleanup/report.html USH_Apps/WeeklyBHACleanup/report_* -USH_Apps/WeeklyBHACleanup/settings.cfg \ No newline at end of file +USH_Apps/WeeklyBHACleanup/settings.cfg diff --git a/Features/DataDictionary/README.md b/Features/DataDictionary/README.md new file mode 100644 index 000000000..88116b467 --- /dev/null +++ b/Features/DataDictionary/README.md @@ -0,0 +1,57 @@ +## Commcare Data Dictionary Test Script + +These tests ensure that the Data Dictionary (https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143944977/Data+Dictionary)features work as expected and that there are no regressions +The automated tests comprises of the Data dictionary functionality.](https://docs.google.com/spreadsheets/d/1Ixw1PC9PVpqYOlP7aq9a86pC0wZaJVgGNVL_4h9KLbM/edit#gid=195915568) +## Executing Scripts + +### On Local Machine + +#### Setting up test environment + +```sh + +# create and activate a virtualenv using your preferred method. Example: +python -m venv venv +source venv/bin/activate + + +# install requirements +pip install -r requires.txt + +``` + +[More on setting up virtual environments](https://confluence.dimagi.com/display/GTD/QA+and+Python+Virtual+Environments) + + +#### Running Tests + + + - Copy `settings-sample.cfg` to `settings.cfg` and populate `settings.cfg` for +the environment you want to test. +- Run tests using pytest command like: + +```sh + +# To execute all the test cases +pytest -v --rootdir= Features/DataDictionary/testCases + +``` +- You could also pass the following arguments + - ` -n 3 --dist=loadfile` - This will run the tests parallelly in 3 instances. The number of reruns is configurable. + - ` --reruns 1` - This will re-run the tests once in case of failures.The number of reruns is configurable too. + +### Trigger Manually on Gitaction + +clone this repository + +To manually trigger the script, + - Go to [DataDictionary](https://github.com/dimagi/dimagi-qa/actions/lookuptable.yml) + - Run workflow + - Use workflow from ```master``` + - Run! + +If you are a part of the QA team, you'll receive emails for the result of the run after it's complete. + +clone this repository + +Besides, you should be able to find the zipped results in the **Artifacts** section, of the corresponding run (after it's complete). diff --git a/Features/DataDictionary/__init__.py b/Features/DataDictionary/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/DataDictionary/requires.txt b/Features/DataDictionary/requires.txt new file mode 100644 index 000000000..826c8961f --- /dev/null +++ b/Features/DataDictionary/requires.txt @@ -0,0 +1,14 @@ +## Stores information about all the libraries, modules, and packages that are used in this project. + + +pandas>=1.2.2 +pytest>=6.2.5 +pytest-html>=3.1.1 +selenium == 4.11.2 +openpyxl>=3.0.6 +pytest-rerunfailures>=10.2 +pytest-xdist>=2.4.0 +pytest-xdist[psutil] +pyotp >=2.6.0 +pytest-order +py \ No newline at end of file diff --git a/Features/DataDictionary/settings-sample.cfg b/Features/DataDictionary/settings-sample.cfg new file mode 100644 index 000000000..b6bb402bb --- /dev/null +++ b/Features/DataDictionary/settings-sample.cfg @@ -0,0 +1,12 @@ +[default] +# This is the environment url of commcare +url = https://www.commcarehq.org/ +# Login username of the webuser +login_username = +# Login password of the webuser +login_password = +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + diff --git a/Features/DataDictionary/settings.cfg b/Features/DataDictionary/settings.cfg new file mode 100644 index 000000000..d150a7436 --- /dev/null +++ b/Features/DataDictionary/settings.cfg @@ -0,0 +1,14 @@ +[default] +# This is the environment url of commcare +url = https://staging.commcarehq.org/ +# Login username of the webuser +login_username = automation.user.commcarehq@gmail.com +# Login password of the webuser +login_password = pass@123 +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + + + diff --git a/Features/DataDictionary/testCases/__init__.py b/Features/DataDictionary/testCases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/DataDictionary/testCases/conftest.py b/Features/DataDictionary/testCases/conftest.py new file mode 100644 index 000000000..b9aa46ac7 --- /dev/null +++ b/Features/DataDictionary/testCases/conftest.py @@ -0,0 +1,73 @@ +import os + +from configparser import ConfigParser +from pathlib import Path +from common_utilities.fixtures import * + +""""This file provides fixture functions for driver initialization""" + +global driver + + +@pytest.fixture(scope="session") +def environment_settings_lookup(): + """Load settings from os.environ + + Names of environment variables: + DIMAGIQA_URL + DIMAGIQA_LOGIN_USERNAME + DIMAGIQA_LOGIN_PASSWORD + DIMAGIQA_MAIL_USERNAME + DIMAGIQA_MAIL_PASSWORD + + See https://docs.github.com/en/actions/reference/encrypted-secrets + for instructions on how to set them. + """ + settings = {} + for name in ["url", "login_username", "login_password", "mail_username", + "mail_password", "staging_auth_key", "prod_auth_key"]: + + var = f"DIMAGIQA_{name.upper()}" + if var in os.environ: + settings[name] = os.environ[var] + if "url" not in settings: + env = os.environ.get("DIMAGIQA_ENV") or "staging" + subdomain = "www" if env == "production" else env + # updates the url with the project domain while testing in CI + project = "a/qa-automation-prod" if env == "production" else "a/qa-automation" + settings["url"] = f"https://{subdomain}.commcarehq.org/{project}" + return settings + + +@pytest.fixture(scope="session", autouse=True) +def settings(environment_settings_lookup): + if os.environ.get("CI") == "true": + settings = environment_settings_lookup + settings["CI"] = "true" + if any(x not in settings for x in ["url", "login_username", "login_password", + "mail_username", "mail_password", "staging_auth_key", + "prod_auth_key"]): + lines = environment_settings_lookup.__doc__.splitlines() + vars_ = "\n ".join(line.strip() for line in lines if "DIMAGIQA_" in line) + raise RuntimeError( + f"Environment variables not set:\n {vars_}\n\n" + "See https://docs.github.com/en/actions/reference/encrypted-secrets " + "for instructions on how to set them." + ) + return settings + path = Path(__file__).parent.parent / "settings.cfg" + if not path.exists(): + raise RuntimeError( + f"Not found: {path}\n\n" + "Copy settings-sample.cfg to settings.cfg and populate " + "it with values for the environment you want to test." + ) + settings = ConfigParser() + settings.read(path) + # updates the url with the project domain while testing in local + if settings["default"]["url"] == "https://www.commcarehq.org/": + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation-prod" + else: + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation" + return settings["default"] + diff --git a/Features/DataDictionary/testCases/report.html b/Features/DataDictionary/testCases/report.html new file mode 100644 index 000000000..5b4df70f9 --- /dev/null +++ b/Features/DataDictionary/testCases/report.html @@ -0,0 +1,1091 @@ + + + + + report.html + + + + +

report.html

+

Report generated on 02-Sep-2025 at 14:36:20 by pytest-html + v4.1.1

+
+

Environment

+
+
+ + + + + +
+
+

Summary

+
+
+

10 tests took 00:07:58.

+

(Un)check the boxes to filter the results.

+
+ +
+
+
+
+ + 2 Failed, + + 8 Passed, + + 0 Skipped, + + 0 Expected failures, + + 0 Unexpected passes, + + 0 Errors, + + 0 Reruns +
+
+  /  +
+
+
+
+
+
+
+
+ + + + + + + + + +
ResultTagsTestDuration
+ + + \ No newline at end of file diff --git a/Features/DataDictionary/testCases/test.py b/Features/DataDictionary/testCases/test.py new file mode 100644 index 000000000..135d3b7a5 --- /dev/null +++ b/Features/DataDictionary/testCases/test.py @@ -0,0 +1,326 @@ +import time + +import pytest + +from Features.DataDictionary.userInputs.user_inputs import UserData +from HQSmokeTests.testPages.data.import_cases_page import ImportCasesPage +from HQSmokeTests.testPages.home.home_page import HomePage +from HQSmokeTests.testPages.applications.application_page import ApplicationPage +from Features.DataDictionary.testPages.data.data_dictionary_page import DataDictionaryPage +from HQSmokeTests.testPages.messaging.messaging_page import MessagingPage +from HQSmokeTests.testPages.users.org_structure_page import latest_download_file +from HQSmokeTests.testPages.users.roles_permissions_page import RolesPermissionPage +from common_utilities.hq_login.login_page import LoginPage +from HQSmokeTests.testPages.users.web_user_page import WebUsersPage + + +""""Contains test cases related to the Data module""" + +values = dict() + +@pytest.mark.lookup +def test_ui(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.dropdown() + +def test_edit(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.edit_case_property_description() + +def test_case_property_adding(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.add_new_case_property() + data.delete_case_property1() + +def test_add_case_group(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.add_a_new_group() + +def test_deprecate(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_property() + +def test_add_group_description(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.group_description() + +def test_download(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.download() + +def test_upload(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + download_path = data.latest_download_file() + data.upload_dd(download_path) + +def test_case_type_deprecate(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + data.restore_case_type1() + +def test_make_new_version(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + home.applications_menu(UserData.application) + data.application() + data.case_data_page() + home.data_menu() + data.data_page() + data.restore_case_type1() + +def test_deprecate_case_types_reports(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + home.reports_menu() + data.reports() + home.data_menu() + data.restore_case_type1() + +def test_deprecate_case_types_exports(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + home.data_menu() + data.exports() + home.data_menu() + data.restore_case_type1() + home.data_menu() + data.exports() + +def test_deprecate_case_types_exports_1(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.create_case_export() + home.data_menu() + data.ui() + data.deprecate_case_type1() + data.validate_exports() + data.restore_case_type1() + home.data_menu() + data.validate_exports() + +def test_edit_data_deprecate_cases(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + data.exports_edit_data_section(UserData.data_upload_path) + home.data_menu() + data.restore_case_type1() + data.exports_edit_data_section(UserData.data_upload_path) + +def test_messaging(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_type1() + menu = HomePage(driver, settings) + msg = MessagingPage(driver) + menu.messaging_menu() + data.messaging() + home.data_menu() + data.restore_case_type1() + menu.messaging_menu() + data.messaging() + + +def test_vv2(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.dropdown() + data.valid_values_2() + +def test_vv3(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.dropdown() + data.valid_values_3() + +def test_vv5(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.download() + download_path = latest_download_file() + data.excel_verification(download_path) + +def test_vv6(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.download() + download_path = latest_download_file() + data.update_excel(download_path) + data.upload_dd(download_path) + +def test_vv7(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.download() + download_path = latest_download_file() + data.update_excel_invalid(download_path) + data.upload_dd(download_path) + + +def test_case_management(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.add_property_description() + home.applications_menu(UserData.application) + data.case_management() + data.app_summary() + +def test_restore_case_property(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + data.deprecate_case_property() + home.applications_menu(UserData.application) + data.case_management() + data.warning_message() + home.data_menu() + data.ui() + data.restore_case_property() + +def test_cle(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = DataDictionaryPage(driver) + data.ui() + property_value = data.add_new_case_property() + home.reports_menu() + data.case_list_explorer1(property_value,'yes') + home.data_menu() + data.ui() + data.delete_case_property1() + home.reports_menu() + data.case_list_explorer1(property_value,'no') + +def test_roles_permission(driver,settings): + login = LoginPage(driver, settings["url"]) + login.logout() + login.login(settings["login_username"], settings["login_password"]) + menu = HomePage(driver, settings) + menu.users_menu() + role = RolesPermissionPage(driver, settings) + role.roles_menu_click() + print("Opened Roles and Permissions Page") + role_name1 = role.add_non_admin_role_dd(1) + print (role_name1) + web_user1 = WebUsersPage(driver) + menu.users_menu() + web_user1.edit_user_permission(role_name1) + login.logout() + login.login(UserData.p1p2_user, settings["login_password"]) + data = DataDictionaryPage(driver) + data.data_dictionary_access_page() + login.logout() + login.login(settings["login_username"], settings["login_password"]) + menu = HomePage(driver, settings) + menu.users_menu() + role = RolesPermissionPage(driver, settings) + role.roles_menu_click() + print("Opened Roles and Permissions Page") + role_name1 = role.add_non_admin_role_dd(2) + print(role_name1) + web_user1 = WebUsersPage(driver) + time.sleep(2) + menu.users_menu() + web_user1.edit_user_permission(role_name1) + login.logout() + login.login(UserData.p1p2_user, settings["login_password"]) + data = DataDictionaryPage(driver) + data.data_dictionary_access_page() + login = LoginPage(driver, settings["url"]) + login.logout() + login.login(settings["login_username"], settings["login_password"]) + menu = HomePage(driver, settings) + menu.users_menu() + role = RolesPermissionPage(driver, settings) + role.roles_menu_click() + print("Opened Roles and Permissions Page") + role_name1 = role.add_non_admin_role_dd(3) + print(role_name1) + web_user1 = WebUsersPage(driver) + menu.users_menu() + web_user1.edit_user_permission(role_name1) + login.logout() + login.login(UserData.p1p2_user, settings["login_password"]) + data = DataDictionaryPage(driver) + data.data_dictionary_revoke_access() + login.logout() + time.sleep(2) + login.login(settings["login_username"], settings["login_password"]) + menu.users_menu() + web_user1.edit_user_permission("Admin") + role.roles_menu_click() + role.delete_test_roles() + +def test_case_importer(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + imp = ImportCasesPage(driver) + imp.replace_property_and_upload(UserData.case_type, UserData.file, "YES", ['Hindi', 'Telugu','YYYY-MM-DD'], + ['select_dd_language', 'opened_date']) + + + + + + + + + + + + + diff --git a/Features/DataDictionary/testPages/__init__.py b/Features/DataDictionary/testPages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/DataDictionary/testPages/data/__init__.py b/Features/DataDictionary/testPages/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/DataDictionary/testPages/data/data_dictionary_page.py b/Features/DataDictionary/testPages/data/data_dictionary_page.py new file mode 100644 index 000000000..30cadfbfa --- /dev/null +++ b/Features/DataDictionary/testPages/data/data_dictionary_page.py @@ -0,0 +1,513 @@ +import os.path +import time +from time import sleep + +from matplotlib.widgets import EllipseSelector +from selenium.webdriver.common.by import By + +from ElasticSearchTests.testCases.conftest import settings +from Features.DataDictionary.userInputs.user_inputs import UserData +from HQSmokeTests.testCases.conftest import driver +from HQSmokeTests.testPages.home.home_page import HomePage +from common_utilities.Excel.excel_manage import ExcelManager +from common_utilities.selenium.base_page import BasePage +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings +from selenium.webdriver.support.select import Select + +""""Contains test page elements and functions related to the Lookup Table module""" + + +class DataDictionaryPage(BasePage): + + def __init__(self, driver): + super().__init__(driver) + + #data dictionary page + self.view_data = (By.XPATH, " //*[@id='ProjectDataTab']") + self.Data = (By.LINK_TEXT, "Data") + self.view_all = (By.LINK_TEXT, "View All") + self.data_dictionary = (By.XPATH, "//*[@id='hq-sidebar']/nav/ul[4]/li/a") + self.dd = (By.XPATH, "//*[@class='text-hq-nav-header']") + self.check = (By.XPATH, "//*[@id='download-dict']") + self.datatype = (By.XPATH, "(//select[@class='form-control'])[5]") + self.upload = (By.XPATH, "//*[@id='gtm-upload-dict']") + self.choose_file1 = (By.XPATH, "//input[@id='file']") + self.upload_button = (By.XPATH, "//*[@class='btn btn-primary disable-on-submit']") + self.menu_settings = (By.XPATH, "//a[@class='appnav-title appnav-title-secondary appnav-responsive']") + self.type_value = (By.XPATH, "//*[@id='case_type']") + self.save = (By.XPATH, "//*[@class='pull-right savebtn-bar savebtn-bar-save']") + self.case_type_value = (By.XPATH, "//*[@href='#case_dd']") + self.case_property = (By.XPATH, "(//div[@id='data-dictionary-table']//div[1]//div[4]//select[1])") + self.case_property_vv = (By.XPATH,"//div[@class='groups ui-sortable']//div[2]//div[4]//select[1]") + self.description = (By.XPATH, "//div[@class='groups ui-sortable']//div//div[1]//div[5]//textarea[1]") + self.add_property = (By.XPATH, "//*[@id='data-dictionary-table']/div[2]/div[1]/div[4]/div/form/input") + self.add_group = (By.XPATH, "//input[@placeholder='Group Name']") + self.add_property_button = (By.XPATH, "//*[@id='gtm-add-case-property']") + self.added_group = (By.XPATH, "//*[@id='data-dictionary-table']/div[2]/div[1]/div[2]/div[5]/textarea") + self.deprecate_button = (By.XPATH, "(//*[@id='gtm-deprecate-case-property'])[1]") + self.show_deprecate = (By.XPATH, "//*[@data-bind='click: $root.showDeprecated, visible: !showAll()']") + self.restore_button = (By.XPATH, "//button[@title='Restore Property']") + self.hide_deprecate = (By.XPATH, "//*[@data-bind='click: $root.hideDeprecated, visible: showAll']") + self.deprecate_case = (By.XPATH, "//*[@id='hq-content']/div[2]/div[3]/div/a[3]") + self.confirm = (By.XPATH, "//*[@id='gtm-deprecate-case-type-confirm']") + self.show_deprecate_case_type = (By.XPATH, "//*[@class='deprecate-case-type']") + self.add_group_button = (By.XPATH, "//*[@id='gtm-add-case-property-group']") + self.data_dictionary = (By.LINK_TEXT, "Data Dictionary") + self.delete_case_property = (By.XPATH, "(//button[@title='Delete Property'][normalize-space()='Delete'])[1]") + self.delete_property_confirm = (By.XPATH, "//button[@id='delete-case-prop-btn']") + self.show_deprecated_case_type = (By.XPATH, "//*[@class='deprecate-case-type']") + self.restore_case_type = (By.XPATH, "(//*[@class='btn btn-default'])[3]") + self.date_valid_values = (By.XPATH, + "//*[@id='data-dictionary-table']/div[2]/div[1]/div[3]/div[1]/div[6]/div[2]") + self.edit_button = (By.XPATH, "//div[@id='data-dictionary-table']//div[1]//div[6]//div[1]//div[1]//div[1]//a[1]") + self.edit_button_vv = (By.XPATH,"//div[@class='groups ui-sortable']//div[2]//div[6]//div[1]//div[1]//div[1]//a[1]") + self.add_item = (By.XPATH, "(//a[@data-enum-action='add'])[3]") + self.valid_value_text = (By.XPATH, "(//input[@placeholder='valid value'])[3]") + self.valid_description = (By.XPATH, "(//input[@placeholder='description'])[3]") + self.done = (By.XPATH, "(//button[@class='btn btn-primary'][normalize-space()='Done'])[3]") + self.property_deprecate = (By.XPATH, "(//*[@id='gtm-deprecate-case-property'])[1]") + self.property_deprecate_message = (By.XPATH, "(//*[contains(text(),'Property has been deprecated')])[3]") + self.data_strong = (By.XPATH, "//*[@id='hq-breadcrumbs']/li[1]/a/strong") + self.property_description = (By.XPATH, "//div[@class='groups ui-sortable']//div//div[5]//div[5]//textarea[1]") + self.add_property_values = "//div[@class='atwho-view'][not(contains(@style,'none'))]//li//strong[.='{}'] " + self.delete_button_vv = (By.XPATH,"//a[@data-enum-action='remove']") + self.upload_error_message = (By.XPATH, + "//div[@id='hq-messages-container']/div[@class='row']/div[@class='col-sm-12']/div[@class='alert alert-margin-top fade in html alert-danger']") + + #In App + self.make_new_version_button = (By.XPATH, "//button[contains(@data-bind,'Make New Version')]") + self.case_list = (By.XPATH, "//*[@id='hq-sidebar']/nav/ul[1]/li/div/a[2]") + self.case_type_warning = (By.XPATH, "//*[@id='deprecated-case-types-warning']") + self.applications_menu_id = (By.ID, "ApplicationsTab") + self.case_list_warning = (By.XPATH, "//*[@id='case_type_deprecated_warning']") + self.app_description = (By.XPATH, + "//*[@id='js-appmanager-body']/div[3]/inline-edit/div/div/div[2]/div[1]/textarea") + self.edit_icon = (By.XPATH, "//*[@id='js-appmanager-body']/div[3]/inline-edit/div/div/div[1]/span[4]/i") + self.save_description = (By.XPATH, + "//*[@id='js-appmanager-body']/div[3]/inline-edit/div/div/div[2]/div[3]/button[1]") + self.case_data_page_warning = (By.XPATH, "//*[@class='alert alert-warning']") + self.registration_form = (By.XPATH, "(//*[@data-category='App Builder'])[1]") + self.settings_icon = (By.XPATH, "(//*[@data-category='App Builder'])[2]") + self.description_text = (By.XPATH, "(//*[@class='read-only'])[4]") + self.app_summary_button = (By.XPATH, + "//*[@class='appmanager-page-actions']/a/i[@class = 'fa-regular fa-rectangle-list']") + self.case_summary_button = (By.XPATH, "//*[@class='fcc fcc-fd-external-case appnav-primary-icon']") + self.description_property = (By.XPATH, "(//*[@data-bind='text: $parent.description'])[2]") + + # Reports + self.case_list_explorer = (By.LINK_TEXT, "Case List Explorer") + self.report_case_type = (By.XPATH, "//select[contains(@id,'report_filter_case_type')]") + + #Exports + self.export_case_data_link = (By.LINK_TEXT, 'Export Case Data') + self.add_export_button = (By.XPATH, "//a[@href='#createExportOptionsModal']") + self.case_type_dropdown = (By.XPATH, "//select[@name='case_type']") + self.daily_saved_exports_link = (By.LINK_TEXT, 'Daily Saved Exports') + self.close_popup = (By.XPATH, "//*[@id='createExportOptionsModal']/div/form/div/div[1]/button") + self.model_type = (By.ID, "id_model_type") + self.export_excel_dash_int = (By.LINK_TEXT, 'Excel Dashboard Integration') + self.powerBI_tab_int = (By.LINK_TEXT, 'PowerBi/Tableau Integration') + self.add_export_conf = (By.XPATH, "//button[@data-bind='visible: showSubmit, disable: disableSubmit']") + self.export_settings_create = (By.XPATH, "//button[@class='btn btn-lg btn-primary']") + self.warning_label = (By.XPATH, "//*[@class='badge text-bg-warning']") + self.case_type_dropdown1 = (By.XPATH, + "//label[.='Case Type']//following-sibling::div/select[@name='case_type']") + self.copy_cases_menu = (By.LINK_TEXT, "Copy Cases") + self.reassign_cases_menu = (By.LINK_TEXT, "Reassign Cases") + self.reassign_case_type = (By.ID, "report_filter_case_type") + self.deduplicate_case_link = (By.LINK_TEXT, 'Deduplicate Cases') + self.add_rule_button = (By.ID, 'add-new') + self.add_rule_name = (By.XPATH, "//input[@type='text']") + self.deduplicate_case_type = (By.XPATH, "//select[@name='case_type']") + self.import_cases_menu = (By.LINK_TEXT, "Import Cases from Excel") + self.choose_file = (By.XPATH,"//input[@id='id_bulk_upload_file']") + self.next_step = (By.XPATH, "//button[normalize-space()='Next step']") + + # Messaging + self.cond_alerts = (By.LINK_TEXT, "Conditional Alerts") + self.add_cond_alert = (By.LINK_TEXT, "New Conditional Alert") + self.messaging_menu_id = (By.ID, "MessagingTab") + self.case_type_ca = (By.XPATH, "//select[contains(@name,'case_type')]") + self.cond_alert_name_input = "cond_alert_" + fetch_random_string() + self.cond_alert_name = (By.XPATH, "//input[@name='conditional-alert-name']") + self.continue_button_basic_tab = ( + By.XPATH, "//button[@data-bind='click: handleBasicNavContinue, enable: basicTabValid']") + #CLE + self.property_value = (By.XPATH, "//div[@id='data-dictionary-table']//div[1]//div[3]//div[1]//div[3]//input[1]") + self.edit_column = (By.XPATH, + "//div[./label[contains(.,'Columns')]]//following-sibling::div//a[@data-parent='#case-list-explorer-columns']") + self.properties_table = (By.XPATH, "//tbody[contains(@data-bind,'properties')]") + self.add_property_button_cle = (By.XPATH, "//button[normalize-space()='Add Property']") + self.property_name_input = (By.XPATH, "(//tbody[contains(@data-bind,'properties')]//td[2]//input)[last()]") + self.apply_id = (By.ID, "apply-filters") + self.add_property_column = (By.XPATH, "(//table[contains(@class,'datatable')]//th[5])[1]") + + + def ui(self): + self.js_click(self.data_dictionary, 10) + assert self.is_present_and_displayed(self.dd, 10) + print("Data dictionary page is opened") + self.wait_to_click(self.case_type_value) + + def data_page(self): + self.js_click(self.data_dictionary, 10) + assert self.is_present_and_displayed(self.dd, 10) + print("Data dictionary page is opened") + + def dropdown(self): + self.wait_to_click(self.datatype) + dropdown_values = self.find_elements_texts(self.datatype) + print(dropdown_values) + dt = ['Select a data type\nDate\nPlain\nNumber\nMultiple Choice\nBarcode\nGPS\nPhone Number\nPassword'] + assert dropdown_values == dt + print("Below are the dropdown values") + for data_type in dropdown_values: + print(data_type) + + def download(self): + self.wait_to_click(self.check, 2) + print("File is downloaded") + time.sleep(5) + + def latest_download_file(self): + os.chdir(PathSettings.DOWNLOAD_PATH) + files = sorted(os.listdir(os.getcwd()), key=os.path.getmtime) + max_file = max(files, key=os.path.getctime) + path = os.path.join(PathSettings.DOWNLOAD_PATH, max_file) + return path + + def upload_dd(self, path): + download_path = str(PathSettings.DOWNLOAD_PATH / path) + self.wait_to_click(self.upload, 2) + self.send_keys(self.choose_file, download_path) + self.wait_to_click(self.upload_button, 2) + if self.is_present_and_displayed(self.upload_error_message): + print("Error in uploading file") + else: + print("File is upload") + + def edit_case_property_description(self): + self.wait_to_click(self.case_type_value, 10) + self.wait_to_click(self.description) + self.wait_to_clear_and_send_keys(self.description, "property description to be tested") + self.wait_to_click(self.case_property, 10) + ss1 = Select(self.find_element(self.case_property)) + ss1.select_by_visible_text('Plain') + self.wait_to_click(self.save) + print("editing property description updated") + + def add_new_case_property(self): + self.wait_to_click(self.case_type_value, 5) + self.wait_to_click(self.add_property) + self.send_keys(self.add_property, UserData.case_properties) + self.wait_to_click(self.add_property_button) + self.wait_to_click(self.save) + value1 = self.wait_to_get_value(self.property_value) + print("new property added :", value1) + return value1 + + def case_list_explorer1(self, value1, flag): + self.wait_to_click(self.case_list_explorer) + time.sleep(30) + self.wait_for_element(self.edit_column) + self.wait_to_click(self.edit_column,10) + self.wait_for_element(self.properties_table) + self.wait_to_click(self.add_property_button_cle) + self.wait_to_click(self.property_name_input) + self.send_keys(self.property_name_input, value1) + time.sleep(0.5) + if flag == "yes": + assert self.is_present( + (By.XPATH, self.add_property_values.format(value1))), 'entered property is not displayed' + print("entered property displayed on the dropdown") + else: + assert not self.is_present( + (By.XPATH, self.add_property_values.format(value1))), "Entered property is displayed" + print("added property is deleted on data dictionary page") + self.wait_to_click(self.apply_id) + time.sleep(10) + self.scroll_to_bottom() + self.is_present_and_displayed(self.add_property_column) + property_added = self.get_text(self.add_property_column) + assert property_added == value1 + print("Added property displayed on the CLE report result page") + + def delete_case_property1(self): + self.reload_page() + self.wait_to_click(self.delete_case_property) + self.wait_to_click(self.delete_property_confirm) + self.wait_to_click(self.save) + print(" added property deleted") + + def add_a_new_group(self): + self.wait_to_click(self.case_type_value, 10) + self.wait_to_click(self.add_group) + self.wait_to_clear_and_send_keys(self.add_group, UserData.name_group) + added_group_name = self.wait_to_get_value(self.add_group) + self.wait_to_click(self.add_group_button, 10) + self.wait_to_click(self.save, 10) + self.is_present_and_displayed(self.added_group, 10) + print(added_group_name,"group added successfully") + + def group_description(self): + self.wait_to_click(self.case_type_value, 10) + self.wait_to_clear_and_send_keys(self.added_group, "updated_description_value") + self.wait_to_click(self.addgroup) + self.wait_to_click(self.save, 10) + print("group description is updated") + + def deprecate_property(self): + self.wait_to_click(self.case_type_value, 2) + self.wait_to_click(self.deprecate_button, 2) + self.wait_to_click(self.save, 5) + self.reload_page() + self.js_click(self.show_deprecate, 2) + self.is_present_and_displayed(self.restore_button) + print("Restore button is displayed") + self.is_present_and_displayed(self.hide_deprecate) + print("hide deprecate button is displayed") + self.wait_to_click(self.restore_button) + self.wait_to_click(self.save, 2) + print("Case property is restored") + + def deprecate_case_type1(self): + self.wait_to_click(self.case_type_value, 10) + self.wait_to_click(self.deprecate_case, 10) + self.wait_to_click(self.confirm, 10) + self.wait_to_click(self.show_deprecate_case_type, 10) + print("case type has been deprecated") + self.wait_to_click(self.data_strong) + + def restore_case_type1(self): + self.wait_to_click((self.data_strong)) + self.js_click(self.data_dictionary, 10) + self.wait_to_click(self.show_deprecated_case_type) + self.wait_to_click(self.case_type_value) + self.wait_to_click(self.restore_case_type) + print("casetype has been restored") + + def application(self): + self.wait_to_click(self.edit_icon) + self.wait_to_clear_and_send_keys(self.app_description, UserData.application_description) + self.wait_to_click(self.save_description) + self.wait_to_click(self.make_new_version_button, 10) + self.is_present_and_displayed(self.case_type_warning, 10) + print("The new application build contains the following deprecated case type") + self.wait_to_click(self.case_list, 10) + self.is_present_and_displayed(self.case_list_warning, 10) + print("This case type has been deprecated in the Data Dictionary on case list page") + + def app_summary(self): + self.wait_to_click(self.app_summary_button) + self.wait_to_click(self.case_summary_button) + self.wait_to_click(self.description_property) + property_description_value = self.get_text(self.description_property) + assert property_description_value == 'Testing the age property description' + print("Descriptions of each property automatically show in the App Summary page.") + + def case_data_page(self): + self.get_url(UserData.case_data_link) + self.is_present_and_displayed(self.case_data_page_warning) + print("This case uses a deprecated case type. See the help documentation for more information is displayed") + + def reports(self): + self.wait_to_click(self.case_list_explorer, 10) + self.wait_for_element(self.report_case_type, 60) + dropdown = self.get_all_dropdown_options(self.report_case_type) + if 'case_dd' in dropdown: + print("Active case types are displayed in the reports.") + else: + print("deprecated case types are not displayed in the reports.") + + def exports(self): + self.wait_to_click(self.export_case_data_link, 10) + self.wait_for_element(self.add_export_button, 200) + dropdown = self.get_all_dropdown_options(self.case_type_dropdown) + if 'case_dd' in dropdown: + print("Active case types are displayed in the case exports.") + else: + print("deprecated case types are not displayed in the case exports.") + #self.wait_to_click(self.close_popup) + self.wait_to_click(self.daily_saved_exports_link) + self.wait_to_click(self.add_export_button, 30) + self.is_visible_and_displayed(self.model_type, 200) + self.wait_for_element(self.model_type, 400) + self.select_by_value(self.model_type, UserData.model_value) + dropdown = self.get_all_dropdown_options(self.case_type_dropdown) + if 'case_dd' in dropdown: + print("Active case types are displayed in the daily saved exports.") + else: + print("deprecated case types are not displayed in the daily saved exports.") + self.wait_to_click(self.export_excel_dash_int) + self.wait_to_click(self.add_export_button) + self.wait_to_click(self.model_type) + self.select_by_value(self.model_type, UserData.model_value) + dropdown = self.get_all_dropdown_options(self.case_type_dropdown) + if 'case_dd' in dropdown: + print("Active case types are displayed in the excel dashboard exports.") + else: + print("deprecated case types are not displayed in the excel dashboard exports.") + #self.wait_to_click(self.close_popup) + self.wait_to_click(self.powerBI_tab_int) + self.wait_to_click(self.add_export_button) + self.wait_to_click(self.model_type) + self.select_by_value(self.model_type, UserData.model_value) + dropdown = self.get_all_dropdown_options(self.case_type_dropdown) + if 'case_dd' in dropdown: + print("Active case types are displayed in the power bi exports.") + else: + print("deprecated case types are not displayed in the power bi exports.") + + def create_case_export(self): + self.wait_to_click(self.export_case_data_link, 10) + self.wait_to_click(self.add_export_button, 200) + self.wait_to_click(self.case_type_dropdown) + self.select_by_value(self.case_type_dropdown, UserData.case_type) + self.wait_to_click(self.add_export_conf) + self.wait_to_click(self.export_settings_create) + print("Export created!!") + + def validate_exports(self): + self.wait_to_click(self.export_case_data_link, 10) + self.is_present_and_displayed(self.warning_label) + print("deprecated case type label displayed on the already created export") + + def exports_edit_data_section(self, filepath): + self.js_click(self.data_strong,100) + self.wait_to_click(self.copy_cases_menu, 200) + self.wait_for_element(self.case_type_dropdown1, 200) + dropdown = self.get_all_dropdown_options(self.case_type_dropdown1) + if 'case_dd' in dropdown: + print("Active case types are displayed in the copy cases page.") + else: + print("deprecated case types are not displayed in the copy cases page.") + self.wait_to_click(self.reassign_cases_menu, 100) + dropdown = self.get_all_dropdown_options(self.reassign_case_type) + if 'case_dd' in dropdown: + print("Active case types are displayed in the reassign cases page.") + else: + print("deprecated case types are not displayed in the reassign cases page.") + self.wait_to_click(self.deduplicate_case_link, 100) + self.wait_to_click(self.add_rule_button) + self.send_keys(self.add_rule_name, 'deduplicate_rule_1') + dropdown = self.get_all_dropdown_options(self.deduplicate_case_type) + if 'case_dd' in dropdown: + print("Active case types are displayed in the deduplicate page.") + else: + print("deprecated case types are not displayed in the deduplicate page.") + self.wait_to_click(self.import_cases_menu, 50) + time.sleep(5) + filepath = str(UserData.user_input_base_dir + "\\" + filepath) + print("File Path: ", filepath) + self.wait_for_element(self.choose_file1) + self.send_keys(self.choose_file1, filepath) + self.wait_for_element(self.next_step) + self.js_click(self.next_step) + self.is_visible_and_displayed(self.case_type_ca) + dropdown = self.get_all_dropdown_options(self.case_type_ca) + if 'case_dd' in dropdown: + print("Active case types are displayed in the import cases from excel page.") + else: + print("deprecated case types are not displayed in the import cases from excel page.") + + def messaging(self): + self.wait_to_click(self.cond_alerts) + self.wait_to_click(self.add_cond_alert) + self.send_keys(self.cond_alert_name, self.cond_alert_name_input) + self.wait_to_click(self.continue_button_basic_tab) + time.sleep(10) + self.wait_to_click(self.case_type_ca, 10) + dropdown = self.get_all_dropdown_options(self.case_type_ca) + if 'case_dd' in dropdown: + print("Active case types are displayed in the conditional alert page.") + else: + print("deprecated case types are not displayed in the conditional alert page.") + + def valid_values_2(self): + ss1 = Select(self.find_element(self.case_property_vv)) + ss1.select_by_visible_text('Date') + self.is_present_and_displayed(self.date_valid_values) + print("YYYY-MM-DD Valid values are getting displayed") + self.wait_to_click(self.save) + + def valid_values_3(self): + self.wait_to_click(self.edit_button_vv) + self.wait_to_click(self.valid_value_text, 2) + self.wait_to_clear_and_send_keys(self.valid_value_text, UserData.english_value) + self.wait_to_click(self.valid_description, 2) + self.wait_to_clear_and_send_keys(self.valid_description, UserData.english_value) + self.wait_to_click(self.done) + self.wait_to_click(self.save) + + def resetting_valid_values(self): + self.wait_to_click(self.edit_button) + self.wait_to_click(self.delete_button_vv) + ss1 = Select(self.find_element(self.case_property)) + ss1.select_by_visible_text('Plain') + self.wait_to_click(self.save) + + def excel_verification(self, download_path): + download_path = str(PathSettings.DOWNLOAD_PATH / download_path) + time.sleep(5) + excel = ExcelManager() + excel.read_excel('case_dd-vl', download_path) + print("The valid values are displayed") + + def update_excel(self, download_path): + download_path = str(PathSettings.DOWNLOAD_PATH / download_path) + excel = ExcelManager() + excel.write_excel_data('case_dd', 2, 6, UserData.plain1, download_path) + excel.write_excel_data('case_dd', 4, 6, UserData.plain1, download_path) + excel.write_excel_data('case_dd', 3, 6, UserData.lookup_function, download_path) + + def update_excel_invalid(self, download_path1): + download_path1 = str(PathSettings.DOWNLOAD_PATH / download_path1) + excel = ExcelManager() + excel.write_excel_data('case_dd-vl', 4, 2, UserData.randomvalue1, download_path1) + excel.write_excel_data('case_dd-vl', 4, 3, UserData.randomvalue1, download_path1) + + def add_property_description(self): + self.wait_to_click(self.property_description) + self.wait_to_clear_and_send_keys(self.property_description, UserData.age_property_description) + self.get_text(self.property_description) + self.wait_to_click(self.datatype) + self.wait_to_click(self.save) + + def case_management(self): + self.wait_to_click(self.registration_form) + self.wait_to_click(self.settings_icon) + description = self.get_text(self.description_text) + assert description == UserData.age_property_description + + def deprecate_case_property(self): + self.wait_to_click(self.property_deprecate) + self.wait_to_click(self.save) + + def warning_message(self): + sleep(10) + self.is_present_and_displayed(self.property_deprecate_message) + message = self.get_text(self.property_deprecate_message) + print(message) + + def restore_case_property(self): + self.js_click(self.show_deprecate, 2) + self.wait_to_click(self.restore_button) + self.wait_to_click(self.save, 2) + print("deprecated Case property is restored") + + def data_dictionary_access_page(self): + self.wait_to_click(self.view_data, 2) + self.wait_to_click(self.data_dictionary, 2) + self.wait_to_click(self.case_type_value) + if self.is_present_and_displayed(self.uploadkey): + print("both view and edit access is present for the user login") + else: + print("only view access is present for the user login") + + def data_dictionary_revoke_access(self): + self.wait_to_click(self.view_data, 2) + if self.is_present_and_displayed(self.data_dictionary): + print("access not revoked") + else: + print("Access revoked") diff --git a/Features/DataDictionary/userInputs/__init__.py b/Features/DataDictionary/userInputs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/DataDictionary/userInputs/case_dd.xlsx b/Features/DataDictionary/userInputs/case_dd.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..099a264479b2491b3bb26f7fa5217a9fb8bba973 GIT binary patch literal 8684 zcmeHs^ztgG`bqhX6lWu zm#GS^ZcGi2r(T#pSmX*zXu(++Swd-U6`)k&C9NurI(etxn_$n{=4k8bw4F6Q2cnfVGy!^l(HSbbBh81+}$ApRR7_YwOU+{PT*KmgnJz(+$~L9EgjrAIey>&=Z^oy zKKQr44oy^2>Egl)JCMB&??0bfNB~MGcs`SDrqT2dl>35P8(Tm}w$R2%4%8$KL6iw- z@xLCNUl5J`)=zV`$_tIgBM^C1=Lri-y>xkr%EI89BJBcQ=>~aBolc#l%gB2%d9=i{ zlr=!|6b6%#GnyGN zol?Aw{WL;Iad$kOu;0zhdbZrR-+|`z2dS2ZjgW1nS+=V%t(S>~L;JB*W*h#EFT3*F zesx-2ybGRDnLhg5(*XS%-lLCMUflxdU1j};1HsW$kVSZg`nQu%iH(C|;0|(*3;^K5 zE%VBO(-Z7!Zw3b2|DLxBHI={xT)>u!n!AqgS;2C-=q#mk7G<%M<@pQ#hO}C7F2P#n z)pfS&S~mwG;2;R%>@-wSi}ilg@xHa< z|N4b+95;me43mYx350hH!fG+Z=w-~%Le?oq<~CGEJ#plapU)%QedLVHhpV;CZIlFf z4^(d`K_Mszrk_Y2596SF-9iJJBW^H%B@2+8*+VK1fnjvS)Ol~m0p`GRRNX2MauQky z9B|wYxeHst7X=Xe26M|*m8vLD-6K|F^wt<@as60rJlRFYv3!bpV7zjQ#P@marihdc zPW{Q`L}EF+g?L55?4k=|#gNR+~`2$ zt74jm?Ju7TWQ8Ni(O2n^j&_`V|Mp|y(+}fsfjeg}Suf^VQ`-2)HkBcY6&kz00p^w_ zS{t3-deXWHZIwKfc2=CL%`t?!zEJG+Z$aa!NH2O7KUQezj*zN@>l1S=kdz^fKAAyM zD*eUWF7Gbis&s%`nxV9wYa)i1Z}-+rvnu`Bsb&B%??}0I{Ox}PZje|~>Mpp% z0?-iP!2Kh@{~f-61$zW|?FHBJ-@TyfN($X@U&g!*=km<-AOy|2bJFf=?ckyf)G*I8 z&^-1(St4U;GSdC5z=;SB^Ew#r^El_jSw+M@?O=sO;URhBI~<50gNKiY5z)-{V3ff! z$oP1>-*(FOFmW>6@EgSv``NMy1UE03=t;s0ifN@z8pp$U?MAp>^4Ph3q8Nk+yN@;N zftRl}c45Ia_4U*l%vKwBjgs?cC^^AiRAa{^=yIaiUVyQ!NgCFRj^}4+-#=ViDS3_9 zcNAP=ZIG8KP`=}P;v42p9o{cP6~LV6wvnIStaY&BE^#%;+&2H6?c>RT)%rl+)Drwn z|2x+4Os&Z{Aj0DuIsiZnzv9nW=VoJR>F&n)^WgqH=)FyvkDBKKh8cZw~tCO21K=)aoN6|CSADuCn*@)=9u)^VlZ=f{?h~cHPpeCrN?+&KkvIoN5_4xs|=p!rQ9oq3)Tqu1naDY0E(vl=V_X%m}|G z61=MLxcnwB+Kc_@wV!skxW*W+T;mY4!I0$5bwXu*#bx)=l+Kp8pg0veeXFRBKKQzq zUb#oM#x#V{b!8ogGH-|FGJ?>bwY{)G@SO%K^7Z($zwly zF#xI2iK6N|Q{TGZY0~LOeBB_+=oP8-K8yt#M1y=ffBy!JBgsl1y=I~fPqmDp{oe&Fkk({-^%|xFQqbCqqukTG>X|vGF)DMG z)zS@^MdyjrLrHH}lq51uk!0W&k*w>020BE~B3BVt<5ey6OJq(Gr|JSzFCJ=I5%vOw zzSJ0FtCniaq@YD5rYb< z>sk4(oO9R^6ebQKyClUk2`Q!>ej=1naWsX>Jc(k|mp zwLxxtO1k64&pY2Q*|&@78?4~Pyaw6to%Bx+b+@r}wB-D`|8&k>y!6d z?j2azsrQ;>RF*XjNMbned)HF-N|K(A9_`H96|p{8s?^uq@_ATg%!Xz58>~g_M%nkl zGN1~n2UG|PNie83OKdX11e-XCa?tMHN>q$ic@yk_jb}%-dD^3W3{nW zK)6TXB$ zm-d)E0`q$wEqeWCk)#PX`UdS4I;6EQEmW{Lk%>wB^qfJKa#sggLXQB^VGHZ9^l%TE zYNrjqzPNq(nK$w&iADcx+{0F!!&7;LoC0c6tFaL` z`r;`mo?efQyYlZZvvGiZf9zQY_}_3}u5Al&QU$E^edM{FrV{(H@pbe@yg#>5inG=C z`+PMcXY1w3`2nTRvcUt~!9y{nNU8dM(A-z%Ei0@wd1aJUUy)1n;t`y;07u;Ia(VJ) z7`m01VP->bGi&avi@@o=a|%6NJZ|mDt{>8g!tF*QdYPCN2u?P}^qA*;db`#fy_R3Z zdc(yQDc%Z|@5Q-6GA44}9hT%G9&J)~rFU`-7QtX4;W3CF|Qu*)x~H zL6o*gtyTQ*FK9b?%2^3t2{XhLI7t@~8BFC|WZUqEz>>kq2rzR`x}KcX7WOgrXr z&5RdDS9Yry;#C-&!Cgm_ASifJ?t5soZx!K>@^smuwD~}Nwc+YS!a2M*PWBiu2JX#= z%qia}*`76-4KZo1dG@^W7Iy3yTUUt8!l!>lvTvgMs{TlD{t<8|8g;a}>8gAm+Y+=oLs_0~AB@ET*(~Sa|x0TRNrO?`|;7(-$8XnAPmBHp2bHeQ74OgHJ_|I{chZ_zjQZ(-Y6^?hp)* zI+9ES=mOX4NB9f`?%>_TzY9l8B@gBy_%GM+)P(&o`TzN%dPK5B*i-qHDq*q~|6!`KxLXntb!%?q5YyoxhoMj@MnnC>IunYa7t zBBS%qe0EXV8-Z^$Q6*EvRdlU1ag2G(sm}P;3v>NYot7`HPdT|H^1gIi#ecUfBI~cTENZ2BLy7=IG z3N#;9i7R6rrLr>$U56Qx7qlKSI{HKqeuhibuB=3P%=? zKOK-!*{C#fcBbEPw}NYeLjgITZOsQx$X$82Vk@ZoK}{TB8BBzAb=R)W-y`S{)9nDj^*wLdWi8r zL;uwq)P~dfz3GkK}8uk!FdV`u(_%$*x8NK9PDcO=V<7E^aLDT zF^MKBAzTDKOG?)`g$JrHG6P6oL}ol`?6x3|1uOZ~^A!}?mc@XOHQz*uYuns{4 z{zE!T^a-IkkrMRYl)Y!_WM1LlSL^r_g#&m+pv)qlD3*ESz?DccLN5tC+g0PG+$CK3 zX-`W^y#dz5ED5=9d*qUqsVWd3?d3f5wp>l>tex`|?#ZeWBI_RWt1;u{SHkD377H8A zSw25KF0LTOnA(d%VBr}-dn|6`7**cp6t5M=*yR*NSnIUs*+SS(HD@MCADG`^rP4#w z)rL(u=Ske(j+hgLMKLlszk5__m`58s@tk-mZ2NW#5Y{?#Pe4R8 zPb=fV+is0j64jGN&H(A7|LDoOj;KZca1h?WK?wXi2u+=x|A#?1`2JjP6Q8N9!K2yF zd4T6P0-!{)c@0anU?60YXvElhT(`kvog_cSWRwm>kF4xNHY)lg;Mn11_539?^fgDA zgd$^OKlO}mQzdh^_e)O?b{;x>`8D%EiCu)ASg|K+(FA0&>ww2~Xu1&@kaW4ox-eJ= zdOV`ayR^Eni!^>7?c{1_vb}kt2Ls2VZHbkS(CCw_hdWfU;;K__r)y&B*1EnpGGPA=?rL z#Hp%fDOT*dQOuI(Bk!}KcXOU?Ib)1vtSeIYezWMxtMCg+I#E!p&_x8Y3TS^#^w01+N9TG zAbCi#;G)59|0Z|626v2-^jo%uLhIx7X$FVf>&a_!cibD%YpW+M8S4*)+EySX>(gyh zRB-G6k$n&zu){OWf1a!V^^X2J|HG7BRpFls{y72sOYrwI6;1>GZ60`E@cvZfm*@w0 z33zWta$oqL%=9l&0N^E@c>MqK)c5t=rz?MHf?)o?hxiwHd0)$YO68XpdHB2s{xtUq zmir3s7r(z0tPuWG@T(NQFM7X#_$4|6=U?IXcDr9r+}H5W?DB>V z?v?+3bJly#I&00d&)NHZ_H*{$$`B-Ed;kD|3YdHj(~;^`kV$~w4Z{}*zKm^+lTPchxV05r!0#{m9ooiD<$#bzJ1Vb~LMp>N#Y8b5juMhT+g>Bs2`be`` zL!QvpBugH#u3K|)_C6Gup}rOx9QY9WVcnyYW6>wPf&@F4f*h$c+j{n000$=T5UjR* zYl>CM)M+$P%%zfFg{~8(1?I1KN>TKc3>~!TPAe3K2jNR%Lb(f58zfN6*ch<=VT{uH zpn*{N4;KzMEpgEi0Dx=w!uh8QMz;3QpFRY{h+1~CV+P#G^$W(tF*9+MVlT{@2ehM; zHWKSv55v0VSY_{S)0h=>HHj`^7a2mb!LktO&f#$NBmJ@Ogw3BwmGbmyZ$Qi_hxSoY zm&|mjW;VK}62S(wNKEUsb5!{LU;WxC<53ZTh8Cu;IjIuTV$N5#ak6L(BY^K$s7CF1 zO_BK5Y#)V>r0q6msOir__4zhn5`1q<4VHUX)fO))NkY&}>BZT$hC*Q^g`!ih<*k-d$65?i*`%Lz?@Aeu#jNRu@SlO(%BQWXL;(QQNC5ymc;H;E z*c{EE*3jQ)j-R31(^iODkRWQIKBACnsg1;YK3tkw@@zggzpRhEUX0H1*-(b~bYm;%JuR`v~G*qFzc3mCjlyJnsP4D2G!UdRPkN3yJ#0 z=T&o?E*TvXFs0aB%9CBDNTfgMjHFY%rZ%k{MCR`*w)RQG5hjVg84$ySpzwUrWVz8g zpMiRacB!M3NX?E4q3DaUr%rM4<6si{D=shAeXd$D7`-Zkxi`P8-DJLm0(NfnEJwD&^5x_2&Hv=%dsf}x22-1k?B&iWXon2;_4D|p?P*-<&2!Z7rvYqaedh9qj(9sVf*W^bG6gixPzAh*8|~6{C{0!3f=6@vJH#m zA!T~sUPgV4%Je?D$`lshY^y~f-#$c$2$taK$N92}GiL%434~eU`++8LQLqrq0q5#k zh)Zd&Ogfxo=G|P7!2rC&?bDaCvwq6*1l)kKAQiN_LB1WkSKv)|MhK57$pe$s*M)26 z975p!{R~uV+f)%cHM~I^899r6j^JllGV=Ix2S;dBz{#2*b1&vsf0CdZ^~tbC~? z%#_g+aofBj+4KTqIInoo8az3=uKE{5f9EZ=CX`Cdr%$Cl0Xr z(Ly}Bx^QU5nb6h$NH5@q;@Q$;frty#_3N9Ko0fx?Fwfe;#M!-?L7?T1!6FCkn7b0}eA?!@arHdKG+aS2HJJT2&xxJlm+0M#5Y_@|=INHV{W+t=8?f(*I*^T=^yT);( z(LG|X{dG-EvP+bgUrhWN!41)=^T{rW3fgG+^;@bevBf7x0|^G|?foynl1_$+{&nAF zSy;~b1R7s{k1he9?V>LlOvd-{rNWvU^K(-$j4X}f9~T&6f{60?zSPtyQw6cc`%fKU z5Rq|eXA$~V%4mo&d#w5f#i7s=p*DsIqg1IWVh}&~d zrToZ*G3`i2mq%CcV#aHV&hAoVix6zX__ij^_Q6|SkSPOb@GF8jAL&ajUESSDE^dq>3F8ZefW0~8!q!iza`$q*51;=3<`C0VEc9XrSl2# z%Qo-X@p}wVjsVHgU~?wlsoaQ~Fj!cW@Aj59&WNW#T^XJcK!fLeCY}BZ5plNPmlNxd$1zT5LDM$1lu0%UOYlbiw#rkMwaP}4+l>T zUYZ}*^$VG!3mAYjcJ3wcH*}W~$t{w5`o`s6x(9LFMGnwgdOeg3 zL|=d6At;oz;3{m_=wV@4a;c;_Z$iA>tAfJ4SLdmAOGq@z3)ycn{#17~T0k*D5!(-Y z<<;8ut&&SKLDDp=Zh!gIf5n#fdUE?dGtD*-QzkyUIUMk@eS7UgT7LD2Cs@#Y*)BjADz z#>?&eRK42*IBnzo#R>S?8#(@^nB4yIB{fbKk7z@Z)`dZ6Rxe>rRn>SAGo|<^^SO`# z-I;)7x2isO_rcGgH^$c{AxWc%@09?4c%eOHQq$aVwxwHXH2N7dQB(rHI^6SJ^~!du>6pSu=d3dae0>Wt=QM zg)4_+q|oJQe{miL^DyQy{ ziDZ8A*p^A&gKCXZC1;Z{oAi+hnuTJWoU&3klE+?bY65Oh`*cnD&@Z8NDGXFf?X zUYnD9b?cE7W?|E=d$vW7{)-!?^yQ{HtdB}`j-63WpL+5ZXWQkQRwpB07&ZmetRjMp zB|##{9eT<@V@dL%GvS%)E+kZAPq;u3sJR>u6fY`9 z8NYcXe!bz5nBM9ze|87k;Q5nwwbDDGZE(6hf>RFzel@U%S~;+>{yeA0LTnH@Fh$C6 zp$77wx)3>v=gP%{G3r7#b5goujJi(GBj(yD&2S)h85t~P*x)Q@GKYmcs~+|)y?IUU z5r|sx_TGFJxj?5{=Hb{0ikyN9WfWd9>jgYz%i4`*(i*)Zo{)ww6CL zEJ8(JsY3+xgK4Fzm!2|Aaidg4RJU5XJf}i zxndbdo4cdz0Y7zh#Pad}E?)jA$58 zDN8jbI9Tq*XnJp474sSAiQw!8O7hmKltkp-oeh#q z>Ux2*7qy*SJP(-Vu#D~FkYm4XG&3*|d7<0*z#X}eY`FD8<`W#d0F!t^yLI6!MI z@g-v^NLqnJfEormlEA&$J3Pzgx1g0GPiNJ1>hoprE#jZMz_qj1Py&zs4SXj^e(%C> z#{F|IezC7QMi9#n-Y%BmeghB{OnpGiXDucm4EDXk@sX$~>ewS~2gp2bVAmc^Y`rix zWdnzLJXe{0j(cK8HvsM3B?sg_CnJ!YS;eP|ILm=idSWPUiB_YUlP$MX5Lfm^%B!$o z-BEqX-{KZ3@rvR-!e)P4Oi>_i%)t0fcU`Z;>ik)|PAq+N9{rBqb){nNyFpwWHlTS_ z)mEIhd!BHR{@1-1_1vSMc!N!+L+yczB>$lG?Z;NrCt*BywUdk@?p=O*uxhpa1DIF+TM(lwq7up($(4Jx zkIp^|bo8-p4qD3qp4L#+M~(_pjr#C$655b8*vc8U+>s=Gy?tMMkA$9`SVl-yFx4s3 zs`{n-yZG=xD<-^VnO^hkD#R@_guy`)(*{f@Yy9t7=aCS(aJ1YW;JY*y>G5s?@KN-u zA4|hFhSBvh>r>X~IP=xW-ka%GTSZ}gV$pqieCz(kd-G5Im)<0$7yV3{!dvxtC*kMzH1BXHLI`n`ik`2SBz?xXLI zC4OT8fOiO;|3v@akm5f4eslge{0IE;|I(`8H*ml7`>z3?g4ZYTXZ|x~?}P8>`QP9ztY6^&k@ffO i+)t&y?To-h{9h7F8G-`*`6o2^Aq1CtlAp6Ez<&Tv4Y!K` literal 0 HcmV?d00001 diff --git a/Features/DataDictionary/userInputs/user_inputs.py b/Features/DataDictionary/userInputs/user_inputs.py new file mode 100644 index 000000000..c42bfffa6 --- /dev/null +++ b/Features/DataDictionary/userInputs/user_inputs.py @@ -0,0 +1,32 @@ +""""Contains test data that are used as user inputs across various areasn in CCHQ""" +import os +import random +import string + +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings + + +class UserData: + + + """User Test Data""" + user_input_base_dir = os.path.dirname(os.path.abspath(__file__)) + application = "Data_Dictionary" + application_description = 'dd' + str(fetch_random_string()) + case_properties = 'property'+ str(fetch_random_string()) + name_group = 'group'+ str(fetch_random_string()) + case_type = 'case_dd' + case_data_link = 'https://staging.commcarehq.org/a/qa-automation/reports/case_data/d57fa5c7fb184d219e7fe155dabdbb6a/' + english_value ='English' + plain1 ='Plain' + randomvalue1 = 'dyfuyvh' + date1 = 'Date' + number ='Number' + updated_input = 'English' + age_property_description = 'Testing the age property description' + model_value = 'case' + data_upload_path = "import_file.xlsx" + p1p2_user = "p1p2.web.user@gmail.com" + file = os.path.abspath(os.path.join(user_input_base_dir, "case_dd.xlsx")) + lookup_function = '=(F2)' diff --git a/Features/Powerbi_integration_exports/README.md b/Features/Powerbi_integration_exports/README.md new file mode 100644 index 000000000..6e43f04c9 --- /dev/null +++ b/Features/Powerbi_integration_exports/README.md @@ -0,0 +1,57 @@ +## Commcare Powerbi Integration exports Test Script + +These tests ensure that the [Powerbi_integration_exports] (https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143945995/Microsoft+Power+BI+Integration)features work as expected and that there are no regressions +The automated tests comprises of [these powerbi_integration_exports functionality.] (https://docs.google.com/spreadsheets/d/1wlW7gvmz8aW1gPLurHgD5onU1_C6laUhUtzQfmi8H9Y/edit?gid=0#gid=0) +## Executing Scripts + +### On Local Machine + +#### Setting up test environment + +```sh + +# create and activate a virtualenv using your preferred method. Example: +python -m venv venv +source venv/bin/activate + + +# install requirements +pip install -r requires.txt + +``` + +[More on setting up virtual environments](https://confluence.dimagi.com/display/GTD/QA+and+Python+Virtual+Environments) + + +#### Running Tests + + + - Copy `settings-sample.cfg` to `settings.cfg` and populate `settings.cfg` for +the environment you want to test. +- Run tests using pytest command like: + +```sh + +# To execute all the test cases +pytest -v --rootdir= Features/Lookuptable/testCases + +``` +- You could also pass the following arguments + - ` -n 3 --dist=loadfile` - This will run the tests parallelly in 3 instances. The number of reruns is configurable. + - ` --reruns 1` - This will re-run the tests once in case of failures.The number of reruns is configurable too. + +### Trigger Manually on Gitaction + +clone this repository + +To manually trigger the script, + - Go to [Powerbi_integration_exports action](https://github.com/dimagi/dimagi-qa/actions/lookuptable.yml) + - Run workflow + - Use workflow from ```master``` + - Run! + +If you are a part of the QA team, you'll receive emails for the result of the run after it's complete. + +clone this repository + +Besides, you should be able to find the zipped results in the **Artifacts** section, of the corresponding run (after it's complete). diff --git a/Features/Powerbi_integration_exports/__init__.py b/Features/Powerbi_integration_exports/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/Powerbi_integration_exports/requires.txt b/Features/Powerbi_integration_exports/requires.txt new file mode 100644 index 000000000..826c8961f --- /dev/null +++ b/Features/Powerbi_integration_exports/requires.txt @@ -0,0 +1,14 @@ +## Stores information about all the libraries, modules, and packages that are used in this project. + + +pandas>=1.2.2 +pytest>=6.2.5 +pytest-html>=3.1.1 +selenium == 4.11.2 +openpyxl>=3.0.6 +pytest-rerunfailures>=10.2 +pytest-xdist>=2.4.0 +pytest-xdist[psutil] +pyotp >=2.6.0 +pytest-order +py \ No newline at end of file diff --git a/Features/Powerbi_integration_exports/settings-sample.cfg b/Features/Powerbi_integration_exports/settings-sample.cfg new file mode 100644 index 000000000..b6bb402bb --- /dev/null +++ b/Features/Powerbi_integration_exports/settings-sample.cfg @@ -0,0 +1,12 @@ +[default] +# This is the environment url of commcare +url = https://www.commcarehq.org/ +# Login username of the webuser +login_username = +# Login password of the webuser +login_password = +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + diff --git a/Features/Powerbi_integration_exports/settings.cfg b/Features/Powerbi_integration_exports/settings.cfg new file mode 100644 index 000000000..d150a7436 --- /dev/null +++ b/Features/Powerbi_integration_exports/settings.cfg @@ -0,0 +1,14 @@ +[default] +# This is the environment url of commcare +url = https://staging.commcarehq.org/ +# Login username of the webuser +login_username = automation.user.commcarehq@gmail.com +# Login password of the webuser +login_password = pass@123 +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + + + diff --git a/Features/Powerbi_integration_exports/testCases/__init__.py b/Features/Powerbi_integration_exports/testCases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/Powerbi_integration_exports/testCases/conftest.py b/Features/Powerbi_integration_exports/testCases/conftest.py new file mode 100644 index 000000000..b9aa46ac7 --- /dev/null +++ b/Features/Powerbi_integration_exports/testCases/conftest.py @@ -0,0 +1,73 @@ +import os + +from configparser import ConfigParser +from pathlib import Path +from common_utilities.fixtures import * + +""""This file provides fixture functions for driver initialization""" + +global driver + + +@pytest.fixture(scope="session") +def environment_settings_lookup(): + """Load settings from os.environ + + Names of environment variables: + DIMAGIQA_URL + DIMAGIQA_LOGIN_USERNAME + DIMAGIQA_LOGIN_PASSWORD + DIMAGIQA_MAIL_USERNAME + DIMAGIQA_MAIL_PASSWORD + + See https://docs.github.com/en/actions/reference/encrypted-secrets + for instructions on how to set them. + """ + settings = {} + for name in ["url", "login_username", "login_password", "mail_username", + "mail_password", "staging_auth_key", "prod_auth_key"]: + + var = f"DIMAGIQA_{name.upper()}" + if var in os.environ: + settings[name] = os.environ[var] + if "url" not in settings: + env = os.environ.get("DIMAGIQA_ENV") or "staging" + subdomain = "www" if env == "production" else env + # updates the url with the project domain while testing in CI + project = "a/qa-automation-prod" if env == "production" else "a/qa-automation" + settings["url"] = f"https://{subdomain}.commcarehq.org/{project}" + return settings + + +@pytest.fixture(scope="session", autouse=True) +def settings(environment_settings_lookup): + if os.environ.get("CI") == "true": + settings = environment_settings_lookup + settings["CI"] = "true" + if any(x not in settings for x in ["url", "login_username", "login_password", + "mail_username", "mail_password", "staging_auth_key", + "prod_auth_key"]): + lines = environment_settings_lookup.__doc__.splitlines() + vars_ = "\n ".join(line.strip() for line in lines if "DIMAGIQA_" in line) + raise RuntimeError( + f"Environment variables not set:\n {vars_}\n\n" + "See https://docs.github.com/en/actions/reference/encrypted-secrets " + "for instructions on how to set them." + ) + return settings + path = Path(__file__).parent.parent / "settings.cfg" + if not path.exists(): + raise RuntimeError( + f"Not found: {path}\n\n" + "Copy settings-sample.cfg to settings.cfg and populate " + "it with values for the environment you want to test." + ) + settings = ConfigParser() + settings.read(path) + # updates the url with the project domain while testing in local + if settings["default"]["url"] == "https://www.commcarehq.org/": + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation-prod" + else: + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation" + return settings["default"] + diff --git a/Features/Powerbi_integration_exports/testCases/report.html b/Features/Powerbi_integration_exports/testCases/report.html new file mode 100644 index 000000000..33b554b7b --- /dev/null +++ b/Features/Powerbi_integration_exports/testCases/report.html @@ -0,0 +1,1091 @@ + + + + + report.html + + + + +

report.html

+

Report generated on 13-Aug-2025 at 18:18:35 by pytest-html + v4.1.1

+
+

Environment

+
+
+ + + + + +
+
+

Summary

+
+
+

1 test took 00:02:17.

+

(Un)check the boxes to filter the results.

+
+ +
+
+
+
+ + 0 Failed, + + 1 Passed, + + 0 Skipped, + + 0 Expected failures, + + 0 Unexpected passes, + + 0 Errors, + + 0 Reruns +
+
+  /  +
+
+
+
+
+
+
+
+ + + + + + + + + +
ResultTagsTestDuration
+ +
+
+ +
+ \ No newline at end of file diff --git a/Features/Powerbi_integration_exports/testCases/test.py b/Features/Powerbi_integration_exports/testCases/test.py new file mode 100644 index 000000000..f296d4f22 --- /dev/null +++ b/Features/Powerbi_integration_exports/testCases/test.py @@ -0,0 +1,188 @@ +import pytest + +from Features.Powerbi_integration_exports.userInputs.user_inputs import UserData +from HQSmokeTests.testPages.data.export_data_page import ExportDataPage +from HQSmokeTests.testPages.home.home_page import HomePage +from Features.Powerbi_integration_exports.testPages.data.power_bi_page import PowerBiPage + +""""Contains test cases related to the Data module""" + +values = dict() + +@pytest.mark.lookup +def test_01_Odata_Feed_1(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + +def test_02_Odata_Feed_2(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_feed_type() + + +def test_03_Odata_Feed_3(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + +def test_04_Odata_Feed_4(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.application_dropdown(UserData.reassign_cases_application) + +def test_05_Odata_Feed_5(driver, settings): + home = (HomePage(driver, settings)) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.application_dropdown(UserData.reassign_cases_application) + data.menu_dropdown() + +def test_06_Odata_Feed_6(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.cancel() + +def test_07_Odata_Feed_07(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.save_odata_feed() + data.delete1() + +def test_08_Odata_Feed_08(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + +def test_09_Odata_Feed_09(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application, UserData.reassign_menu, UserData.reassign_form) + data.add_odata() + data.show_advance_question() + + +def test_10_Odata_Feed_10(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_case() + data.case_feed(UserData.reassign_case) + data.add_odata() + data.save_odata_feed() + data.delete1() + + + +def test_11_Odata_Feed_11(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.save_odata_feed() + data.copy_edit_feed() + + +def test_12_Odata_Feed_12(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.odata_20() + + +def test_13_Odata_Feed_13(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.create_multiple_odatafeed(10) + data.go_to_page() + data.power_bi_tableau_integration_bulk_delete() + +def test_14_Odata_Feed_14(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.delete_questions() + +def test_15_Odata_Feed_15(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.de_identified() + +def test_16_Odata_Feed_16(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + username = settings["login_username"] + password = settings["login_password"] + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.odata_20() + data.view_odata_feed(username, password) + +def test_17_Odata_Feed_17(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.Basic_tests_application,UserData.Basic_menu,UserData.Repeat_form) + data.add_odata() + data.repeat_checkbox2() + data.save_odata_feed() + data.edit_filters() + + +def test_18_Odata_Feed_18(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_case() + data.case_feed(UserData.parent) + data.add_odata() + data.parent_checkbox1() + data.save_odata_feed() \ No newline at end of file diff --git a/Features/Powerbi_integration_exports/testPages/__init__.py b/Features/Powerbi_integration_exports/testPages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/Powerbi_integration_exports/testPages/data/__init__.py b/Features/Powerbi_integration_exports/testPages/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py b/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py new file mode 100644 index 000000000..466bc5f82 --- /dev/null +++ b/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py @@ -0,0 +1,274 @@ +import os.path +import time +from time import sleep + +from selenium.common import TimeoutException, ElementClickInterceptedException +from selenium.webdriver import Keys +from selenium.webdriver.common.by import By +from selenium.webdriver.support.select import Select + +from common_utilities.selenium.base_page import BasePage +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings +from Features.Powerbi_integration_exports.userInputs.user_inputs import UserData + + +""""Contains test page elements and functions related to the Lookup Table module""" + +class PowerBiPage(BasePage): + + def __init__(self, driver): + super().__init__(driver) + self.Data = (By.LINK_TEXT, "Data") + self.view_all = (By.LINK_TEXT, "View All") + self.power_bi = (By.XPATH, "//*[@id='hq-sidebar']/nav/ul[1]/li[7]/a") + self.add_odata_feed = (By.XPATH, "//*[@id='create-export']/p/a") + self.disabled = (By.XPATH,"//*[@data-bind='visible: showSubmit, disable: disableSubmit']") + self.feed_type = (By.XPATH,"//select[@id='id_model_type']") + self.feed_type_visible = (By.XPATH,"//*[@data-bind='visible: modelType()']") + self.case_type = (By.XPATH,"//*[@for='id_case_type']") + self.cancel_button = (By.XPATH,"//*[@id='createExportOptionsModal']/div/form/div/div[7]/button[1]") + self.applications = (By.XPATH,"//select[contains(@name,'application')]") + self.app_dropdown = (By.XPATH,"//input[contains(@aria-controls,'id_application-results')]") + self.users_list_item = "//ul[@role='listbox']/li[contains(.,'{}')]" + self.menu =(By.XPATH,"//select[contains(@name,'module')]") + self.menu_select = (By.XPATH,"//input[@aria-controls='select2-id_module-results']") + self.form = (By.XPATH,"//select[contains(@placeholder,'Select Form')]") + self.add_export_conf = (By.XPATH, "//button[@data-bind='visible: showSubmit, disable: disableSubmit']") + self.submission_msg = (By.XPATH, "//span[contains(@data-bind,'text: submissionCountText')]") + self.case = (By.XPATH,"//span[@aria-controls='select2-id_case_type-container']") + self.case_select = (By.XPATH,"//input[@aria-controls='select2-id_case_type-results']") + self.save = (By.XPATH,"//button[@type='submit'][contains(@data-bind,'click: save')]") + self.settings = (By.XPATH, "//*[@id='customize-export']/header/div/div/h3") + self.success = (By.XPATH,"//*[@class='alert alert-margin-top fade in alert-success']") + self.odata_feed_name = (By.XPATH,"//input[@id='export-name']") + self.powerBI_tab_int = (By.LINK_TEXT, "PowerBi/Tableau Integration") + self.select_all_btn = (By.XPATH, '//button[@data-bind="click: selectAll"]') + self.delete_selected_exports = (By.XPATH, '//a[@href= "#bulk-delete-export-modal"]') + self.bulk_delete_confirmation_btn = (By.XPATH, '//button[@data-bind="click: BulkExportDelete"]') + self.show_advance = (By.XPATH, "//span[@data-bind='visible: !table.showAdvanced()']") + self.hide_advance = (By.XPATH,"//span[@data-bind='visible: table.showAdvanced']") + self.show_delete = (By.XPATH,"//span[@data-bind='visible: !table.showDeleted()']") + self.copy_edit_button = (By.XPATH,"(//*[@data-bind='if: isOData()'])[1]") + self.description = (By.XPATH,("//*[@id='export-description']")) + self.disabled1 =(By.XPATH,("//td[5][./input[@disabled]]//preceding-sibling::td//input[@type='checkbox' and @disabled='disabled']")) + self.copy_odata_feed = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div/span/a")) + self.link = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div/input")) + self.delete = (By.XPATH,("(//a[@data-bs-toggle='modal'])[9]")) + self.delete_button = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[5]/div/a")) + self.edit_filter = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[3]/a[2]")) + self.daterange =(By.XPATH,("//*[@id='id_date_range']")) + self.save_filter = (By.XPATH,("//*[@id='setFeedFiltersModal']/div/form/div/div[3]/button[2]")) + self.pagination = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/pagination/div/div/div[2]/nav/ul/li[3]/a")) + self.delete_question = (By.XPATH,("//span[@data-bind='visible: !table.showDeleted()']")) + self.process = (By.XPATH, "//*[@id='export-process-deleted-applications']/div/div/div[3]/button[2]") + self.refresh_page = (By.XPATH, "//*[@id='export-process-deleted-applications']/div/div/div[3]/button[2]") + self.hide_delete_question = (By.XPATH,"//*[@data-bind='visible: table.showDeleted()']") + self.allow_sensitive = (By.XPATH, "//*[@id='customize-export']/form/fieldset[3]/button") + self.de_identified1 = (By.XPATH,"//*[@id='is_deidentified']") + self.label = (By.XPATH,"/html/body/div[1]/div[4]/div/div[2]/div[4]/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/h4/label") + self.check_data = (By.XPATH, "//*[contains(text(), '@odata.context')]") + self.copy_odata_link = (By.XPATH,"//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div[2]/button" ) + self.copy_odata_link_form = (By.XPATH,"(//*[contains(@class,'form-control input-sm')])[1]") + self.copy_odata_link_btn_form = ( + By.XPATH, + "//div[contains(@data-bind,'text-body-secondary')][.//span[text()='"+ UserData.name +"']]//following-sibling::div[contains(@data-bind,'showLink')]//button") + self.repeat_checkbox = (By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[2]/legend/span[1]/input") + self.parent_checkbox = (By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[3]/legend/span[1]/input") + self.case_id_duplicate =(By.XPATH,"//body[1]/div[1]/div[3]/div[1]/div[2]/div[2]/form[1]/fieldset[2]/div[1]/div[1]/table[1]/tbody[1]/tr[9]/td[1]/input[1]") + self.repeat_checkbox1 =(By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[4]/legend/span[1]/input") + + def ui(self): + self.wait_to_click(self.power_bi,2) + self.power_bi_tableau_integration_bulk_delete() + self.wait_to_click(self.add_odata_feed) + + def select_feed_type(self): + self.is_present_and_displayed(self.disabled,10) + print("Add OData Feed button is disabled") + + def select_form(self): + self.wait_to_click(self.feed_type) + ss = Select(self.find_element(self.feed_type)) + ss.select_by_visible_text('Form') + self.is_present_and_displayed(self.feed_type_visible) + print("App type, application,Menu , form are displayed") + + def application_dropdown(self,selectapp): + self.wait_to_click(self.applications) + app_dropdown=[] + app_dropdown=(self.find_elements_texts(self.applications)) + time.sleep(10) + print(app_dropdown) + self.wait_to_click(self.applications) + self.select_by_text(self.applications,selectapp) + + def menu_dropdown(self): + self.wait_to_click(self.menu) + menu_dropdown=[] + menu_dropdown=(self.find_elements_texts(self.menu_select)) + time.sleep(10) + print(menu_dropdown) + + def select_case(self): + self.wait_to_click(self.feed_type,10) + ss = Select(self.find_element(self.feed_type)) + # Select option by visible text + ss.select_by_visible_text('Case') + self.is_present_and_displayed(self.case_type,15) + print("case type field displayed") + + def cancel(self): + time.sleep(10) + self.wait_to_click(self.cancel_button,10) + + def form_feed(self,appselect,menu_select,form_select): + self.wait_to_click(self.applications) + self.select_by_text(self.applications,appselect) + self.wait_to_click(self.menu) + self.select_by_text(self.menu,menu_select) + self.wait_to_click(self.form) + self.select_by_text(self.form,form_select) + self.is_present_and_displayed(self.submission_msg) + print("Form submission message displayed") + + def add_odata(self): + self.wait_to_click(self.add_export_conf) + self.is_present_and_displayed(self.settings,10) + print ("odata feed settings page displayed") + self.wait_to_click(self.odata_feed_name) + self.wait_to_clear_and_send_keys(self.odata_feed_name,UserData.name+Keys.TAB) + self.wait_to_click(self.case_id_duplicate) + self.scroll_to_bottom() + self.is_present_and_displayed(self.save,10) + + def repeat_checkbox2(self): + self.scroll_to_element(self.repeat_checkbox) + self.wait_to_click(self.repeat_checkbox) + self.scroll_to_element(self.repeat_checkbox1) + self.wait_to_click(self.repeat_checkbox1) + + def parent_checkbox1(self): + self.scroll_to_element(self.parent_checkbox) + self.js_click(self.parent_checkbox,2) + + + def delete_bulk_exports(self): + try: + self.wait_to_click(self.select_all_btn) + self.wait_to_click(self.delete_selected_exports) + self.wait_to_click(self.bulk_delete_confirmation_btn) + time.sleep(10) + except TimeoutException: + print("No exports available") + + + def power_bi_tableau_integration_bulk_delete(self): + try: + self.wait_and_sleep_to_click(self.powerBI_tab_int) + except ElementClickInterceptedException: + self.js_click(self.powerBI_tab_int) + self.delete_bulk_exports() + + def case_feed(self,select_case): + self.wait_to_click(self.case,10) + self.wait_for_element(self.case_select) + self.send_keys(self.case_select, select_case) + self.wait_to_click((By.XPATH, self.users_list_item.format(select_case))) + + def save_odata_feed(self): + self.js_click(self.save) + time.sleep(2) + self.is_present_and_displayed(self.success,10) + print("odata feed is created") + + def show_advance_question(self): + self.wait_to_click(self.show_advance) + self.wait_to_click(self.hide_advance) + self.is_present_and_displayed(self.show_advance) + self.is_present_and_displayed(self.show_delete) + print("show advance and show delete questions buttons are displayed") + + def copy_edit_feed(self): + self.wait_to_click(self.copy_edit_button,20) + self.wait_to_click(self.odata_feed_name) + self.wait_to_clear_and_send_keys(self.odata_feed_name,UserData.updatedname) + self.wait_to_click(self.save) + + def odata_20(self): + self.wait_to_click(self.description,5) + self.send_keys(self.description, UserData.description+Keys.TAB) + assert self.is_present_and_displayed(self.disabled1,10) + print("disabled button present") + self.scroll_to_bottom() + time.sleep(2) + self.js_click(self.save) + print("clicked on saved button") + self.is_present_and_displayed(self.success, 10) + + + + def delete1(self): + self.wait_to_click(self.delete) + self.wait_to_click(self.delete_button) + + def edit_filters(self): + self.wait_to_click(self.edit_filter,2) + self.wait_to_click(self.daterange, 5) + ss = Select(self.find_element(self.daterange)) + # Select option by visible text + ss.select_by_visible_text('Last year') + self.wait_to_click(self.save_filter,10) + self.wait_to_click(self.edit_filter) + print("Data range values", self.daterange) + + def create_multiple_odatafeed(self,no_of_feeds): + for i in range(1,no_of_feeds): + self.ui() + self.select_form() + self.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + self.add_odata() + self.save_odata_feed() + + + def go_to_page(self): + self.wait_to_click(self.pagination,10) + print("pagination working fine") + + def delete_questions(self): + self.wait_to_click(self.delete_question) + self.is_present_and_displayed(self.hide_delete_question) + print("Deleted questions become included in the question list for form feeds") + + def de_identified(self): + self.scroll_to_bottom() + self.js_click(self.allow_sensitive,2) + self.js_click(self.de_identified1,2) + self.js_click(self.save,10) + self.is_present_and_displayed(self.label) + print("odatafeed marked as de-identified") + + def view_odata_feed(self,username,password): + self.driver.refresh() + time.sleep(10) + print(self.copy_odata_link_btn_form) + self.wait_and_sleep_to_click(self.copy_odata_link_btn_form,40) + self.get_url_paste_browser(username, password, 'forms') + self.assert_odata_feed_data() + + def get_url_paste_browser(self, username, password, item): + global odata_feed_link1 + if item == 'forms': + odata_feed_link1 = self.wait_to_get_value(self.copy_odata_link_form) + print("===="+odata_feed_link1) + final_URL_case = f"https://{username}:{password}@{odata_feed_link1[8:]}" + print("--------"+final_URL_case) + time.sleep(10) + self.driver.get(final_URL_case) + + def assert_odata_feed_data(self): + odata_feed_data = self.driver.page_source + verify_data = self.find_elements(self.check_data) + assert len(verify_data) > 0, "Odata feed is Empty " + # self.driver.close() # Close the feed URL + self.driver.back() \ No newline at end of file diff --git a/Features/Powerbi_integration_exports/userInputs/__init__.py b/Features/Powerbi_integration_exports/userInputs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/Powerbi_integration_exports/userInputs/user_inputs.py b/Features/Powerbi_integration_exports/userInputs/user_inputs.py new file mode 100644 index 000000000..2cdaeee9e --- /dev/null +++ b/Features/Powerbi_integration_exports/userInputs/user_inputs.py @@ -0,0 +1,28 @@ +""""Contains test data that are used as user inputs across various areasn in CCHQ""" +import os +import random +import string + +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings + + +class UserData: + + + """User Test Data""" + user_input_base_dir = os.path.dirname(os.path.abspath(__file__)) + # Pre-setup application and case names + field_val = 'table'+ str(fetch_random_string()) + reassign_cases_application = 'Reassign Cases' + reassign_menu = 'Case List' + reassign_form = 'Registration Form' + reassign_case = 'reassign' + name = 'powerbi_odatafeed1' + updatedname = 'updated_powerbi_odatafeed1' + description = 'testing the powerbi form report' + Basic_tests_application = 'Basic Tests' + Basic_menu = 'Formplayer Specific Tests' + Repeat_form = '[Formplayer] Repeats' + parent = 'form_linking_child' + odata_feed_form = '' \ No newline at end of file From 1cbed672713276f2e576bb9b1add09b546eaa89a Mon Sep 17 00:00:00 2001 From: sameena1415 <110610899+sameena1415@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:35:48 +0530 Subject: [PATCH 02/14] Update user_inputs.py Powerbi userinputs file updated after running on production. --- .../Powerbi_integration_exports/userInputs/user_inputs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Features/Powerbi_integration_exports/userInputs/user_inputs.py b/Features/Powerbi_integration_exports/userInputs/user_inputs.py index 2cdaeee9e..3db676486 100644 --- a/Features/Powerbi_integration_exports/userInputs/user_inputs.py +++ b/Features/Powerbi_integration_exports/userInputs/user_inputs.py @@ -24,5 +24,6 @@ class UserData: Basic_tests_application = 'Basic Tests' Basic_menu = 'Formplayer Specific Tests' Repeat_form = '[Formplayer] Repeats' - parent = 'form_linking_child' - odata_feed_form = '' \ No newline at end of file + parent = 'sub_case_one' + odata_feed_form = '' + case='case' From bfe937b5b09ba385bf3434a11ff386fb2db3f39f Mon Sep 17 00:00:00 2001 From: sameena1415 <110610899+sameena1415@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:38:35 +0530 Subject: [PATCH 03/14] Update power_bi_page.py Added wait time for the popup to display --- .../testPages/data/power_bi_page.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py b/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py index 466bc5f82..e26260a5d 100644 --- a/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py +++ b/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py @@ -88,7 +88,7 @@ def select_feed_type(self): print("Add OData Feed button is disabled") def select_form(self): - self.wait_to_click(self.feed_type) + self.wait_to_click(self.feed_type,40) ss = Select(self.find_element(self.feed_type)) ss.select_by_visible_text('Form') self.is_present_and_displayed(self.feed_type_visible) @@ -111,7 +111,7 @@ def menu_dropdown(self): print(menu_dropdown) def select_case(self): - self.wait_to_click(self.feed_type,10) + self.wait_to_click(self.feed_type,40) ss = Select(self.find_element(self.feed_type)) # Select option by visible text ss.select_by_visible_text('Case') @@ -271,4 +271,4 @@ def assert_odata_feed_data(self): verify_data = self.find_elements(self.check_data) assert len(verify_data) > 0, "Odata feed is Empty " # self.driver.close() # Close the feed URL - self.driver.back() \ No newline at end of file + self.driver.back() From 2169efd362d8fe39031bd44cd062df63713e5532 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Sep 2025 11:39:59 +0530 Subject: [PATCH 04/14] changes to the data dictionary code --- Features/FIND_BY_ID/README.md | 57 + Features/FIND_BY_ID/__init__.py | 0 Features/FIND_BY_ID/requires.txt | 14 + Features/FIND_BY_ID/settings-sample.cfg | 12 + Features/FIND_BY_ID/settings.cfg | 14 + Features/FIND_BY_ID/testCases/__init__.py | 0 Features/FIND_BY_ID/testCases/conftest.py | 73 ++ Features/FIND_BY_ID/testCases/report.html | 1091 +++++++++++++++++ Features/FIND_BY_ID/testCases/test.py | 188 +++ Features/FIND_BY_ID/testPages/__init__.py | 0 .../FIND_BY_ID/testPages/data/Data_page.py | 274 +++++ .../FIND_BY_ID/testPages/data/__init__.py | 0 Features/FIND_BY_ID/userInputs/__init__.py | 0 Features/FIND_BY_ID/userInputs/user_inputs.py | 29 + Features/GenericScript/README.md | 57 + Features/GenericScript/__init__.py | 0 Features/GenericScript/requires.txt | 14 + Features/GenericScript/settings-sample.cfg | 12 + Features/GenericScript/settings.cfg | 14 + Features/GenericScript/testCases/__init__.py | 0 Features/GenericScript/testCases/conftest.py | 73 ++ Features/GenericScript/testCases/report.html | 1091 +++++++++++++++++ Features/GenericScript/testCases/test.py | 26 + Features/GenericScript/testPages/__init__.py | 0 .../GenericScript/testPages/data/__init__.py | 0 .../testPages/data/data_generator_page.py | 44 + Features/GenericScript/userInputs/__init__.py | 0 .../GenericScript/userInputs/user_inputs.py | 32 + 28 files changed, 3115 insertions(+) create mode 100644 Features/FIND_BY_ID/README.md create mode 100644 Features/FIND_BY_ID/__init__.py create mode 100644 Features/FIND_BY_ID/requires.txt create mode 100644 Features/FIND_BY_ID/settings-sample.cfg create mode 100644 Features/FIND_BY_ID/settings.cfg create mode 100644 Features/FIND_BY_ID/testCases/__init__.py create mode 100644 Features/FIND_BY_ID/testCases/conftest.py create mode 100644 Features/FIND_BY_ID/testCases/report.html create mode 100644 Features/FIND_BY_ID/testCases/test.py create mode 100644 Features/FIND_BY_ID/testPages/__init__.py create mode 100644 Features/FIND_BY_ID/testPages/data/Data_page.py create mode 100644 Features/FIND_BY_ID/testPages/data/__init__.py create mode 100644 Features/FIND_BY_ID/userInputs/__init__.py create mode 100644 Features/FIND_BY_ID/userInputs/user_inputs.py create mode 100644 Features/GenericScript/README.md create mode 100644 Features/GenericScript/__init__.py create mode 100644 Features/GenericScript/requires.txt create mode 100644 Features/GenericScript/settings-sample.cfg create mode 100644 Features/GenericScript/settings.cfg create mode 100644 Features/GenericScript/testCases/__init__.py create mode 100644 Features/GenericScript/testCases/conftest.py create mode 100644 Features/GenericScript/testCases/report.html create mode 100644 Features/GenericScript/testCases/test.py create mode 100644 Features/GenericScript/testPages/__init__.py create mode 100644 Features/GenericScript/testPages/data/__init__.py create mode 100644 Features/GenericScript/testPages/data/data_generator_page.py create mode 100644 Features/GenericScript/userInputs/__init__.py create mode 100644 Features/GenericScript/userInputs/user_inputs.py diff --git a/Features/FIND_BY_ID/README.md b/Features/FIND_BY_ID/README.md new file mode 100644 index 000000000..6e43f04c9 --- /dev/null +++ b/Features/FIND_BY_ID/README.md @@ -0,0 +1,57 @@ +## Commcare Powerbi Integration exports Test Script + +These tests ensure that the [Powerbi_integration_exports] (https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143945995/Microsoft+Power+BI+Integration)features work as expected and that there are no regressions +The automated tests comprises of [these powerbi_integration_exports functionality.] (https://docs.google.com/spreadsheets/d/1wlW7gvmz8aW1gPLurHgD5onU1_C6laUhUtzQfmi8H9Y/edit?gid=0#gid=0) +## Executing Scripts + +### On Local Machine + +#### Setting up test environment + +```sh + +# create and activate a virtualenv using your preferred method. Example: +python -m venv venv +source venv/bin/activate + + +# install requirements +pip install -r requires.txt + +``` + +[More on setting up virtual environments](https://confluence.dimagi.com/display/GTD/QA+and+Python+Virtual+Environments) + + +#### Running Tests + + + - Copy `settings-sample.cfg` to `settings.cfg` and populate `settings.cfg` for +the environment you want to test. +- Run tests using pytest command like: + +```sh + +# To execute all the test cases +pytest -v --rootdir= Features/Lookuptable/testCases + +``` +- You could also pass the following arguments + - ` -n 3 --dist=loadfile` - This will run the tests parallelly in 3 instances. The number of reruns is configurable. + - ` --reruns 1` - This will re-run the tests once in case of failures.The number of reruns is configurable too. + +### Trigger Manually on Gitaction + +clone this repository + +To manually trigger the script, + - Go to [Powerbi_integration_exports action](https://github.com/dimagi/dimagi-qa/actions/lookuptable.yml) + - Run workflow + - Use workflow from ```master``` + - Run! + +If you are a part of the QA team, you'll receive emails for the result of the run after it's complete. + +clone this repository + +Besides, you should be able to find the zipped results in the **Artifacts** section, of the corresponding run (after it's complete). diff --git a/Features/FIND_BY_ID/__init__.py b/Features/FIND_BY_ID/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/FIND_BY_ID/requires.txt b/Features/FIND_BY_ID/requires.txt new file mode 100644 index 000000000..826c8961f --- /dev/null +++ b/Features/FIND_BY_ID/requires.txt @@ -0,0 +1,14 @@ +## Stores information about all the libraries, modules, and packages that are used in this project. + + +pandas>=1.2.2 +pytest>=6.2.5 +pytest-html>=3.1.1 +selenium == 4.11.2 +openpyxl>=3.0.6 +pytest-rerunfailures>=10.2 +pytest-xdist>=2.4.0 +pytest-xdist[psutil] +pyotp >=2.6.0 +pytest-order +py \ No newline at end of file diff --git a/Features/FIND_BY_ID/settings-sample.cfg b/Features/FIND_BY_ID/settings-sample.cfg new file mode 100644 index 000000000..b6bb402bb --- /dev/null +++ b/Features/FIND_BY_ID/settings-sample.cfg @@ -0,0 +1,12 @@ +[default] +# This is the environment url of commcare +url = https://www.commcarehq.org/ +# Login username of the webuser +login_username = +# Login password of the webuser +login_password = +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + diff --git a/Features/FIND_BY_ID/settings.cfg b/Features/FIND_BY_ID/settings.cfg new file mode 100644 index 000000000..49e640100 --- /dev/null +++ b/Features/FIND_BY_ID/settings.cfg @@ -0,0 +1,14 @@ +[default] +# This is the environment url of commcare +url = https://www.commcarehq.org/ +# Login username of the webuser +login_username = automation.user.commcarehq@gmail.com +# Login password of the webuser +login_password = pass@123 +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + + + diff --git a/Features/FIND_BY_ID/testCases/__init__.py b/Features/FIND_BY_ID/testCases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/FIND_BY_ID/testCases/conftest.py b/Features/FIND_BY_ID/testCases/conftest.py new file mode 100644 index 000000000..b9aa46ac7 --- /dev/null +++ b/Features/FIND_BY_ID/testCases/conftest.py @@ -0,0 +1,73 @@ +import os + +from configparser import ConfigParser +from pathlib import Path +from common_utilities.fixtures import * + +""""This file provides fixture functions for driver initialization""" + +global driver + + +@pytest.fixture(scope="session") +def environment_settings_lookup(): + """Load settings from os.environ + + Names of environment variables: + DIMAGIQA_URL + DIMAGIQA_LOGIN_USERNAME + DIMAGIQA_LOGIN_PASSWORD + DIMAGIQA_MAIL_USERNAME + DIMAGIQA_MAIL_PASSWORD + + See https://docs.github.com/en/actions/reference/encrypted-secrets + for instructions on how to set them. + """ + settings = {} + for name in ["url", "login_username", "login_password", "mail_username", + "mail_password", "staging_auth_key", "prod_auth_key"]: + + var = f"DIMAGIQA_{name.upper()}" + if var in os.environ: + settings[name] = os.environ[var] + if "url" not in settings: + env = os.environ.get("DIMAGIQA_ENV") or "staging" + subdomain = "www" if env == "production" else env + # updates the url with the project domain while testing in CI + project = "a/qa-automation-prod" if env == "production" else "a/qa-automation" + settings["url"] = f"https://{subdomain}.commcarehq.org/{project}" + return settings + + +@pytest.fixture(scope="session", autouse=True) +def settings(environment_settings_lookup): + if os.environ.get("CI") == "true": + settings = environment_settings_lookup + settings["CI"] = "true" + if any(x not in settings for x in ["url", "login_username", "login_password", + "mail_username", "mail_password", "staging_auth_key", + "prod_auth_key"]): + lines = environment_settings_lookup.__doc__.splitlines() + vars_ = "\n ".join(line.strip() for line in lines if "DIMAGIQA_" in line) + raise RuntimeError( + f"Environment variables not set:\n {vars_}\n\n" + "See https://docs.github.com/en/actions/reference/encrypted-secrets " + "for instructions on how to set them." + ) + return settings + path = Path(__file__).parent.parent / "settings.cfg" + if not path.exists(): + raise RuntimeError( + f"Not found: {path}\n\n" + "Copy settings-sample.cfg to settings.cfg and populate " + "it with values for the environment you want to test." + ) + settings = ConfigParser() + settings.read(path) + # updates the url with the project domain while testing in local + if settings["default"]["url"] == "https://www.commcarehq.org/": + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation-prod" + else: + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation" + return settings["default"] + diff --git a/Features/FIND_BY_ID/testCases/report.html b/Features/FIND_BY_ID/testCases/report.html new file mode 100644 index 000000000..a59d6c646 --- /dev/null +++ b/Features/FIND_BY_ID/testCases/report.html @@ -0,0 +1,1091 @@ + + + + + report.html + + + + +

report.html

+

Report generated on 19-Sep-2025 at 15:02:18 by pytest-html + v4.1.1

+
+

Environment

+
+
+ + + + + +
+
+

Summary

+
+
+

0 test took 00:00:06.

+

(Un)check the boxes to filter the results.

+
+ +
+
+
+
+ + 0 Failed, + + 0 Passed, + + 0 Skipped, + + 0 Expected failures, + + 0 Unexpected passes, + + 1 Errors, + + 0 Reruns +
+
+  /  +
+
+
+
+
+
+
+
+ + + + + + + + + +
ResultTagsTestDuration
+ +
+
+ +
+ \ No newline at end of file diff --git a/Features/FIND_BY_ID/testCases/test.py b/Features/FIND_BY_ID/testCases/test.py new file mode 100644 index 000000000..0960d22b3 --- /dev/null +++ b/Features/FIND_BY_ID/testCases/test.py @@ -0,0 +1,188 @@ +import pytest + +from Features.Powerbi_integration_exports.userInputs.user_inputs import UserData +from HQSmokeTests.testPages.data.export_data_page import ExportDataPage +from HQSmokeTests.testPages.home.home_page import HomePage +from Features.Powerbi_integration_exports.testPages.data.power_bi_page import PowerBiPage + +""""Contains test cases related to the Data module""" + +values = dict() + +@pytest.mark.lookup +def test_01_Odata_Feed_1(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + +def test_02_Odata_Feed_2(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_feed_type() + + +def test_03_Odata_Feed_3(driver,settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + +def test_04_Odata_Feed_4(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.application_dropdown(UserData.reassign_cases_application) + +def test_05_Odata_Feed_5(driver, settings): + home = (HomePage(driver, settings)) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.application_dropdown(UserData.reassign_cases_application) + data.menu_dropdown() + +def test_06_Odata_Feed_6(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.cancel() + +def test_07_Odata_Feed_07(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.save_odata_feed() + data.delete1() + +def test_08_Odata_Feed_08(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + +def test_09_Odata_Feed_09(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application, UserData.reassign_menu, UserData.reassign_form) + data.add_odata() + data.show_advance_question() + + +def test_10_Odata_Feed_10(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_case() + data.case_feed(UserData.case) + data.add_odata() + data.save_odata_feed() + data.delete1() + + + +def test_11_Odata_Feed_11(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.save_odata_feed() + data.copy_edit_feed() + + +def test_12_Odata_Feed_12(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.odata_20() + + +def test_13_Odata_Feed_13(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.create_multiple_odatafeed(10) + data.go_to_page() + data.power_bi_tableau_integration_bulk_delete() + +def test_14_Odata_Feed_14(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.delete_questions() + +def test_15_Odata_Feed_15(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.de_identified() + +def test_16_Odata_Feed_16(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + username = settings["login_username"] + password = settings["login_password"] + data.ui() + data.select_form() + data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + data.add_odata() + data.odata_20() + data.view_odata_feed(username, password) + +def test_17_Odata_Feed_17(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_form() + data.form_feed(UserData.Basic_tests_application,UserData.Basic_menu,UserData.Repeat_form) + data.add_odata() + data.repeat_checkbox2() + data.save_odata_feed() + data.edit_filters() + + +def test_18_Odata_Feed_18(driver, settings): + home = HomePage(driver, settings) + home.data_menu() + data = PowerBiPage(driver) + data.ui() + data.select_case() + data.case_feed(UserData.parent) + data.add_odata() + data.parent_checkbox1() + data.save_odata_feed() \ No newline at end of file diff --git a/Features/FIND_BY_ID/testPages/__init__.py b/Features/FIND_BY_ID/testPages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/FIND_BY_ID/testPages/data/Data_page.py b/Features/FIND_BY_ID/testPages/data/Data_page.py new file mode 100644 index 000000000..607d15d89 --- /dev/null +++ b/Features/FIND_BY_ID/testPages/data/Data_page.py @@ -0,0 +1,274 @@ +import os.path +import time +from time import sleep + +from selenium.common import TimeoutException, ElementClickInterceptedException +from selenium.webdriver import Keys +from selenium.webdriver.common.by import By +from selenium.webdriver.support.select import Select + +from common_utilities.selenium.base_page import BasePage +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings +from Features.Powerbi_integration_exports.userInputs.user_inputs import UserData + + +""""Contains test page elements and functions related to the Lookup Table module""" + +class PowerBiPage(BasePage): + + def __init__(self, driver): + super().__init__(driver) + self.Data = (By.LINK_TEXT, "Data") + self.view_all = (By.LINK_TEXT, "View All") + self.power_bi = (By.XPATH, "//*[@id='hq-sidebar']/nav/ul[1]/li[7]/a") + self.add_odata_feed = (By.XPATH, "//*[@id='create-export']/p/a") + self.disabled = (By.XPATH,"//*[@data-bind='visible: showSubmit, disable: disableSubmit']") + self.feed_type = (By.XPATH,"//select[@id='id_model_type']") + self.feed_type_visible = (By.XPATH,"//*[@data-bind='visible: modelType()']") + self.case_type = (By.XPATH,"//*[@for='id_case_type']") + self.cancel_button = (By.XPATH,"//*[@id='createExportOptionsModal']/div/form/div/div[7]/button[1]") + self.applications = (By.XPATH,"//select[contains(@name,'application')]") + self.app_dropdown = (By.XPATH,"//input[contains(@aria-controls,'id_application-results')]") + self.users_list_item = "//ul[@role='listbox']/li[contains(.,'{}')]" + self.menu =(By.XPATH,"//select[contains(@name,'module')]") + self.menu_select = (By.XPATH,"//input[@aria-controls='select2-id_module-results']") + self.form = (By.XPATH,"//select[contains(@placeholder,'Select Form')]") + self.add_export_conf = (By.XPATH, "//button[@data-bind='visible: showSubmit, disable: disableSubmit']") + self.submission_msg = (By.XPATH, "//span[contains(@data-bind,'text: submissionCountText')]") + self.case = (By.XPATH,"//span[@aria-controls='select2-id_case_type-container']") + self.case_select = (By.XPATH,"//input[@aria-controls='select2-id_case_type-results']") + self.save = (By.XPATH,"//button[@type='submit'][contains(@data-bind,'click: save')]") + self.settings = (By.XPATH, "//*[@id='customize-export']/header/div/div/h3") + self.success = (By.XPATH,"//*[@class='alert alert-margin-top fade in alert-success']") + self.odata_feed_name = (By.XPATH,"//input[@id='export-name']") + self.powerBI_tab_int = (By.LINK_TEXT, "PowerBi/Tableau Integration") + self.select_all_btn = (By.XPATH, '//button[@data-bind="click: selectAll"]') + self.delete_selected_exports = (By.XPATH, '//a[@href= "#bulk-delete-export-modal"]') + self.bulk_delete_confirmation_btn = (By.XPATH, '//button[@data-bind="click: BulkExportDelete"]') + self.show_advance = (By.XPATH, "//span[@data-bind='visible: !table.showAdvanced()']") + self.hide_advance = (By.XPATH,"//span[@data-bind='visible: table.showAdvanced']") + self.show_delete = (By.XPATH,"//span[@data-bind='visible: !table.showDeleted()']") + self.copy_edit_button = (By.XPATH,"(//*[@data-bind='if: isOData()'])[1]") + self.description = (By.XPATH,("//*[@id='export-description']")) + self.disabled1 =(By.XPATH,("//td[5][./input[@disabled]]//preceding-sibling::td//input[@type='checkbox' and @disabled='disabled']")) + self.copy_odata_feed = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div/span/a")) + self.link = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div/input")) + self.delete = (By.XPATH,("(//a[@data-bs-toggle='modal'])[9]")) + self.delete_button = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[5]/div/a")) + self.edit_filter = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[3]/a[2]")) + self.daterange =(By.XPATH,("//*[@id='id_date_range']")) + self.save_filter = (By.XPATH,("//*[@id='setFeedFiltersModal']/div/form/div/div[3]/button[2]")) + self.pagination = (By.XPATH,("//*[@id='export-list']/div[2]/div[1]/div[2]/pagination/div/div/div[2]/nav/ul/li[3]/a")) + self.delete_question = (By.XPATH,("//span[@data-bind='visible: !table.showDeleted()']")) + self.process = (By.XPATH, "//*[@id='export-process-deleted-applications']/div/div/div[3]/button[2]") + self.refresh_page = (By.XPATH, "//*[@id='export-process-deleted-applications']/div/div/div[3]/button[2]") + self.hide_delete_question = (By.XPATH,"//*[@data-bind='visible: table.showDeleted()']") + self.allow_sensitive = (By.XPATH, "//*[@id='customize-export']/form/fieldset[3]/button") + self.de_identified1 = (By.XPATH,"//*[@id='is_deidentified']") + self.label = (By.XPATH,"/html/body/div[1]/div[4]/div/div[2]/div[4]/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/h4/label") + self.check_data = (By.XPATH, "//*[contains(text(), '@odata.context')]") + self.copy_odata_link = (By.XPATH,"//*[@id='export-list']/div[2]/div[1]/div[2]/table/tbody/tr/td[2]/div[2]/button" ) + self.copy_odata_link_form = (By.XPATH,"(//*[contains(@class,'form-control input-sm')])[1]") + self.copy_odata_link_btn_form = ( + By.XPATH, + "//div[contains(@data-bind,'text-body-secondary')][.//span[text()='"+ UserData.name +"']]//following-sibling::div[contains(@data-bind,'showLink')]//button") + self.repeat_checkbox = (By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[2]/legend/span[1]/input") + self.parent_checkbox = (By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[3]/legend/span[1]/input") + self.case_id_duplicate =(By.XPATH,"//body[1]/div[1]/div[3]/div[1]/div[2]/div[2]/form[1]/fieldset[2]/div[1]/div[1]/table[1]/tbody[1]/tr[9]/td[1]/input[1]") + self.repeat_checkbox1 =(By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[4]/legend/span[1]/input") + + def ui(self): + self.wait_to_click(self.power_bi,2) + self.power_bi_tableau_integration_bulk_delete() + self.wait_to_click(self.add_odata_feed) + + def select_feed_type(self): + self.is_present_and_displayed(self.disabled,10) + print("Add OData Feed button is disabled") + + def select_form(self): + self.wait_to_click(self.feed_type,50) + ss = Select(self.find_element(self.feed_type)) + ss.select_by_visible_text('Form') + self.is_present_and_displayed(self.feed_type_visible) + print("App type, application,Menu , form are displayed") + + def application_dropdown(self,selectapp): + self.wait_to_click(self.applications) + app_dropdown=[] + app_dropdown=(self.find_elements_texts(self.applications)) + time.sleep(10) + print(app_dropdown) + self.wait_to_click(self.applications) + self.select_by_text(self.applications,selectapp) + + def menu_dropdown(self): + self.wait_to_click(self.menu) + menu_dropdown=[] + menu_dropdown=(self.find_elements_texts(self.menu_select)) + time.sleep(10) + print(menu_dropdown) + + def select_case(self): + self.wait_to_click(self.feed_type,30) + ss = Select(self.find_element(self.feed_type)) + # Select option by visible text + ss.select_by_visible_text('Case') + self.is_present_and_displayed(self.case_type,15) + print("case type field displayed") + + def cancel(self): + time.sleep(10) + self.wait_to_click(self.cancel_button,10) + + def form_feed(self,appselect,menu_select,form_select): + self.wait_to_click(self.applications) + self.select_by_text(self.applications,appselect) + self.wait_to_click(self.menu) + self.select_by_text(self.menu,menu_select) + self.wait_to_click(self.form) + self.select_by_text(self.form,form_select) + self.is_present_and_displayed(self.submission_msg) + print("Form submission message displayed") + + def add_odata(self): + self.wait_to_click(self.add_export_conf) + self.is_present_and_displayed(self.settings,10) + print ("odata feed settings page displayed") + self.wait_to_click(self.odata_feed_name) + self.wait_to_clear_and_send_keys(self.odata_feed_name,UserData.name+Keys.TAB) + self.wait_to_click(self.case_id_duplicate) + self.scroll_to_bottom() + self.is_present_and_displayed(self.save,10) + + def repeat_checkbox2(self): + self.scroll_to_element(self.repeat_checkbox) + self.wait_to_click(self.repeat_checkbox) + self.scroll_to_element(self.repeat_checkbox1) + self.wait_to_click(self.repeat_checkbox1) + + def parent_checkbox1(self): + self.scroll_to_element(self.parent_checkbox) + self.js_click(self.parent_checkbox,2) + + + def delete_bulk_exports(self): + try: + self.wait_to_click(self.select_all_btn) + self.wait_to_click(self.delete_selected_exports) + self.wait_to_click(self.bulk_delete_confirmation_btn) + time.sleep(10) + except TimeoutException: + print("No exports available") + + + def power_bi_tableau_integration_bulk_delete(self): + try: + self.wait_and_sleep_to_click(self.powerBI_tab_int) + except ElementClickInterceptedException: + self.js_click(self.powerBI_tab_int) + self.delete_bulk_exports() + + def case_feed(self,select_case): + self.wait_to_click(self.case,10) + self.wait_for_element(self.case_select) + self.send_keys(self.case_select, select_case) + self.wait_to_click((By.XPATH, self.users_list_item.format(select_case))) + + def save_odata_feed(self): + self.js_click(self.save) + time.sleep(2) + self.is_present_and_displayed(self.success,10) + print("odata feed is created") + + def show_advance_question(self): + self.wait_to_click(self.show_advance) + self.wait_to_click(self.hide_advance) + self.is_present_and_displayed(self.show_advance) + self.is_present_and_displayed(self.show_delete) + print("show advance and show delete questions buttons are displayed") + + def copy_edit_feed(self): + self.wait_to_click(self.copy_edit_button,20) + self.wait_to_click(self.odata_feed_name) + self.wait_to_clear_and_send_keys(self.odata_feed_name,UserData.updatedname) + self.wait_to_click(self.save) + + def odata_20(self): + self.wait_to_click(self.description,5) + self.send_keys(self.description, UserData.description+Keys.TAB) + assert self.is_present_and_displayed(self.disabled1,10) + print("disabled button present") + self.scroll_to_bottom() + time.sleep(2) + self.js_click(self.save) + print("clicked on saved button") + self.is_present_and_displayed(self.success, 10) + + + + def delete1(self): + self.wait_to_click(self.delete) + self.wait_to_click(self.delete_button) + + def edit_filters(self): + self.wait_to_click(self.edit_filter,2) + self.wait_to_click(self.daterange, 5) + ss = Select(self.find_element(self.daterange)) + # Select option by visible text + ss.select_by_visible_text('Last year') + self.wait_to_click(self.save_filter,10) + self.wait_to_click(self.edit_filter) + print("Data range values", self.daterange) + + def create_multiple_odatafeed(self,no_of_feeds): + for i in range(1,no_of_feeds): + self.ui() + self.select_form() + self.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form) + self.add_odata() + self.save_odata_feed() + + + def go_to_page(self): + self.wait_to_click(self.pagination,10) + print("pagination working fine") + + def delete_questions(self): + self.wait_to_click(self.delete_question) + self.is_present_and_displayed(self.hide_delete_question) + print("Deleted questions become included in the question list for form feeds") + + def de_identified(self): + self.scroll_to_bottom() + self.js_click(self.allow_sensitive,2) + self.js_click(self.de_identified1,2) + self.js_click(self.save,10) + self.is_present_and_displayed(self.label) + print("odatafeed marked as de-identified") + + def view_odata_feed(self,username,password): + self.driver.refresh() + time.sleep(10) + print(self.copy_odata_link_btn_form) + self.wait_and_sleep_to_click(self.copy_odata_link_btn_form,40) + self.get_url_paste_browser(username, password, 'forms') + self.assert_odata_feed_data() + + def get_url_paste_browser(self, username, password, item): + global odata_feed_link1 + if item == 'forms': + odata_feed_link1 = self.wait_to_get_value(self.copy_odata_link_form) + print("===="+odata_feed_link1) + final_URL_case = f"https://{username}:{password}@{odata_feed_link1[8:]}" + print("--------"+final_URL_case) + time.sleep(10) + self.driver.get(final_URL_case) + + def assert_odata_feed_data(self): + odata_feed_data = self.driver.page_source + verify_data = self.find_elements(self.check_data) + assert len(verify_data) > 0, "Odata feed is Empty " + # self.driver.close() # Close the feed URL + self.driver.back() \ No newline at end of file diff --git a/Features/FIND_BY_ID/testPages/data/__init__.py b/Features/FIND_BY_ID/testPages/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/FIND_BY_ID/userInputs/__init__.py b/Features/FIND_BY_ID/userInputs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/FIND_BY_ID/userInputs/user_inputs.py b/Features/FIND_BY_ID/userInputs/user_inputs.py new file mode 100644 index 000000000..e73c879a8 --- /dev/null +++ b/Features/FIND_BY_ID/userInputs/user_inputs.py @@ -0,0 +1,29 @@ +""""Contains test data that are used as user inputs across various areasn in CCHQ""" +import os +import random +import string + +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings + + +class UserData: + + + """User Test Data""" + user_input_base_dir = os.path.dirname(os.path.abspath(__file__)) + # Pre-setup application and case names + field_val = 'table'+ str(fetch_random_string()) + reassign_cases_application = 'Reassign Cases' + reassign_menu = 'Case List' + reassign_form = 'Registration Form' + reassign_case = 'reassign' + name = 'powerbi_odatafeed1' + updatedname = 'updated_powerbi_odatafeed1' + description = 'testing the powerbi form report' + Basic_tests_application = 'Basic Tests' + Basic_menu = 'Formplayer Specific Tests' + Repeat_form = '[Formplayer] Repeats' + parent = 'sub_case_one' + odata_feed_form = '' + case='case' \ No newline at end of file diff --git a/Features/GenericScript/README.md b/Features/GenericScript/README.md new file mode 100644 index 000000000..88116b467 --- /dev/null +++ b/Features/GenericScript/README.md @@ -0,0 +1,57 @@ +## Commcare Data Dictionary Test Script + +These tests ensure that the Data Dictionary (https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143944977/Data+Dictionary)features work as expected and that there are no regressions +The automated tests comprises of the Data dictionary functionality.](https://docs.google.com/spreadsheets/d/1Ixw1PC9PVpqYOlP7aq9a86pC0wZaJVgGNVL_4h9KLbM/edit#gid=195915568) +## Executing Scripts + +### On Local Machine + +#### Setting up test environment + +```sh + +# create and activate a virtualenv using your preferred method. Example: +python -m venv venv +source venv/bin/activate + + +# install requirements +pip install -r requires.txt + +``` + +[More on setting up virtual environments](https://confluence.dimagi.com/display/GTD/QA+and+Python+Virtual+Environments) + + +#### Running Tests + + + - Copy `settings-sample.cfg` to `settings.cfg` and populate `settings.cfg` for +the environment you want to test. +- Run tests using pytest command like: + +```sh + +# To execute all the test cases +pytest -v --rootdir= Features/DataDictionary/testCases + +``` +- You could also pass the following arguments + - ` -n 3 --dist=loadfile` - This will run the tests parallelly in 3 instances. The number of reruns is configurable. + - ` --reruns 1` - This will re-run the tests once in case of failures.The number of reruns is configurable too. + +### Trigger Manually on Gitaction + +clone this repository + +To manually trigger the script, + - Go to [DataDictionary](https://github.com/dimagi/dimagi-qa/actions/lookuptable.yml) + - Run workflow + - Use workflow from ```master``` + - Run! + +If you are a part of the QA team, you'll receive emails for the result of the run after it's complete. + +clone this repository + +Besides, you should be able to find the zipped results in the **Artifacts** section, of the corresponding run (after it's complete). diff --git a/Features/GenericScript/__init__.py b/Features/GenericScript/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/GenericScript/requires.txt b/Features/GenericScript/requires.txt new file mode 100644 index 000000000..826c8961f --- /dev/null +++ b/Features/GenericScript/requires.txt @@ -0,0 +1,14 @@ +## Stores information about all the libraries, modules, and packages that are used in this project. + + +pandas>=1.2.2 +pytest>=6.2.5 +pytest-html>=3.1.1 +selenium == 4.11.2 +openpyxl>=3.0.6 +pytest-rerunfailures>=10.2 +pytest-xdist>=2.4.0 +pytest-xdist[psutil] +pyotp >=2.6.0 +pytest-order +py \ No newline at end of file diff --git a/Features/GenericScript/settings-sample.cfg b/Features/GenericScript/settings-sample.cfg new file mode 100644 index 000000000..b6bb402bb --- /dev/null +++ b/Features/GenericScript/settings-sample.cfg @@ -0,0 +1,12 @@ +[default] +# This is the environment url of commcare +url = https://www.commcarehq.org/ +# Login username of the webuser +login_username = +# Login password of the webuser +login_password = +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + diff --git a/Features/GenericScript/settings.cfg b/Features/GenericScript/settings.cfg new file mode 100644 index 000000000..d150a7436 --- /dev/null +++ b/Features/GenericScript/settings.cfg @@ -0,0 +1,14 @@ +[default] +# This is the environment url of commcare +url = https://staging.commcarehq.org/ +# Login username of the webuser +login_username = automation.user.commcarehq@gmail.com +# Login password of the webuser +login_password = pass@123 +# This is a preconfigured authentication key used for 2FA tests on staging - If 2FA enabled on staging. +staging_auth_key = +# This is a preconfigured authentication key used for 2FA tests on prod +prod_auth_key = + + + diff --git a/Features/GenericScript/testCases/__init__.py b/Features/GenericScript/testCases/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/GenericScript/testCases/conftest.py b/Features/GenericScript/testCases/conftest.py new file mode 100644 index 000000000..b9aa46ac7 --- /dev/null +++ b/Features/GenericScript/testCases/conftest.py @@ -0,0 +1,73 @@ +import os + +from configparser import ConfigParser +from pathlib import Path +from common_utilities.fixtures import * + +""""This file provides fixture functions for driver initialization""" + +global driver + + +@pytest.fixture(scope="session") +def environment_settings_lookup(): + """Load settings from os.environ + + Names of environment variables: + DIMAGIQA_URL + DIMAGIQA_LOGIN_USERNAME + DIMAGIQA_LOGIN_PASSWORD + DIMAGIQA_MAIL_USERNAME + DIMAGIQA_MAIL_PASSWORD + + See https://docs.github.com/en/actions/reference/encrypted-secrets + for instructions on how to set them. + """ + settings = {} + for name in ["url", "login_username", "login_password", "mail_username", + "mail_password", "staging_auth_key", "prod_auth_key"]: + + var = f"DIMAGIQA_{name.upper()}" + if var in os.environ: + settings[name] = os.environ[var] + if "url" not in settings: + env = os.environ.get("DIMAGIQA_ENV") or "staging" + subdomain = "www" if env == "production" else env + # updates the url with the project domain while testing in CI + project = "a/qa-automation-prod" if env == "production" else "a/qa-automation" + settings["url"] = f"https://{subdomain}.commcarehq.org/{project}" + return settings + + +@pytest.fixture(scope="session", autouse=True) +def settings(environment_settings_lookup): + if os.environ.get("CI") == "true": + settings = environment_settings_lookup + settings["CI"] = "true" + if any(x not in settings for x in ["url", "login_username", "login_password", + "mail_username", "mail_password", "staging_auth_key", + "prod_auth_key"]): + lines = environment_settings_lookup.__doc__.splitlines() + vars_ = "\n ".join(line.strip() for line in lines if "DIMAGIQA_" in line) + raise RuntimeError( + f"Environment variables not set:\n {vars_}\n\n" + "See https://docs.github.com/en/actions/reference/encrypted-secrets " + "for instructions on how to set them." + ) + return settings + path = Path(__file__).parent.parent / "settings.cfg" + if not path.exists(): + raise RuntimeError( + f"Not found: {path}\n\n" + "Copy settings-sample.cfg to settings.cfg and populate " + "it with values for the environment you want to test." + ) + settings = ConfigParser() + settings.read(path) + # updates the url with the project domain while testing in local + if settings["default"]["url"] == "https://www.commcarehq.org/": + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation-prod" + else: + settings["default"]["url"] = f"{settings['default']['url']}a/qa-automation" + return settings["default"] + diff --git a/Features/GenericScript/testCases/report.html b/Features/GenericScript/testCases/report.html new file mode 100644 index 000000000..b4e437ed6 --- /dev/null +++ b/Features/GenericScript/testCases/report.html @@ -0,0 +1,1091 @@ + + + + + report.html + + + + +

report.html

+

Report generated on 10-Sep-2025 at 19:10:37 by pytest-html + v4.1.1

+
+

Environment

+
+
+ + + + + +
+
+

Summary

+
+
+

4 tests took 00:08:03.

+

(Un)check the boxes to filter the results.

+
+ +
+
+
+
+ + 0 Failed, + + 4 Passed, + + 0 Skipped, + + 0 Expected failures, + + 0 Unexpected passes, + + 0 Errors, + + 0 Reruns +
+
+  /  +
+
+
+
+
+
+
+
+ + + + + + + + + +
ResultTagsTestDuration
+ +
+
+ +
+ \ No newline at end of file diff --git a/Features/GenericScript/testCases/test.py b/Features/GenericScript/testCases/test.py new file mode 100644 index 000000000..da61e2619 --- /dev/null +++ b/Features/GenericScript/testCases/test.py @@ -0,0 +1,26 @@ +import unittest +import os +from pages.data_generator_page import DataGeneratorPage + +class TestDataGeneratorPage(unittest.TestCase): + + def setUp(self): + self.generator = DataGeneratorPage() + self.num_rows = 100 + + def test_data_generation_length(self): + df = self.generator.generate_data(self.num_rows) + self.assertEqual(len(df), self.num_rows, "Row count mismatch") + + def test_column_names_match(self): + df = self.generator.generate_data(self.num_rows) + expected_columns = list(self.generator.schema_df['Column Name']) + self.assertEqual(list(df.columns), expected_columns, "Columns do not match schema") + + def test_data_file_creation(self): + df = self.generator.generate_data(self.num_rows) + self.generator.save_data(df) + self.assertTrue(os.path.exists(self.generator.output_path), "Output file not created") + +if __name__ == '__main__': + unittest.main() diff --git a/Features/GenericScript/testPages/__init__.py b/Features/GenericScript/testPages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/GenericScript/testPages/data/__init__.py b/Features/GenericScript/testPages/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/GenericScript/testPages/data/data_generator_page.py b/Features/GenericScript/testPages/data/data_generator_page.py new file mode 100644 index 000000000..8cd066a74 --- /dev/null +++ b/Features/GenericScript/testPages/data/data_generator_page.py @@ -0,0 +1,44 @@ +import pandas as pd +import random +from faker import Faker +from datetime import datetime, timedelta +import os + +class DataGeneratorPage: + def __init__(self, config_path='userinputs/config.xlsx', output_path='output/generated_data.csv'): + self.config_path = config_path + self.output_path = output_path + self.fake = Faker() + self.schema_df = pd.read_excel(self.config_path, sheet_name='Schema') + + def generate_value(self, row): + dtype = row['Data Type'] + if dtype == 'int': + return random.randint(int(row['Min']), int(row['Max'])) + elif dtype == 'name': + return self.fake.name() + elif dtype == 'email': + return self.fake.email() + elif dtype == 'date': + start_date = datetime.now() - timedelta(days=5 * 365) + return self.fake.date_between(start_date=start_date, end_date='today') + elif dtype == 'choice': + options = [opt.strip() for opt in str(row['Options']).split(',')] + return random.choice(options) + else: + return 'N/A' + + def generate_data(self, num_rows=100): + data = [] + for _ in range(num_rows): + row_data = {} + for _, row in self.schema_df.iterrows(): + column_name = row['Column Name'] + row_data[column_name] = self.generate_value(row) + data.append(row_data) + return pd.DataFrame(data) + + def save_data(self, df): + os.makedirs(os.path.dirname(self.output_path), exist_ok=True) + df.to_csv(self.output_path, index=False) + print(f"[INFO] Data saved to: {self.output_path}") diff --git a/Features/GenericScript/userInputs/__init__.py b/Features/GenericScript/userInputs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Features/GenericScript/userInputs/user_inputs.py b/Features/GenericScript/userInputs/user_inputs.py new file mode 100644 index 000000000..c42bfffa6 --- /dev/null +++ b/Features/GenericScript/userInputs/user_inputs.py @@ -0,0 +1,32 @@ +""""Contains test data that are used as user inputs across various areasn in CCHQ""" +import os +import random +import string + +from common_utilities.generate_random_string import fetch_random_string +from common_utilities.path_settings import PathSettings + + +class UserData: + + + """User Test Data""" + user_input_base_dir = os.path.dirname(os.path.abspath(__file__)) + application = "Data_Dictionary" + application_description = 'dd' + str(fetch_random_string()) + case_properties = 'property'+ str(fetch_random_string()) + name_group = 'group'+ str(fetch_random_string()) + case_type = 'case_dd' + case_data_link = 'https://staging.commcarehq.org/a/qa-automation/reports/case_data/d57fa5c7fb184d219e7fe155dabdbb6a/' + english_value ='English' + plain1 ='Plain' + randomvalue1 = 'dyfuyvh' + date1 = 'Date' + number ='Number' + updated_input = 'English' + age_property_description = 'Testing the age property description' + model_value = 'case' + data_upload_path = "import_file.xlsx" + p1p2_user = "p1p2.web.user@gmail.com" + file = os.path.abspath(os.path.join(user_input_base_dir, "case_dd.xlsx")) + lookup_function = '=(F2)' From 1465221a3222827ac49e5682b0af6de59103ceb0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Sep 2025 11:42:35 +0530 Subject: [PATCH 05/14] few code changes for data dictionary functionality --- .github/workflows/data-dictionary-tests.yml | 286 +++++ .github/workflows/power-bi-tests.yml | 286 +++++ Features/DataDictionary/settings.cfg | 2 +- Features/DataDictionary/testCases/report.html | 12 +- Features/DataDictionary/testCases/test.py | 2 +- .../testPages/data/data_dictionary_page.py | 21 +- .../DataDictionary/userInputs/case_dd.xlsx | Bin 8684 -> 8778 bytes Features/FIND_BY_ID/README.md | 57 - Features/FIND_BY_ID/__init__.py | 0 Features/FIND_BY_ID/requires.txt | 14 - Features/FIND_BY_ID/settings-sample.cfg | 12 - Features/FIND_BY_ID/settings.cfg | 14 - Features/FIND_BY_ID/testCases/__init__.py | 0 Features/FIND_BY_ID/testCases/conftest.py | 73 -- Features/FIND_BY_ID/testCases/report.html | 1091 ----------------- Features/FIND_BY_ID/testCases/test.py | 188 --- Features/FIND_BY_ID/testPages/__init__.py | 0 .../FIND_BY_ID/testPages/data/Data_page.py | 274 ----- .../FIND_BY_ID/testPages/data/__init__.py | 0 Features/FIND_BY_ID/userInputs/__init__.py | 0 Features/FIND_BY_ID/userInputs/user_inputs.py | 29 - Features/GenericScript/README.md | 57 - Features/GenericScript/__init__.py | 0 Features/GenericScript/requires.txt | 14 - Features/GenericScript/settings-sample.cfg | 12 - Features/GenericScript/settings.cfg | 14 - Features/GenericScript/testCases/__init__.py | 0 Features/GenericScript/testCases/conftest.py | 73 -- Features/GenericScript/testCases/report.html | 1091 ----------------- Features/GenericScript/testCases/test.py | 26 - Features/GenericScript/testPages/__init__.py | 0 .../GenericScript/testPages/data/__init__.py | 0 .../testPages/data/data_generator_page.py | 44 - Features/GenericScript/userInputs/__init__.py | 0 .../GenericScript/userInputs/user_inputs.py | 32 - .../Powerbi_integration_exports/settings.cfg | 2 +- .../testCases/report.html | 14 +- .../testCases/test.py | 2 +- .../testPages/data/power_bi_page.py | 4 +- .../userInputs/user_inputs.py | 5 +- .../testPages/users/roles_permissions_page.py | 24 + 41 files changed, 629 insertions(+), 3146 deletions(-) create mode 100644 .github/workflows/data-dictionary-tests.yml create mode 100644 .github/workflows/power-bi-tests.yml delete mode 100644 Features/FIND_BY_ID/README.md delete mode 100644 Features/FIND_BY_ID/__init__.py delete mode 100644 Features/FIND_BY_ID/requires.txt delete mode 100644 Features/FIND_BY_ID/settings-sample.cfg delete mode 100644 Features/FIND_BY_ID/settings.cfg delete mode 100644 Features/FIND_BY_ID/testCases/__init__.py delete mode 100644 Features/FIND_BY_ID/testCases/conftest.py delete mode 100644 Features/FIND_BY_ID/testCases/report.html delete mode 100644 Features/FIND_BY_ID/testCases/test.py delete mode 100644 Features/FIND_BY_ID/testPages/__init__.py delete mode 100644 Features/FIND_BY_ID/testPages/data/Data_page.py delete mode 100644 Features/FIND_BY_ID/testPages/data/__init__.py delete mode 100644 Features/FIND_BY_ID/userInputs/__init__.py delete mode 100644 Features/FIND_BY_ID/userInputs/user_inputs.py delete mode 100644 Features/GenericScript/README.md delete mode 100644 Features/GenericScript/__init__.py delete mode 100644 Features/GenericScript/requires.txt delete mode 100644 Features/GenericScript/settings-sample.cfg delete mode 100644 Features/GenericScript/settings.cfg delete mode 100644 Features/GenericScript/testCases/__init__.py delete mode 100644 Features/GenericScript/testCases/conftest.py delete mode 100644 Features/GenericScript/testCases/report.html delete mode 100644 Features/GenericScript/testCases/test.py delete mode 100644 Features/GenericScript/testPages/__init__.py delete mode 100644 Features/GenericScript/testPages/data/__init__.py delete mode 100644 Features/GenericScript/testPages/data/data_generator_page.py delete mode 100644 Features/GenericScript/userInputs/__init__.py delete mode 100644 Features/GenericScript/userInputs/user_inputs.py diff --git a/.github/workflows/data-dictionary-tests.yml b/.github/workflows/data-dictionary-tests.yml new file mode 100644 index 000000000..c2216f6fd --- /dev/null +++ b/.github/workflows/data-dictionary-tests.yml @@ -0,0 +1,286 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Data Dictionary Tests + +on: + schedule: + - cron: '0 3 * * 6' + + workflow_dispatch: + inputs: + environment: + description: 'Environment to run tests against' + required: true + default: 'staging' + type: choice + options: + - staging + - production + +concurrency: + group: lookup-table-tests-${{ github.ref }} + cancel-in-progress: true + +jobs: + set_matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix-manual.outputs.matrix || steps.set-matrix-default.outputs.matrix }} + steps: + - id: set-matrix-manual + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "::set-output name=matrix::{\"environment\": [\"${{ inputs.environment }}\"]}" + - id: set-matrix-default + if: ${{ !contains(github.event_name , 'dispatch') }} + run: | + echo "::set-output name=matrix::{\"environment\": [\"production\", \"staging\"]}" + build: + needs: set_matrix + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.set_matrix.outputs.matrix) }} + name: Lookup tests on '${{ matrix.environment }}' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.13 + uses: actions/setup-python@v2 + with: + python-version: 3.13 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r Features/DataDictionary/requires.txt + - name: Data Dictionary tests with pytest + env: + DIMAGIQA_ENV: ${{ matrix.environment }} + DIMAGIQA_LOGIN_USERNAME: ${{ secrets.DIMAGIQA_LOGIN_USERNAME }} + DIMAGIQA_LOGIN_PASSWORD: ${{ secrets.DIMAGIQA_LOGIN_PASSWORD }} + DIMAGIQA_MAIL_USERNAME: ${{ secrets.DIMAGIQA_MAIL_USERNAME }} + DIMAGIQA_MAIL_PASSWORD: ${{ secrets.DIMAGIQA_MAIL_PASSWORD }} + DIMAGIQA_STAGING_AUTH_KEY: ${{ secrets.DIMAGIQA_STAGING_AUTH_KEY }} + DIMAGIQA_PROD_AUTH_KEY: ${{ secrets.DIMAGIQA_PROD_AUTH_KEY }} + run: | + echo "client_payload: ${{ toJson(github.event.client_payload) }}" + echo "matrix environment: ${{ matrix.environment }}" + echo "NOW=$(date +'%m-%d %H:%M')" >> $GITHUB_ENV + echo ${{env.NOW}} + pytest -v --rootdir= Features/DataDictionary/testCases -n 0 --dist=loadfile --reruns 1 --html=data_dictionary_reports_${{ matrix.environment }}.html + + + - name: Parse test counts + id: parse_counts + if: always() + run: | + # Extract variables from the api_test_counts.txt file + while IFS= read -r line; do + echo "::set-output name=${line%=*}::${line#*=}" + done < data_dictionary_test_counts_${{ matrix.environment }}.txt + + - name: Archive test results + id: artifact-upload-step + if: ${{ success() || failure() }} + uses: actions/upload-artifact@v4 + with: + name: test-result-reports--${{ matrix.environment }}-${{ github.run_id }} + path: /home/runner/work/dimagi-qa/dimagi-qa/data_dictionary_reports_${{ matrix.environment }}.html + retention-days: 2 + + - name: Fetch artifact ID + run: echo 'Artifact ID is ${{ steps.artifact-upload-step.outputs.artifact-id }}' + + - name: Set email vars + if: ${{ success() || failure() }} + id: configure_email + uses: actions/github-script@v6 + env: + JOB_STATUS: ${{ job.status }} + CC_ENV: ${{ matrix.environment }} + with: + script: | + const { promises: fs } = require('fs') + const {JOB_STATUS, NOW, CC_ENV, GITHUB_HEAD_REF} = process.env + const prefix = `[${CC_ENV}] DataDictionary Tests - ${JOB_STATUS.toUpperCase()} - Run #${context.runNumber}` + const suffix = `at ${NOW}` + let subject = `${prefix} on branch "${GITHUB_HEAD_REF}" ${suffix}` + + let bodyFile = './common_utilities/mail_templates/email_pass.md' + if (JOB_STATUS !== 'success') { + bodyFile = './common_utilities/mail_templates/email_fail.md' + } + + let actionRunLink = context.payload.repository.html_url + `/actions/runs/${context.runId}` + let testSuite = 'Data Dictionary Regression' + let bodyContent = await fs.readFile(bodyFile, 'utf8') + bodyContent = bodyContent.replace(/{{actionRunLink}}/g, actionRunLink) + .replace(/{{runNumber}}/g, context.runNumber) + .replace(/{{environment}}/g, CC_ENV) + .replace(/{{testSuite}}/g, testSuite) + + let receivers = 'qa-automation@dimagi.com' + if (context.eventName !== "pull_request" || context.eventName !== "push") { + receivers = 'qa@dimagi.com, sameena.shaik@fissionlabs.com' + } + return { + "subject": subject, + "body": bodyContent, + "reference": Math.random().toString(36).substr(2), // used to prevent threading of similar emails + "receivers": receivers + } + - name: Send Result Email + if: ${{ success() || failure() }} + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + username: ${{secrets.DIMAGIQA_MAIL_USERNAME}} + password: ${{secrets.DIMAGIQA_MAIL_PASSWORD}} + subject: ${{ fromJSON(steps.configure_email.outputs.result).subject }} + to: ${{ fromJSON(steps.configure_email.outputs.result).receivers }} + from: <${{secrets.DIMAGIQA_MAIL_USERNAME}}> + html_body: ${{ fromJSON(steps.configure_email.outputs.result).body }} + convert_markdown: true + attachments: ${{ github.workspace }}/data_dictionary_reports_${{ matrix.environment }}.html + in_reply_to: ${{ fromJSON(steps.configure_email.outputs.result).reference }} + + - name: Post to Slack channel on Failure + id: slack_fail + uses: slackapi/slack-github-action@v1.23.0 + if: failure() + with: + payload: | + { + "attachments": [ + { + "color": "#FF0000", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": " Konnichiwa :bye_boo: \n*${{ github.workflow }}* were just triggered! \n" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Passed:* ${{ steps.parse_counts.outputs.PASSED }} *Failed:* ${{ steps.parse_counts.outputs.FAILED }} *Error:* ${{ steps.parse_counts.outputs.ERROR }} *Skipped:* ${{ steps.parse_counts.outputs.SKIPPED }} *XFail:* ${{ steps.parse_counts.outputs.XFAIL }}\n" + } + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Environment: *\n ${{ matrix.environment }} \n" + }, + { + "type": "mrkdwn", + "text": " " + }, + { + "type": "mrkdwn", + "text": "*Status: *\n ${{ job.status }} :x:" + } + ] + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Here's the corresponding report :arrow_right::arrow_right:" + }, + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "Click to Downlaod", + "emoji": true + }, + "value": "click_me_123", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}", + "action_id": "button-action", + "style": "danger" + } + } + ] + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_FEATURES }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + + - name: Post to Slack channel on Success + id: slack_pass + uses: slackapi/slack-github-action@v1.23.0 + if: success() + with: + payload: | + { + "attachments": [ + { + "color": "#36a64f", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": " Konnichiwa :bye_boo: \n*${{ github.workflow }}* were just triggered! \n" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Passed:* ${{ steps.parse_counts.outputs.PASSED }} *Failed:* ${{ steps.parse_counts.outputs.FAILED }} *Error:* ${{ steps.parse_counts.outputs.ERROR }} *Skipped:* ${{ steps.parse_counts.outputs.SKIPPED }} *XFail:* ${{ steps.parse_counts.outputs.XFAIL }}\n" + } + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Environment: *\n ${{ matrix.environment }} \n" + }, + { + "type": "mrkdwn", + "text": " " + }, + { + "type": "mrkdwn", + "text": "*Status: *\n ${{ job.status }} :white_check_mark:" + } + ] + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Here's the corresponding report :arrow_right::arrow_right:" + }, + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "Click to Download", + "emoji": true + }, + "value": "click_me_123", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}", + "action_id": "button-action", + "style": "primary" + } + } + ] + } + ] + } + + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_FEATURES }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/power-bi-tests.yml b/.github/workflows/power-bi-tests.yml new file mode 100644 index 000000000..0d262f319 --- /dev/null +++ b/.github/workflows/power-bi-tests.yml @@ -0,0 +1,286 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Power BI Tests + +on: + schedule: + - cron: '0 2 * * 6' + + workflow_dispatch: + inputs: + environment: + description: 'Environment to run tests against' + required: true + default: 'staging' + type: choice + options: + - staging + - production + +concurrency: + group: power-bi-tests-${{ github.ref }} + cancel-in-progress: true + +jobs: + set_matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix-manual.outputs.matrix || steps.set-matrix-default.outputs.matrix }} + steps: + - id: set-matrix-manual + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "::set-output name=matrix::{\"environment\": [\"${{ inputs.environment }}\"]}" + - id: set-matrix-default + if: ${{ !contains(github.event_name , 'dispatch') }} + run: | + echo "::set-output name=matrix::{\"environment\": [\"production\", \"staging\"]}" + build: + needs: set_matrix + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.set_matrix.outputs.matrix) }} + name: Lookup tests on '${{ matrix.environment }}' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.13 + uses: actions/setup-python@v2 + with: + python-version: 3.13 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r Features/Powerbi_integration_exports/requires.txt + - name: Power BI tests with pytest + env: + DIMAGIQA_ENV: ${{ matrix.environment }} + DIMAGIQA_LOGIN_USERNAME: ${{ secrets.DIMAGIQA_LOGIN_USERNAME }} + DIMAGIQA_LOGIN_PASSWORD: ${{ secrets.DIMAGIQA_LOGIN_PASSWORD }} + DIMAGIQA_MAIL_USERNAME: ${{ secrets.DIMAGIQA_MAIL_USERNAME }} + DIMAGIQA_MAIL_PASSWORD: ${{ secrets.DIMAGIQA_MAIL_PASSWORD }} + DIMAGIQA_STAGING_AUTH_KEY: ${{ secrets.DIMAGIQA_STAGING_AUTH_KEY }} + DIMAGIQA_PROD_AUTH_KEY: ${{ secrets.DIMAGIQA_PROD_AUTH_KEY }} + run: | + echo "client_payload: ${{ toJson(github.event.client_payload) }}" + echo "matrix environment: ${{ matrix.environment }}" + echo "NOW=$(date +'%m-%d %H:%M')" >> $GITHUB_ENV + echo ${{env.NOW}} + pytest -v --rootdir= Features/Powerbi_integration_exports/testCases -n 0 --dist=loadfile --reruns 1 --html=power_bi_reports_${{ matrix.environment }}.html + + + - name: Parse test counts + id: parse_counts + if: always() + run: | + # Extract variables from the api_test_counts.txt file + while IFS= read -r line; do + echo "::set-output name=${line%=*}::${line#*=}" + done < powerbi_test_counts_${{ matrix.environment }}.txt + + - name: Archive test results + id: artifact-upload-step + if: ${{ success() || failure() }} + uses: actions/upload-artifact@v4 + with: + name: test-result-reports--${{ matrix.environment }}-${{ github.run_id }} + path: /home/runner/work/dimagi-qa/dimagi-qa/power_bi_reports_${{ matrix.environment }}.html + retention-days: 2 + + - name: Fetch artifact ID + run: echo 'Artifact ID is ${{ steps.artifact-upload-step.outputs.artifact-id }}' + + - name: Set email vars + if: ${{ success() || failure() }} + id: configure_email + uses: actions/github-script@v6 + env: + JOB_STATUS: ${{ job.status }} + CC_ENV: ${{ matrix.environment }} + with: + script: | + const { promises: fs } = require('fs') + const {JOB_STATUS, NOW, CC_ENV, GITHUB_HEAD_REF} = process.env + const prefix = `[${CC_ENV}] Powerbi_integration_exports Tests - ${JOB_STATUS.toUpperCase()} - Run #${context.runNumber}` + const suffix = `at ${NOW}` + let subject = `${prefix} on branch "${GITHUB_HEAD_REF}" ${suffix}` + + let bodyFile = './common_utilities/mail_templates/email_pass.md' + if (JOB_STATUS !== 'success') { + bodyFile = './common_utilities/mail_templates/email_fail.md' + } + + let actionRunLink = context.payload.repository.html_url + `/actions/runs/${context.runId}` + let testSuite = 'Power BI Regression' + let bodyContent = await fs.readFile(bodyFile, 'utf8') + bodyContent = bodyContent.replace(/{{actionRunLink}}/g, actionRunLink) + .replace(/{{runNumber}}/g, context.runNumber) + .replace(/{{environment}}/g, CC_ENV) + .replace(/{{testSuite}}/g, testSuite) + + let receivers = 'qa-automation@dimagi.com' + if (context.eventName !== "pull_request" || context.eventName !== "push") { + receivers = 'qa@dimagi.com, sameena.shaik@fissionlabs.com' + } + return { + "subject": subject, + "body": bodyContent, + "reference": Math.random().toString(36).substr(2), // used to prevent threading of similar emails + "receivers": receivers + } + - name: Send Result Email + if: ${{ success() || failure() }} + uses: dawidd6/action-send-mail@v3 + with: + server_address: smtp.gmail.com + server_port: 465 + username: ${{secrets.DIMAGIQA_MAIL_USERNAME}} + password: ${{secrets.DIMAGIQA_MAIL_PASSWORD}} + subject: ${{ fromJSON(steps.configure_email.outputs.result).subject }} + to: ${{ fromJSON(steps.configure_email.outputs.result).receivers }} + from: <${{secrets.DIMAGIQA_MAIL_USERNAME}}> + html_body: ${{ fromJSON(steps.configure_email.outputs.result).body }} + convert_markdown: true + attachments: ${{ github.workspace }}/power_bi_reports_${{ matrix.environment }}.html + in_reply_to: ${{ fromJSON(steps.configure_email.outputs.result).reference }} + + - name: Post to Slack channel on Failure + id: slack_fail + uses: slackapi/slack-github-action@v1.23.0 + if: failure() + with: + payload: | + { + "attachments": [ + { + "color": "#FF0000", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": " Konnichiwa :bye_boo: \n*${{ github.workflow }}* were just triggered! \n" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Passed:* ${{ steps.parse_counts.outputs.PASSED }} *Failed:* ${{ steps.parse_counts.outputs.FAILED }} *Error:* ${{ steps.parse_counts.outputs.ERROR }} *Skipped:* ${{ steps.parse_counts.outputs.SKIPPED }} *XFail:* ${{ steps.parse_counts.outputs.XFAIL }}\n" + } + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Environment: *\n ${{ matrix.environment }} \n" + }, + { + "type": "mrkdwn", + "text": " " + }, + { + "type": "mrkdwn", + "text": "*Status: *\n ${{ job.status }} :x:" + } + ] + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Here's the corresponding report :arrow_right::arrow_right:" + }, + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "Click to Downlaod", + "emoji": true + }, + "value": "click_me_123", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}", + "action_id": "button-action", + "style": "danger" + } + } + ] + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_FEATURES }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + + - name: Post to Slack channel on Success + id: slack_pass + uses: slackapi/slack-github-action@v1.23.0 + if: success() + with: + payload: | + { + "attachments": [ + { + "color": "#36a64f", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": " Konnichiwa :bye_boo: \n*${{ github.workflow }}* were just triggered! \n" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Passed:* ${{ steps.parse_counts.outputs.PASSED }} *Failed:* ${{ steps.parse_counts.outputs.FAILED }} *Error:* ${{ steps.parse_counts.outputs.ERROR }} *Skipped:* ${{ steps.parse_counts.outputs.SKIPPED }} *XFail:* ${{ steps.parse_counts.outputs.XFAIL }}\n" + } + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": "*Environment: *\n ${{ matrix.environment }} \n" + }, + { + "type": "mrkdwn", + "text": " " + }, + { + "type": "mrkdwn", + "text": "*Status: *\n ${{ job.status }} :white_check_mark:" + } + ] + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Here's the corresponding report :arrow_right::arrow_right:" + }, + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "Click to Download", + "emoji": true + }, + "value": "click_me_123", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}", + "action_id": "button-action", + "style": "primary" + } + } + ] + } + ] + } + + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_FEATURES }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/Features/DataDictionary/settings.cfg b/Features/DataDictionary/settings.cfg index d150a7436..49e640100 100644 --- a/Features/DataDictionary/settings.cfg +++ b/Features/DataDictionary/settings.cfg @@ -1,6 +1,6 @@ [default] # This is the environment url of commcare -url = https://staging.commcarehq.org/ +url = https://www.commcarehq.org/ # Login username of the webuser login_username = automation.user.commcarehq@gmail.com # Login password of the webuser diff --git a/Features/DataDictionary/testCases/report.html b/Features/DataDictionary/testCases/report.html index 5b4df70f9..9ea52e61f 100644 --- a/Features/DataDictionary/testCases/report.html +++ b/Features/DataDictionary/testCases/report.html @@ -328,7 +328,7 @@

report.html

-

Report generated on 02-Sep-2025 at 14:36:20 by pytest-html +

Report generated on 23-Sep-2025 at 11:31:16 by pytest-html v4.1.1

Environment

@@ -382,7 +382,7 @@

Environment

Summary

-

10 tests took 00:07:58.

+

1 test took 00:01:43.

(Un)check the boxes to filter the results.