diff --git a/.github/workflows/data-dictionary-tests.yml b/.github/workflows/data-dictionary-tests.yml
index 71d393d06..6ceca5654 100644
--- a/.github/workflows/data-dictionary-tests.yml
+++ b/.github/workflows/data-dictionary-tests.yml
@@ -83,7 +83,7 @@ jobs:
# 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
+ done < data_dictionary_tests_counts_${{ matrix.environment }}.txt
- name: Archive test results
id: artifact-upload-step
diff --git a/.github/workflows/power-bi-tests.yml b/.github/workflows/power-bi-tests.yml
index 3482c1b85..795cde5b6 100644
--- a/.github/workflows/power-bi-tests.yml
+++ b/.github/workflows/power-bi-tests.yml
@@ -73,7 +73,7 @@ jobs:
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
+ pytest -v --rootdir= Features/Powerbi_integration_exports/testCases -n 1 --dist=loadfile --reruns 1 --html=power_bi_reports_${{ matrix.environment }}.html
- name: Parse test counts
diff --git a/.gitignore b/.gitignore
index f47195c5a..d3b95614a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,8 @@ MANIFEST
*.manifest
*.spec
+*/slack_charts/*
+
#Ignoring compliled files of RequestAPI
.idea/
pytest_html_report.html
@@ -238,6 +240,17 @@ Features/Lookuptable/settings.cfg
Features/Lookuptable/report.html
Features/Lookuptable/testCases/report.html
+#Ignoring compliled files of DataDictionary
+Features/DataDictionary/testCases/report.html
+Features/DataDictionary/testCases/slack_charts
+Features/DataDictionary/test_cases/report.html
+
+#Ignoring compliled files of Powerbi_integration_exports
+Features/Powerbi_integration_exports/settings.cfg
+Features/Powerbi_integration_exports/report.html
+Features/Powerbi_integration_exports/test_cases/report.html
+Features/Powerbi_integration_exports/testCases/slack_charts
+
#Ignoring compliled files of QA_Requests
QA_Requests/BHAStressTest/settings.cfg
QA_Requests/BHAStressTest/user_inputs/*.csv
@@ -277,6 +290,8 @@ LocustScripts/update-scripts/project-config/co-carecoordination-perf/mobile_work
/POCs/PercyWebApps/settings.cfg
/POCs/VisualComparison/settings.cfg
+
+
USH_Apps/WeeklyBHACleanup/testCases/report.html
USH_Apps/WeeklyBHACleanup/report.html
USH_Apps/WeeklyBHACleanup/report_*
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
+
+
+
+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.
+
+
+
+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..0fc230cfa
--- /dev/null
+++ b/Features/DataDictionary/requires.txt
@@ -0,0 +1,22 @@
+## Stores information about all the libraries, modules, and packages that are used in this project.
+
+
+flake8>=3.8.4
+pandas>=1.2.2
+pytest
+py>=1.10.0
+pytest-html>=3.1.1
+pytest-json-report
+selenium == 4.11.0
+openpyxl
+matplotlib >= 3.3.4
+pytest-rerunfailures
+pytest-xdist
+pytest-xdist[psutil]
+pytest-order
+requests
+imap-tools
+beautifulsoup4
+html5lib
+pytest-metadata
+pyotp >=2.6.0
\ 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/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..01615e904
--- /dev/null
+++ b/Features/DataDictionary/testCases/conftest.py
@@ -0,0 +1,295 @@
+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", "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",
+ "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"]
+
+def pytest_terminal_summary(terminalreporter, exitstatus, config):
+ # Collect test counts
+ passed = terminalreporter.stats.get('passed', [])
+ failed = terminalreporter.stats.get('failed', [])
+ error = terminalreporter.stats.get('error', [])
+ skipped = terminalreporter.stats.get('skipped', [])
+ xfail = terminalreporter.stats.get('xfail', [])
+
+ env = os.environ.get("DIMAGIQA_ENV", "default_env")
+
+ # Define the filename based on the environment
+ filename = f'data_dictionary_tests_counts_{env}.txt'
+
+ # Write the counts to a file
+ with open(filename, 'w') as f:
+ f.write(f'PASSED={len(passed)}\n')
+ f.write(f'FAILED={len(failed)}\n')
+ f.write(f'ERROR={len(error)}\n')
+ f.write(f'SKIPPED={len(skipped)}\n')
+ f.write(f'XFAIL={len(xfail)}\n')
+
+# conftest.py
+import pytest
+import matplotlib.pyplot as plt
+import base64
+from io import BytesIO
+
+_test_stats = {}
+
+def pytest_sessionfinish(session, exitstatus):
+ """Collect stats at the end of the test session."""
+ tr = session.config.pluginmanager.get_plugin("terminalreporter")
+ global _test_stats
+ _test_stats = {
+ "passed": len(tr.stats.get("passed", [])),
+ "failed": len(tr.stats.get("failed", [])),
+ "skipped": len(tr.stats.get("skipped", [])),
+ "error": len(tr.stats.get("error", [])),
+ "xfail": len(tr.stats.get("xfail", [])),
+ "reruns": len(tr.stats.get("rerun", [])),
+ }
+ save_summary_charts(_test_stats)
+
+import base64
+
+def save_summary_charts(stats):
+ from pathlib import Path
+ out_dir = Path("slack_charts")
+ out_dir.mkdir(exist_ok=True)
+
+ passed = stats.get("passed", 0)
+ failed = stats.get("failed", 0)
+ skipped = stats.get("skipped", 0)
+ reruns = stats.get("reruns", 0)
+
+ # --- Pie chart with legend ---
+ pie_labels = ["Passed", "Failed", "Skipped"]
+ pie_sizes = [passed, failed, skipped]
+ pie_colors = ["#66bb6a", "#ef5350", "#fad000"]
+
+ fig, ax = plt.subplots()
+ wedges, texts = ax.pie(
+ pie_sizes,
+ labels=None,
+ colors=pie_colors,
+ startangle=90,
+ wedgeprops=dict(width=0.4)
+ )
+ ax.axis("equal")
+ ax.set_title("Test Summary")
+
+ # Add legend with counts
+ ax.legend(
+ [f"Passed: {passed}", f"Failed: {failed}", f"Skipped: {skipped}"],
+ loc="lower center",
+ ncol=3,
+ bbox_to_anchor=(0.5, -0.15)
+ )
+
+ fig.savefig(out_dir / "summary_pie.png", bbox_inches="tight")
+ plt.close(fig)
+
+ # --- Bar chart with labels + legend ---
+ bar_path = None
+ if failed > 0 or reruns > 0: # ✅ only generate if needed
+ fig, ax = plt.subplots()
+ bars = ax.bar(
+ ["Failed", "Reruns"],
+ [failed, reruns],
+ color=["#ef5350", "#ffa726"]
+ )
+ ax.set_ylabel("Number of Tests")
+ ax.set_title("Failures and Reruns")
+
+ # Add counts above bars
+ for bar in bars:
+ height = bar.get_height()
+ ax.text(
+ bar.get_x() + bar.get_width() / 2,
+ height + 0.05,
+ str(int(height)),
+ ha="center",
+ va="bottom",
+ fontsize=10,
+ fontweight="bold"
+ )
+
+ # Legend with counts
+ ax.legend(
+ [f"Failed: {failed}", f"Reruns: {reruns}"],
+ loc="lower center",
+ ncol=2,
+ bbox_to_anchor=(0.5, -0.15)
+ )
+
+ bar_path = out_dir / "summary_bar.png"
+ fig.savefig(bar_path, bbox_inches="tight")
+ plt.close(fig)
+
+ # --- Combine ---
+ combine_charts(
+ pie_path=out_dir / "summary_pie.png",
+ bar_path=bar_path,
+ combined_path=out_dir / "summary_combined.png"
+ )
+
+
+import matplotlib.pyplot as plt
+from PIL import Image
+
+def combine_charts(pie_path="slack_charts/summary_pie.png",
+ bar_path=None,
+ combined_path="slack_charts/summary_combined.png"):
+ """Combine pie and bar charts side by side if bar exists, else only pie."""
+ from PIL import Image
+
+ pie = Image.open(pie_path)
+
+ if bar_path and Path(bar_path).exists():
+ bar = Image.open(bar_path)
+ bar = bar.resize((bar.width * pie.height // bar.height, pie.height))
+ combined = Image.new("RGB", (pie.width + bar.width, pie.height), (255, 255, 255))
+ combined.paste(pie, (0, 0))
+ combined.paste(bar, (pie.width, 0))
+ else:
+ # Only pie chart
+ combined = pie.copy()
+
+ combined.save(combined_path)
+ print(f"✅ Combined chart saved to {combined_path}")
+
+
+
+def _matplotlib_img(fig) -> str:
+ """Convert a matplotlib figure to base64 string."""
+ buf = BytesIO()
+ plt.tight_layout()
+ fig.savefig(buf, format="png")
+ plt.close(fig)
+ buf.seek(0)
+ return base64.b64encode(buf.read()).decode("utf-8")
+
+def pytest_html_results_summary(prefix, summary, postfix, session):
+ """Inject donut pie + bar chart with reruns support (parallel-safe)."""
+ tr = session.config.pluginmanager.get_plugin("terminalreporter")
+ stats = tr.stats if tr and hasattr(tr, "stats") else {}
+
+ passed = len(stats.get("passed", []))
+ failed = len(stats.get("failed", []))
+ skipped = len(stats.get("skipped", []))
+
+ # Reruns are recorded separately by pytest-rerunfailures
+ reruns = len(stats.get("rerun", []))
+
+ # --- Donut Pie Chart (Passed, Failed, Skipped) ---
+ pie_labels = ["Passed", "Failed", "Skipped"]
+ pie_sizes = [passed, failed, skipped]
+ pie_colors = ["#66bb6a", "#ef5350", "#fad000"]
+
+ fig, ax = plt.subplots()
+ wedges, texts = ax.pie(
+ pie_sizes,
+ labels=None,
+ colors=pie_colors,
+ startangle=90,
+ wedgeprops=dict(width=0.4)
+ )
+ ax.axis("equal")
+
+ # Legend below the donut
+ plt.legend(
+ wedges,
+ [f"{l}: {v}" for l, v in zip(pie_labels, pie_sizes)],
+ title="Results",
+ loc="upper center",
+ bbox_to_anchor=(0.5, -0.08),
+ ncol=len(pie_labels)
+ )
+ pie_img = _matplotlib_img(fig)
+
+ # --- Bar Chart (Failures + Reruns) ---
+ bar_img = None
+ if failed > 0 or reruns > 0:
+ fig, ax = plt.subplots()
+ bars = ax.bar(["Failed", "Reruns"], [failed, reruns], color=["#ef5350", "#ff9933"])
+ ax.set_title("Failures and Reruns")
+ ax.set_ylabel("Number of Tests")
+ plt.legend(
+ bars,
+ [f"Failed: {failed}", f"Reruns: {reruns}"],
+ loc="upper center",
+ bbox_to_anchor=(0.5, -0.12),
+ ncol=2
+ )
+ bar_img = _matplotlib_img(fig)
+
+ # --- Embed in HTML report ---
+ html = "
"
+ html += f"
Test Summary

"
+ if bar_img:
+ html += f"
Failures and Reruns

"
+ html += "
"
+
+ summary.append(html)
diff --git a/Features/DataDictionary/testCases/test_01_data_dictionary_tests.py b/Features/DataDictionary/testCases/test_01_data_dictionary_tests.py
new file mode 100644
index 000000000..d134751a4
--- /dev/null
+++ b/Features/DataDictionary/testCases/test_01_data_dictionary_tests.py
@@ -0,0 +1,313 @@
+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.roles_permissions_page import RolesPermissionPage
+from common_utilities.hq_login.login_page import LoginPage
+from HQSmokeTests.testPages.users.web_user_page import WebUsersPage
+from HQSmokeTests.testPages.users.org_structure_page import latest_download_file
+
+
+""""Contains test cases related to the Data module"""
+
+values = dict()
+
+@pytest.mark.lookup
+def test_case_01_verify_data_dictionary_ui(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_dropdown_values()
+
+def test_case_02_validate_editing_case_property_values(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.edit_case_property_description()
+
+def test_case_03_validate_case_property_addition(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.add_new_case_property()
+ data.case_property_deletion()
+
+def test_case_04_validate_case_group_addition(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.adding_a_new_group()
+
+def test_case_05_verify_deprecate_property(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_deprecate_restore_case_property("Y")
+
+def test_case_06_verify_add_group_description(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.updating_group_description()
+
+def test_case_07_verify_downloading_dd_file(driver, settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.verify_file_getting_downloaded()
+
+def test_case_08_verify_uploading_dd_file(driver, settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ download_path =latest_download_file()
+ data.verify_uploading_dd(download_path)
+
+def test_case_09_validate_deprecate_case_type(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ data.case_type_restore()
+
+def test_case_10_validate_deprecate_case_types_reports(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ home.reports_menu()
+ data.verify_reports()
+ home.data_menu()
+ data.case_type_restore()
+
+def test_case_11_validate_deprecate_restore_data_exports(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ home.data_menu()
+ data.verify_exports()
+ home.data_menu()
+ data.case_type_restore()
+ home.data_menu()
+ data.verify_exports()
+
+def test_case_12_validate_deprecate_restore_case_exports(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.create_case_export()
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ data.validate_exports()
+ data.case_type_restore()
+ home.data_menu()
+ data.validate_exports()
+
+def test_case_13_validate_case_type_deprecate_restore_on_data_exports(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ data.validate_exports_edit_data_section(UserData.data_upload_path)
+ data.case_type_restore()
+ home.data_menu()
+ data.validate_exports_edit_data_section(UserData.data_upload_path)
+
+def test_case_14_verify_deprecate_cases_under_messaging_menu(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ menu = HomePage(driver, settings)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.case_type_deprecate()
+ menu.messaging_menu()
+ data.verify_conditional_alert_under_messaging()
+ home.data_menu()
+ data.case_type_restore()
+ menu.messaging_menu()
+ data.verify_conditional_alert_under_messaging()
+
+
+def test_case_15_validate_date_type_valid_values(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_valid_values_date_type()
+
+def test_case_16_validate_multiple_choice_type_valid_values(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_valid_values_multiple_choice_type()
+
+def test_case_17_validate_downloaded_valid_values(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.verify_file_getting_downloaded()
+ download_path = latest_download_file()
+ data.verify_excel_verification(download_path)
+
+def test_case_18_verify_uploading_updated_file(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.verify_file_getting_downloaded()
+ download_path = latest_download_file()
+ data.verify_update_excel(download_path)
+ data.verify_uploading_dd(download_path)
+
+def test_case_19_validate_invalid_valid_values(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("N")
+ data.verify_file_getting_downloaded()
+ download_path = latest_download_file()
+ data.verify_updating_excel_invalid_values(download_path)
+ data.verify_uploading_dd(download_path)
+
+
+def test_case_20_verify_added_property_under_case_management_tab(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_add_property_description()
+ home.applications_menu(UserData.application)
+ data.verify_case_management()
+ data.validating_app_summary()
+
+def test_case_21_verify_restore_case_property(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.verify_deprecate_restore_case_property("N")
+ home.applications_menu(UserData.application)
+ data.verify_case_management()
+ data.verify_warning_message()
+ home.data_menu()
+ data.verify_data_page("N")
+ data.verify_restore_case_property()
+
+def test_case_22_verify_case_list_explorer_report(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ property_value = data.add_new_case_property()
+ home.reports_menu()
+ data.view_case_list_explorer_report(property_value,'yes')
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.case_property_deletion()
+ home.reports_menu()
+ data.view_case_list_explorer_report(property_value,'no')
+
+def test_case_23_verify_roles_permission_with_dd_access(driver,settings):
+ login = LoginPage(driver, settings["url"])
+ menu = HomePage(driver, settings)
+ role = RolesPermissionPage(driver, settings)
+ web_user1 = WebUsersPage(driver)
+ data = DataDictionaryPage(driver)
+ login.logout()
+ login.login(settings["login_username"], settings["login_password"])
+ menu.users_menu()
+ role.roles_menu_click()
+ print("Opened Roles and Permissions Page")
+ role_name1 = role.add_non_admin_role_dd(1)
+ print (role_name1)
+ menu.users_menu()
+ web_user1.edit_user_permission(role_name1)
+ login.logout()
+ login.login(UserData.p1p2_user, settings["login_password"])
+ data.verify_data_dictionary_access_page()
+ login.logout()
+ login.login(settings["login_username"], settings["login_password"])
+ menu.users_menu()
+ role.roles_menu_click()
+ print("Opened Roles and Permissions Page")
+ role_name1 = role.add_non_admin_role_dd(2)
+ print(role_name1)
+ time.sleep(2)
+ menu.users_menu()
+ web_user1.edit_user_permission(role_name1)
+ login.logout()
+ login.login(UserData.p1p2_user, settings["login_password"])
+ data.verify_data_dictionary_access_page()
+ login.logout()
+ login.login(settings["login_username"], settings["login_password"])
+ menu.users_menu()
+ role.roles_menu_click()
+ print("Opened Roles and Permissions Page")
+ role_name1 = role.add_non_admin_role_dd(3)
+ print(role_name1)
+ menu.users_menu()
+ web_user1.edit_user_permission(role_name1)
+ login.logout()
+ login.login(UserData.p1p2_user, settings["login_password"])
+ data.verify_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_24_validate_case_importer_valid_values(driver,settings):
+ home = HomePage(driver, settings)
+ imp = ImportCasesPage(driver)
+ home.data_menu()
+ imp.replace_property_and_upload(UserData.case_type, UserData.file, "Yes", ['Hindi', 'Telugu','YYYY-MM-DD'],
+ ['select_dd_language', 'opened_date'])
+
+
+def test_case_25_verify_making_a_new_version_for_deprecated_case_type(driver,settings):
+ home = HomePage(driver, settings)
+ data = DataDictionaryPage(driver)
+ home.data_menu()
+ data.verify_data_page("Y")
+ data.case_type_deprecate()
+ home.applications_menu(UserData.application)
+ data.validating_application()
+ #data.verify_case_data_page()
+ home.data_menu()
+ data.verify_data_page("Y")
+
+
+
+
+
+
+
+
+
+
+
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..314d40d73
--- /dev/null
+++ b/Features/DataDictionary/testPages/data/data_dictionary_page.py
@@ -0,0 +1,525 @@
+import os
+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.dd_language =(By.XPATH,"(//select[@class='form-control'])[1]")
+ self.upload = (By.XPATH, "//*[@id='gtm-upload-dict']")
+ self.choose_file_text_field = (By.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.restore_case_type_new =( By.XPATH,"//button[@title='Restore Property']")
+ 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[@id='data-dictionary-table']//div[1]//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'])")
+ self.valid_description = (By.XPATH, "(//input[@placeholder='description'])")
+ self.done = (By.XPATH, "(//button[@class='btn btn-primary'][normalize-space()='Done'])[1]")
+ self.property_deprecate = (By.XPATH, "(//*[@id='gtm-deprecate-case-property'])[5]")
+ self.property_deprecate_message = (By.XPATH, "(//*[contains(text(),'Property has been deprecated')])[3]")
+ self.data_bold = (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_report = (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 verify_data_page(self,flag):
+ self.js_click(self.data_dictionary, 5)
+ if self.is_present_and_displayed(self.case_type_value):
+ assert self.is_present_and_displayed(self.case_type_value, 2)
+ print("case-dd page is opened")
+ else:
+ self.case_type_restore()
+ if flag =='Y':
+ self.wait_to_click(self.case_type_value)
+
+ def verify_dropdown_values(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 verify_file_getting_downloaded(self):
+ self.wait_to_click(self.check, 2)
+ print("File is downloaded")
+ time.sleep(5)
+
+
+ def verify_uploading_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" + fetch_random_string())
+ self.wait_to_click(self.datatype)
+ 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)
+ value_text = self.wait_to_get_value(self.property_value)
+ print("new property added :", value_text)
+ return value_text
+
+ def view_case_list_explorer_report(self, value1, flag):
+ self.wait_to_click(self.case_list_explorer_report)
+ 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 case_property_deletion(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 adding_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 updating_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.add_group)
+ self.wait_to_click(self.save, 10)
+ print("group description is updated")
+
+ def deprecating_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 case_type_deprecate(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, 30)
+ print("case type has been deprecated")
+ self.wait_to_click(self.data_bold)
+
+ def case_type_restore(self):
+ self.wait_to_click(self.data_bold,10)
+ self.js_click(self.data_dictionary,10)
+ self.wait_to_click(self.show_deprecated_case_type,30)
+ self.wait_to_click(self.case_type_value)
+ self.wait_to_click(self.restore_case_type)
+ print("case type has been restored")
+
+ def validating_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.js_click(self.case_list, 20)
+ 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 validating_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 verify_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")
+ time.sleep(20)
+
+
+ def verify_reports(self):
+ time.sleep(50)
+ self.wait_to_click(self.case_list_explorer_report, 20)
+ 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 verify_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,10)
+ self.wait_to_click(self.model_type,60)
+ 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,20)
+ self.wait_to_click(self.model_type,200)
+ 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.data_bold)
+ self.wait_to_click(self.export_case_data_link, 10)
+ self.wait_to_click(self.add_export_button, 200)
+ self.is_visible_and_displayed(self.case_type_dropdown, 200)
+ self.wait_for_element(self.case_type_dropdown, 200)
+ self.select_by_text(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 validate_exports_edit_data_section(self, filename):
+ self.wait_for_element(self.data_bold, 10)
+ self.js_click(self.data_bold,10)
+ 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 = os.path.abspath(os.path.join(UserData.user_input_base_dir, str(filename)))
+ # filepath = str(UserData.user_input_base_dir + "\\" + filepath)
+ print("File Path: ", filepath)
+ self.wait_for_element(self.choose_file_text_field)
+ print("File Path: ", filepath)
+ self.send_keys(self.choose_file_text_field, 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.")
+ self.js_click(self.data_dictionary)
+
+
+ def verify_conditional_alert_under_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 verify_valid_values_date_type(self):
+ self.select_by_text(self.case_property_vv,'Date')
+ self.is_present_and_displayed(self.date_valid_values)
+ print("YYYY-MM-DD Valid values are getting displayed")
+ self.wait_to_click(self.case_property_vv)
+ self.wait_to_click(self.save)
+
+ def verify_valid_values_multiple_choice_type(self):
+ self.select_by_text(self.dd_language, 'Multiple Choice')
+ 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.js_click(self.done)
+
+
+ def verify_resetting_valid_values(self):
+ self.wait_to_click(self.edit_button)
+ self.wait_to_click(self.delete_button_vv)
+ self.select_by_text(self.case_property,'Plain')
+ self.wait_to_click(self.save)
+
+ def verify_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 verify_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 verify_updating_excel_invalid_values(self, download_path1):
+ download_path1 = str(PathSettings.DOWNLOAD_PATH / download_path1)
+ excel = ExcelManager()
+ excel.write_excel_data('case_dd-vl', 2, 2, UserData.randomvalue1, download_path1)
+ excel.write_excel_data('case_dd-vl', 2, 3, UserData.randomvalue1, download_path1)
+
+ def verify_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 verify_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 verify_deprecate_restore_case_property(self,flag):
+ self.wait_to_click(self.property_deprecate)
+ self.wait_to_click(self.save)
+ time.sleep(5)
+ self.wait_to_click(self.show_deprecate)
+ assert self.is_present_and_displayed(self.restore_case_type_new), "case not deprecated"
+ print("case type is deprecated")
+ if flag =="Y":
+ self.wait_to_click(self.restore_case_type_new)
+ self.wait_to_click(self.save)
+ print("deprecated property restored")
+
+ def verify_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 verify_restore_case_property(self):
+ self.wait_to_click(self.case_type_value)
+ 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 verify_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.upload_button):
+ print("both view and edit access is present for the user login")
+ else:
+ print("only view access is present for the user login")
+
+ def verify_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 000000000..beebfc550
Binary files /dev/null and b/Features/DataDictionary/userInputs/case_dd.xlsx differ
diff --git a/Features/DataDictionary/userInputs/import_file.xlsx b/Features/DataDictionary/userInputs/import_file.xlsx
new file mode 100644
index 000000000..98b42c7d5
Binary files /dev/null and b/Features/DataDictionary/userInputs/import_file.xlsx differ
diff --git a/Features/DataDictionary/userInputs/user_inputs.py b/Features/DataDictionary/userInputs/user_inputs.py
new file mode 100644
index 000000000..2532091c2
--- /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 = 'English'
+ 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/Lookuptable/requires.txt b/Features/Lookuptable/requires.txt
index c144a0e26..1fb1d7849 100644
--- a/Features/Lookuptable/requires.txt
+++ b/Features/Lookuptable/requires.txt
@@ -12,6 +12,4 @@ pytest-xdist[psutil]
pyotp >=2.6.0
pytest-order
py
-beautifulsoup4
-pytest-metadata
-matplotlib
\ No newline at end of file
+beautifulsoup4
\ No newline at end of file
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
+
+
+
+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.
+
+
+
+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..0fc230cfa
--- /dev/null
+++ b/Features/Powerbi_integration_exports/requires.txt
@@ -0,0 +1,22 @@
+## Stores information about all the libraries, modules, and packages that are used in this project.
+
+
+flake8>=3.8.4
+pandas>=1.2.2
+pytest
+py>=1.10.0
+pytest-html>=3.1.1
+pytest-json-report
+selenium == 4.11.0
+openpyxl
+matplotlib >= 3.3.4
+pytest-rerunfailures
+pytest-xdist
+pytest-xdist[psutil]
+pytest-order
+requests
+imap-tools
+beautifulsoup4
+html5lib
+pytest-metadata
+pyotp >=2.6.0
\ 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/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..4ba1337f7
--- /dev/null
+++ b/Features/Powerbi_integration_exports/testCases/conftest.py
@@ -0,0 +1,295 @@
+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", "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",
+ "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"]
+
+def pytest_terminal_summary(terminalreporter, exitstatus, config):
+ # Collect test counts
+ passed = terminalreporter.stats.get('passed', [])
+ failed = terminalreporter.stats.get('failed', [])
+ error = terminalreporter.stats.get('error', [])
+ skipped = terminalreporter.stats.get('skipped', [])
+ xfail = terminalreporter.stats.get('xfail', [])
+
+ env = os.environ.get("DIMAGIQA_ENV", "default_env")
+
+ # Define the filename based on the environment
+ filename = f'powerbi_test_counts_{env}.txt'
+
+ # Write the counts to a file
+ with open(filename, 'w') as f:
+ f.write(f'PASSED={len(passed)}\n')
+ f.write(f'FAILED={len(failed)}\n')
+ f.write(f'ERROR={len(error)}\n')
+ f.write(f'SKIPPED={len(skipped)}\n')
+ f.write(f'XFAIL={len(xfail)}\n')
+
+# conftest.py
+import pytest
+import matplotlib.pyplot as plt
+import base64
+from io import BytesIO
+
+_test_stats = {}
+
+def pytest_sessionfinish(session, exitstatus):
+ """Collect stats at the end of the test session."""
+ tr = session.config.pluginmanager.get_plugin("terminalreporter")
+ global _test_stats
+ _test_stats = {
+ "passed": len(tr.stats.get("passed", [])),
+ "failed": len(tr.stats.get("failed", [])),
+ "skipped": len(tr.stats.get("skipped", [])),
+ "error": len(tr.stats.get("error", [])),
+ "xfail": len(tr.stats.get("xfail", [])),
+ "reruns": len(tr.stats.get("rerun", [])),
+ }
+ save_summary_charts(_test_stats)
+
+import base64
+
+def save_summary_charts(stats):
+ from pathlib import Path
+ out_dir = Path("slack_charts")
+ out_dir.mkdir(exist_ok=True)
+
+ passed = stats.get("passed", 0)
+ failed = stats.get("failed", 0)
+ skipped = stats.get("skipped", 0)
+ reruns = stats.get("reruns", 0)
+
+ # --- Pie chart with legend ---
+ pie_labels = ["Passed", "Failed", "Skipped"]
+ pie_sizes = [passed, failed, skipped]
+ pie_colors = ["#66bb6a", "#ef5350", "#fad000"]
+
+ fig, ax = plt.subplots()
+ wedges, texts = ax.pie(
+ pie_sizes,
+ labels=None,
+ colors=pie_colors,
+ startangle=90,
+ wedgeprops=dict(width=0.4)
+ )
+ ax.axis("equal")
+ ax.set_title("Test Summary")
+
+ # Add legend with counts
+ ax.legend(
+ [f"Passed: {passed}", f"Failed: {failed}", f"Skipped: {skipped}"],
+ loc="lower center",
+ ncol=3,
+ bbox_to_anchor=(0.5, -0.15)
+ )
+
+ fig.savefig(out_dir / "summary_pie.png", bbox_inches="tight")
+ plt.close(fig)
+
+ # --- Bar chart with labels + legend ---
+ bar_path = None
+ if failed > 0 or reruns > 0: # ✅ only generate if needed
+ fig, ax = plt.subplots()
+ bars = ax.bar(
+ ["Failed", "Reruns"],
+ [failed, reruns],
+ color=["#ef5350", "#ffa726"]
+ )
+ ax.set_ylabel("Number of Tests")
+ ax.set_title("Failures and Reruns")
+
+ # Add counts above bars
+ for bar in bars:
+ height = bar.get_height()
+ ax.text(
+ bar.get_x() + bar.get_width() / 2,
+ height + 0.05,
+ str(int(height)),
+ ha="center",
+ va="bottom",
+ fontsize=10,
+ fontweight="bold"
+ )
+
+ # Legend with counts
+ ax.legend(
+ [f"Failed: {failed}", f"Reruns: {reruns}"],
+ loc="lower center",
+ ncol=2,
+ bbox_to_anchor=(0.5, -0.15)
+ )
+
+ bar_path = out_dir / "summary_bar.png"
+ fig.savefig(bar_path, bbox_inches="tight")
+ plt.close(fig)
+
+ # --- Combine ---
+ combine_charts(
+ pie_path=out_dir / "summary_pie.png",
+ bar_path=bar_path,
+ combined_path=out_dir / "summary_combined.png"
+ )
+
+
+import matplotlib.pyplot as plt
+from PIL import Image
+
+def combine_charts(pie_path="slack_charts/summary_pie.png",
+ bar_path=None,
+ combined_path="slack_charts/summary_combined.png"):
+ """Combine pie and bar charts side by side if bar exists, else only pie."""
+ from PIL import Image
+
+ pie = Image.open(pie_path)
+
+ if bar_path and Path(bar_path).exists():
+ bar = Image.open(bar_path)
+ bar = bar.resize((bar.width * pie.height // bar.height, pie.height))
+ combined = Image.new("RGB", (pie.width + bar.width, pie.height), (255, 255, 255))
+ combined.paste(pie, (0, 0))
+ combined.paste(bar, (pie.width, 0))
+ else:
+ # Only pie chart
+ combined = pie.copy()
+
+ combined.save(combined_path)
+ print(f"✅ Combined chart saved to {combined_path}")
+
+
+
+def _matplotlib_img(fig) -> str:
+ """Convert a matplotlib figure to base64 string."""
+ buf = BytesIO()
+ plt.tight_layout()
+ fig.savefig(buf, format="png")
+ plt.close(fig)
+ buf.seek(0)
+ return base64.b64encode(buf.read()).decode("utf-8")
+
+def pytest_html_results_summary(prefix, summary, postfix, session):
+ """Inject donut pie + bar chart with reruns support (parallel-safe)."""
+ tr = session.config.pluginmanager.get_plugin("terminalreporter")
+ stats = tr.stats if tr and hasattr(tr, "stats") else {}
+
+ passed = len(stats.get("passed", []))
+ failed = len(stats.get("failed", []))
+ skipped = len(stats.get("skipped", []))
+
+ # Reruns are recorded separately by pytest-rerunfailures
+ reruns = len(stats.get("rerun", []))
+
+ # --- Donut Pie Chart (Passed, Failed, Skipped) ---
+ pie_labels = ["Passed", "Failed", "Skipped"]
+ pie_sizes = [passed, failed, skipped]
+ pie_colors = ["#66bb6a", "#ef5350", "#fad000"]
+
+ fig, ax = plt.subplots()
+ wedges, texts = ax.pie(
+ pie_sizes,
+ labels=None,
+ colors=pie_colors,
+ startangle=90,
+ wedgeprops=dict(width=0.4)
+ )
+ ax.axis("equal")
+
+ # Legend below the donut
+ plt.legend(
+ wedges,
+ [f"{l}: {v}" for l, v in zip(pie_labels, pie_sizes)],
+ title="Results",
+ loc="upper center",
+ bbox_to_anchor=(0.5, -0.08),
+ ncol=len(pie_labels)
+ )
+ pie_img = _matplotlib_img(fig)
+
+ # --- Bar Chart (Failures + Reruns) ---
+ bar_img = None
+ if failed > 0 or reruns > 0:
+ fig, ax = plt.subplots()
+ bars = ax.bar(["Failed", "Reruns"], [failed, reruns], color=["#ef5350", "#ff9933"])
+ ax.set_title("Failures and Reruns")
+ ax.set_ylabel("Number of Tests")
+ plt.legend(
+ bars,
+ [f"Failed: {failed}", f"Reruns: {reruns}"],
+ loc="upper center",
+ bbox_to_anchor=(0.5, -0.12),
+ ncol=2
+ )
+ bar_img = _matplotlib_img(fig)
+
+ # --- Embed in HTML report ---
+ html = ""
+ html += f"
Test Summary

"
+ if bar_img:
+ html += f"
Failures and Reruns

"
+ html += "
"
+
+ summary.append(html)
diff --git a/Features/Powerbi_integration_exports/testCases/test_01_power_bi_tests.py b/Features/Powerbi_integration_exports/testCases/test_01_power_bi_tests.py
new file mode 100644
index 000000000..34e475985
--- /dev/null
+++ b/Features/Powerbi_integration_exports/testCases/test_01_power_bi_tests.py
@@ -0,0 +1,198 @@
+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.powerbi
+def test_case_01_verify_odata_feed_ui(driver,settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+
+@pytest.mark.powerbi
+def test_case_02_verify_odata_feed_select_type(driver,settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_feed_type()
+
+@pytest.mark.powerbi
+def test_case_03_verify_odata_feed_select_form(driver,settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+
+@pytest.mark.powerbi
+def test_case_04_verify_odata_feed_select_application(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.application_dropdown(UserData.reassign_cases_application)
+
+@pytest.mark.powerbi
+def test_case_05_verify_odata_feed_select_menu(driver, settings):
+ home = (HomePage(driver, settings))
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.application_dropdown(UserData.reassign_cases_application)
+ data.menu_dropdown()
+
+@pytest.mark.powerbi
+def test_case_06_verify_odata_feed_cancel_selection(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.cancel_feed()
+
+def test_case_07_verify_save_and_delete_odata_feed_form(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.save_odata_feed()
+ data.delete_feed()
+
+@pytest.mark.powerbi
+def test_case_08_verify_save_odata_feed_form(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+
+@pytest.mark.powerbi
+def test_case_09_validate_odata_feed_show_advance_question(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application, UserData.reassign_menu, UserData.reassign_form)
+ data.adding_odata_feed()
+ data.show_advance_question()
+
+@pytest.mark.powerbi
+def test_case_10_verify_save_and_delete_odata_feed_case(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_case()
+ data.create_case_feed(UserData.case)
+ data.adding_odata_feed()
+ data.save_odata_feed()
+ data.delete_feed()
+
+
+@pytest.mark.powerbi
+def test_case_11_verify_save_and_copy_odata_feed(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.save_odata_feed()
+ data.copy_edit_feed()
+
+@pytest.mark.powerbi
+def test_case_12_verify_add_description_to_odata_feed(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.add_description()
+
+@pytest.mark.powerbi
+def test_case_13_verify_bulk_create_bulk_delete_odata_feed(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.create_multiple_odata_feed(10)
+ data.validate_go_to_page()
+ data.power_bi_tableau_integration_bulk_delete()
+
+@pytest.mark.powerbi
+def test_case_14_verify_delete_questions_on_odata_feed_page(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.delete_questions()
+
+@pytest.mark.powerbi
+def test_case_15_verify_deidentified_odata_feed(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.validate_de_identified()
+
+@pytest.mark.powerbi
+def test_case_16_verify_view_created_odata_feed(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ username = settings["login_username"]
+ password = settings["login_password"]
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ data.adding_odata_feed()
+ data.add_description()
+ data.view_odata_feed(username, password)
+
+@pytest.mark.powerbi
+def test_case_17_verify_odata_feed_edit_filters(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_form()
+ data.form_feed(UserData.Basic_tests_application,UserData.Basic_menu,UserData.Repeat_form)
+ data.adding_odata_feed()
+ data.verify_repeat_checkbox()
+ data.save_odata_feed()
+ data.edit_filters()
+
+@pytest.mark.powerbi
+def test_case_18_verify_odata_feed_include_parent(driver, settings):
+ home = HomePage(driver, settings)
+ data = PowerBiPage(driver)
+ home.data_menu()
+ data.power_bi_page_ui('y')
+ data.select_case()
+ data.create_case_feed(UserData.parent)
+ data.adding_odata_feed()
+ data.verify_parent_checkbox()
+ 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..5325cd810
--- /dev/null
+++ b/Features/Powerbi_integration_exports/testPages/data/power_bi_page.py
@@ -0,0 +1,275 @@
+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.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.disabled_button =(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_identified_text = (By.XPATH,"//*[@id='is_deidentified']")
+ self.label = (By.XPATH,"(//label[@data-bind='visible: isDeid()'][normalize-space()='De-Identified'])[1]")
+ 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_checkbox_new =(By.XPATH,"//*[@id='customize-export']/form/fieldset[2]/div[4]/legend/span[1]/input")
+
+ def power_bi_page_ui(self,flag):
+ self.wait_to_click(self.power_bi,2)
+ #initially to delete the existing files.
+ if flag == 'Y':
+ 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,select_app):
+ 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,select_app)
+
+ 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_feed(self):
+ time.sleep(10)
+ self.wait_to_click(self.cancel_button,10)
+
+ def form_feed(self,app_select,menu_select,form_select):
+ self.wait_to_click(self.applications)
+ self.select_by_text(self.applications,app_select)
+ 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 adding_odata_feed(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 verify_repeat_checkbox(self):
+ self.scroll_to_element(self.repeat_checkbox)
+ self.wait_to_click(self.repeat_checkbox)
+ self.scroll_to_element(self.repeat_checkbox_new)
+ self.wait_to_click(self.repeat_checkbox_new)
+
+ def verify_parent_checkbox(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 create_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 add_description(self):
+ self.wait_to_click(self.description,5)
+ self.send_keys(self.description, UserData.description+Keys.TAB)
+ assert self.is_present_and_displayed(self.disabled_button,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 delete_feed(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_odata_feed(self,no_of_feeds):
+ for i in range(1,no_of_feeds):
+ self.power_bi_page_ui(10)
+ self.select_form()
+ self.form_feed(UserData.reassign_cases_application,UserData.reassign_menu,UserData.reassign_form)
+ self.adding_odata_feed()
+ self.save_odata_feed()
+ print("create multiple odata feeds completed")
+
+
+ def validate_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 validate_de_identified(self):
+ self.scroll_to_bottom()
+ self.js_click(self.allow_sensitive,2)
+ self.js_click(self.de_identified_text,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..e73c879a8
--- /dev/null
+++ b/Features/Powerbi_integration_exports/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/HQSmokeTests/testPages/users/roles_permissions_page.py b/HQSmokeTests/testPages/users/roles_permissions_page.py
index 37de06820..432fd06c1 100644
--- a/HQSmokeTests/testPages/users/roles_permissions_page.py
+++ b/HQSmokeTests/testPages/users/roles_permissions_page.py
@@ -48,6 +48,9 @@ def __init__(self, driver, settings):
self.confirm_role_delete = (By.XPATH, "//div[@class='btn btn-danger']")
self.full_org_access_checkbox = (By.XPATH, "//label[contains(.,'Full Organization Access')]//following-sibling::div//input")
self.access_all_reports_checkbox = (By.XPATH, "//input[@id='access-all-reports-checkbox']")
+ self.edit_data = (By.XPATH, "//div[@id='user-roles-table']/div[@class='panel-body']/div[@class='modal fade in']/div[@class='modal-dialog']/form/div[@class='modal-content']/div[@class='modal-body']/div[@class='form form-horizontal']/fieldset/div[3]/div[@class='form-group'][7]/div[@class='col-sm-2 controls'][1]/div[@class='form-check']/label")
+ self.view_data_dictionary = (By.XPATH, "//input[@id='view-data-dict-checkbox']")
+ self.edit_data_dictionary = (By.XPATH, "//input[@id='edit-data-dict-checkbox']")
self.web_user_permission = "//th[./span[.='{}']]//following-sibling::td/div[contains(@data-bind,'edit_web_users')]/i[contains(@class,'check')]"
self.mobile_worker_permission = "//th[./span[.='{}']]//following-sibling::td/div[contains(@data-bind,'edit_commcare_users')]/i[contains(@class,'check')]"
@@ -124,9 +127,7 @@ def delete_test_roles(self):
def add_non_admin_role(self):
self.wait_to_click(self.add_new_role)
- self.wait_for_element(self.role_name)
- self.send_keys(self.role_name, self.role_non_admin_created)
-
+ self.wait_to_clear_and_send_keys(self.role_name, self.role_non_admin_created)
self.wait_to_click(self.edit_mobile_worker_checkbox)
self.scroll_to_element(self.access_all_reports_checkbox)
is_checked = self.get_attribute(self.access_all_reports_checkbox, 'checked')
@@ -151,38 +152,25 @@ def add_non_admin_role(self):
self.wait_to_click(self.save_button)
assert self.is_present_and_displayed(self.role_non_admin), "Role not added successfully!"
- print("Role added successfully")
return self.role_non_admin_created
-
- def add_shared_export_role(self, name, flag='NO'):
+ def add_non_admin_role_dd(self, value):
self.wait_to_click(self.add_new_role)
- self.wait_to_clear_and_send_keys(self.role_name, name)
- time.sleep(1)
- self.click(self.edit_mobile_worker_checkbox)
- time.sleep(0.5)
- self.click(self.edit_web_user_checkbox)
- time.sleep(0.5)
- self.click(self.data_checkbox)
- time.sleep(0.5)
- self.scroll_to_element(self.manage_shared_exports)
- if flag == 'YES':
- time.sleep(2)
- self.click(self.manage_shared_exports)
- time.sleep(2)
- time.sleep(0.5)
- self.scroll_to_element(self.access_all_reports_checkbox)
+ self.wait_to_clear_and_send_keys(self.role_name, self.role_non_admin_created)
time.sleep(1)
- self.click(self.access_all_reports_checkbox)
- time.sleep(0.5)
+ self.js_click(self.edit_data,5)
+ self.js_click(self.view_data_dictionary)
+ if value == 1:
+ print("only view access selected")
+ elif value ==2:
+ time.sleep(5)
+ self.js_click(self.edit_data_dictionary)
+ else:
+ self.js_click(self.view_data_dictionary)
+ self.js_click(self.edit_data_dictionary)
+ self.js_click(self.edit_data_dictionary)
self.scroll_to_element(self.save_button)
time.sleep(0.5)
- self.click(self.save_button)
+ self.js_click(self.save_button)
time.sleep(2)
- assert self.is_present_and_displayed((By.XPATH, self.role_no_shared_export.format(name))), "Role not added successfully!"
- assert self.is_present_and_displayed((By.XPATH, self.web_user_permission.format(name))), "Web User Permission not present"
- assert self.is_present_and_displayed((By.XPATH, self.mobile_worker_permission.format(name))), "Mobile Worker Permission not present"
- if flag == "NO":
- assert not self.is_present_and_displayed((By.XPATH, self.managed_shared_export_permission.format(name)), 5), "Shared Export Permission is present"
- else:
- assert self.is_present_and_displayed((By.XPATH, self.managed_shared_export_permission.format(name))), "Shared Export Permission not present"
+ return self.role_non_admin_created
diff --git a/HQSmokeTests/testPages/users/web_user_page.py b/HQSmokeTests/testPages/users/web_user_page.py
index 321b3a837..a1b93216b 100644
--- a/HQSmokeTests/testPages/users/web_user_page.py
+++ b/HQSmokeTests/testPages/users/web_user_page.py
@@ -192,28 +192,25 @@ def upload_web_users(self):
assert self.is_present_and_displayed(self.import_complete, 150), "Upload Not Completed! Taking Longer to process.."
print("File uploaded successfully")
- def edit_user_permission(self, rolename):
+ def edit_user_permission(self, role_name):
self.wait_to_click(self.web_users_menu)
self.wait_for_element(self.search_user)
self.wait_to_clear_and_send_keys(self.search_user, UserData.p1p2_user)
-
+ time.sleep(2)
self.wait_to_click(self.search_user_btn)
-
- self.wait_for_element(self.user_link)
- self.wait_to_click(self.user_link)
+ self.wait_for_element(self.user_link,25)
+ self.wait_to_click(self.user_link,20)
self.wait_for_element(self.select_project_role_id)
- self.select_by_text(self.select_project_role_id, rolename)
- self.wait_to_click(self.update_role_btn)
-
+ self.select_by_text(self.select_project_role_id, role_name)
+ self.wait_to_click(self.update_role_btn,5)
self.scroll_to_element(self.location_field)
if self.is_present(self.remove_location):
self.wait_to_click(self.remove_location)
print("removed location")
-
self.wait_to_click(self.location_field)
self.send_keys(self.location_field, "Test Location")
self.wait_to_click(self.location_value)
- self.wait_to_click(self.update_location_btn)
+ self.wait_to_click(self.update_location_btn,5)
def change_user_role(self, username, role):