From ebef10001747155f8009f53c5fc2864d4f27f6b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:34:57 +0200 Subject: [PATCH 1/5] Bump fast-loops from 1.1.3 to 1.1.4 in /frontend (#4031) Bumps [fast-loops](https://github.com/robinweser/fast-loops) from 1.1.3 to 1.1.4. - [Commits](https://github.com/robinweser/fast-loops/commits) --- updated-dependencies: - dependency-name: fast-loops dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index eb4c2aadae..118293b739 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5648,9 +5648,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-loops@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" - integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.4.tgz#61bc77d518c0af5073a638c6d9d5c7683f069ce2" + integrity sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg== fast-querystring@^1.1.1: version "1.1.2" From 6a73ffe0d3fb42410d7ecbc4c9f52c6b0edd2fc3 Mon Sep 17 00:00:00 2001 From: szymon-kellton <130459593+szymon-kellton@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:13:08 +0200 Subject: [PATCH 2/5] Selenium: Deadlock part 3 (#4038) * Problem with deadlock - test skiped: test_targeting_parametrized_rules_filters_and_or * Added xfail to test_targeting_parametrized_rules_filters_and_or * Black * XFAIL test_targeting_rebuild * XFAIL test_targeting_different_program_statuses * XFAIL test_targeting_create_use_ids_individual --- backend/selenium_tests/targeting/test_targeting.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/selenium_tests/targeting/test_targeting.py b/backend/selenium_tests/targeting/test_targeting.py index 3fb08cebe6..da6360855e 100644 --- a/backend/selenium_tests/targeting/test_targeting.py +++ b/backend/selenium_tests/targeting/test_targeting.py @@ -337,6 +337,7 @@ def test_targeting_create_use_ids_hh( ) assert str(target_population.status) in pageTargetingDetails.getLabelStatus().text + @pytest.mark.xfail(reason="Problem with deadlock during test - 202318") def test_targeting_create_use_ids_individual( self, create_programs: None, From 615236161e16b19c6fd7e5e3def6b4fe16735291 Mon Sep 17 00:00:00 2001 From: szymon-kellton <130459593+szymon-kellton@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:28:58 +0200 Subject: [PATCH 3/5] Remove secrets (#4039) --- cypress/cypress/scripts/slackReport.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cypress/cypress/scripts/slackReport.js b/cypress/cypress/scripts/slackReport.js index d92ed33733..040aff16f2 100644 --- a/cypress/cypress/scripts/slackReport.js +++ b/cypress/cypress/scripts/slackReport.js @@ -10,13 +10,9 @@ const fs = require("fs"); const axios = require("axios"); const FormData = require("form-data"); -// const SLACK_BOT_USER_TOKEN = -// "xoxb-5509997426931-5523162721089-IlVaqxdRKRyKftvRAZojd7yZ"; -// const CHANNEL = "C05EKHETMT9"; - function sendMessage( data, - url = "https://hooks.slack.com/services/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" + url = URL.secret ) { request( { @@ -25,7 +21,6 @@ function sendMessage( json: data, headers: { "Content-Type": "application/json; charset=utf-8", - // Authorization: `Bearer ${SLACK_BOT_USER_TOKEN}`, }, }, function (error, response, body) { @@ -43,14 +38,12 @@ function sendMessage( async function sendFile(file_name) { const form = new FormData(); form.append("file", fs.readFileSync(file_name), file_name); - // form.append("channels", CHANNEL); await axios.post( "https://hooks.slack.com/services/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", form, { headers: { "Content-Type": "multipart/form-data", - // Authorization: `Bearer ${SLACK_BOT_USER_TOKEN}`, }, } ); From c1ea37611b7d57fd4a2f6ae68f68f8cb434b3f80 Mon Sep 17 00:00:00 2001 From: szymon-kellton <130459593+szymon-kellton@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:26:36 +0200 Subject: [PATCH 4/5] Selenium: Payment Module - Happy path (#4016) * Init * In progress test_payment_plan_happy_path * In progress test_payment_plan_happy_path * In progress test_payment_plan_happy_path * In progress test_payment_plan_happy_path * Added test_payment_plan_happy_path * Fix for test_payment_plan_happy_path * Fix for test_smoke_new_payment_plan * Fixed checkStatus --- backend/selenium_tests/conftest.py | 4 + .../helpers/date_time_format.py | 18 +- backend/selenium_tests/helpers/helper.py | 6 +- .../page_object/base_components.py | 13 ++ .../payment_module/new_payment_plan.py | 11 +- .../payment_module/payment_module_details.py | 73 +++++++ .../payment_module/test_payment_module.py | 191 +++++++++++++++++- 7 files changed, 298 insertions(+), 18 deletions(-) diff --git a/backend/selenium_tests/conftest.py b/backend/selenium_tests/conftest.py index 5da5d78d73..2997b0c493 100644 --- a/backend/selenium_tests/conftest.py +++ b/backend/selenium_tests/conftest.py @@ -176,6 +176,10 @@ def driver() -> Chrome: chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--enable-logging") chrome_options.add_argument("--window-size=1920,1080") + if not os.path.exists("./report/downloads/"): + os.makedirs("./report/downloads/") + prefs = {"download.default_directory": "./report/downloads/"} + chrome_options.add_experimental_option("prefs", prefs) yield webdriver.Chrome(options=chrome_options) diff --git a/backend/selenium_tests/helpers/date_time_format.py b/backend/selenium_tests/helpers/date_time_format.py index fd559d4d62..20b06361f2 100644 --- a/backend/selenium_tests/helpers/date_time_format.py +++ b/backend/selenium_tests/helpers/date_time_format.py @@ -2,20 +2,30 @@ class FormatTime: - def __init__(self, day: int, month: int, year: int, hour: int = 0, minute: int = 0): + def __init__( + self, + day: int = 0, + month: int = 0, + year: int = 0, + hour: int = 0, + minute: int = 0, + time: datetime | None = None, + ): + self._datetime = time self.day = day self.month = month self.year = year self.hour = hour self.minute = minute - self.datetime = datetime(year, month, day, hour, minute) + if self.year != 0: + self._datetime = datetime(self.year, self.month, self.day, self.hour, self.minute) @property def numerically_formatted_date(self) -> str: # date format YYYY-MM-DD - return str(self.datetime.strftime("%Y-%m-%d")) + return str(self._datetime.strftime("%Y-%m-%d")) @property def date_in_text_format(self) -> str: # date format -d Mon YYYY - return str(self.datetime.strftime("%-d %b %Y")) + return str(self._datetime.strftime("%-d %b %Y")) diff --git a/backend/selenium_tests/helpers/helper.py b/backend/selenium_tests/helpers/helper.py index 076df7ee16..ee2d771c79 100644 --- a/backend/selenium_tests/helpers/helper.py +++ b/backend/selenium_tests/helpers/helper.py @@ -85,8 +85,10 @@ def check_page_after_click(self, button: WebElement, url_fragment: str) -> None: button.click() assert url_fragment in self.wait_for_new_url(programme_creation_url).split("/")[-1] - def upload_file(self, upload_file: str, xpath: str = "//input[@type='file']") -> None: - self._wait().until(EC.presence_of_element_located((By.XPATH, xpath))).send_keys(upload_file) + def upload_file( + self, upload_file: str, xpath: str = "//input[@type='file']", timeout: int = DEFAULT_TIMEOUT + ) -> None: + self._wait(timeout).until(EC.presence_of_element_located((By.XPATH, xpath))).send_keys(upload_file) def select_option_by_name(self, optionName: str) -> None: selectOption = f'li[data-cy="select-option-{optionName}"]' diff --git a/backend/selenium_tests/page_object/base_components.py b/backend/selenium_tests/page_object/base_components.py index 29e74e4428..18ed9813d9 100644 --- a/backend/selenium_tests/page_object/base_components.py +++ b/backend/selenium_tests/page_object/base_components.py @@ -46,6 +46,7 @@ class BaseComponents(Common): globalProgramFilterSearchButton = 'button[data-cy="search-icon"]' globalProgramFilterClearButton = 'button[data-cy="clear-icon"]' rows = 'tr[role="checkbox"]' + alert = '[role="alert"]' breadcrumbsChevronIcon = 'svg[data-cy="breadcrumbs-chevron-icon"]' arrowBack = 'div[data-cy="arrow_back"]' @@ -188,6 +189,18 @@ def getNavProgramLog(self) -> WebElement: def getRows(self) -> [WebElement]: return self.get_elements(self.rows) + def getAlert(self) -> WebElement: + self.wait_for(self.alert) + return self.wait_for(self.alert) + + def checkAlert(self, text: str) -> None: + self.getAlert() + for _ in range(10): + if text in self.getAlert().text: + break + sleep(1) + assert text in self.getAlert().text + def waitForNumberOfRows(self, number: int) -> bool: for _ in range(5): if len(self.getRows()) == number: diff --git a/backend/selenium_tests/page_object/payment_module/new_payment_plan.py b/backend/selenium_tests/page_object/payment_module/new_payment_plan.py index 06f39ad870..1f9771c9f1 100644 --- a/backend/selenium_tests/page_object/payment_module/new_payment_plan.py +++ b/backend/selenium_tests/page_object/payment_module/new_payment_plan.py @@ -1,4 +1,5 @@ from page_object.base_components import BaseComponents +from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement @@ -30,16 +31,18 @@ def getSelectTargetingid(self) -> WebElement: return self.wait_for(self.selectTargetingid) def getInputStartDate(self) -> WebElement: - return self.wait_for(self.inputStartDate) + self.wait_for(self.inputStartDate) + return self.wait_for(self.inputStartDate).find_elements(By.TAG_NAME, "input")[0] def getInputEndDate(self) -> WebElement: - return self.wait_for(self.inputEndDate) + self.wait_for(self.inputEndDate) + return self.wait_for(self.inputEndDate).find_elements(By.TAG_NAME, "input")[0] def getInputCurrency(self) -> WebElement: return self.wait_for(self.inputCurrency) def getInputDispersionStartDate(self) -> WebElement: - return self.wait_for(self.inputDispersionStartDate) + return self.wait_for(self.inputDispersionStartDate).find_elements(By.TAG_NAME, "input")[0] def getInputDispersionEndDate(self) -> WebElement: - return self.wait_for(self.inputDispersionEndDate) + return self.wait_for(self.inputDispersionEndDate).find_elements(By.TAG_NAME, "input")[0] diff --git a/backend/selenium_tests/page_object/payment_module/payment_module_details.py b/backend/selenium_tests/page_object/payment_module/payment_module_details.py index c6a047312e..5f82a4918a 100644 --- a/backend/selenium_tests/page_object/payment_module/payment_module_details.py +++ b/backend/selenium_tests/page_object/payment_module/payment_module_details.py @@ -1,3 +1,5 @@ +from time import sleep + from page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement @@ -8,6 +10,7 @@ class PaymentModuleDetails(BaseComponents): ppUnicefId = 'span[data-cy="pp-unicef-id"]' statusContainer = 'div[data-cy="status-container"]' buttonExportXlsx = 'button[data-cy="button-export-xlsx"]' + buttonDownloadXlsx = 'a[data-cy="button-download-xlsx"]' labelCreatedBy = 'div[data-cy="label-Created By"]' labelProgramme = 'div[data-cy="label-Programme"]' labelTargetPopulation = 'div[data-cy="label-Target Population"]' @@ -40,6 +43,27 @@ class PaymentModuleDetails(BaseComponents): labelPending = 'div[data-cy="label-Pending"]' labelNumberOfPayments = 'div[data-cy="label-Number of payments"]' labelReconciled = 'div[data-cy="label-Reconciled"]' + labelTotalEntitledQuantity = 'div[data-cy="label-Total Entitled Quantity"]' + buttonLockPlan = 'button[data-cy="button-lock-plan"' + buttonSubmit = 'button[data-cy="button-submit"]' + inputEntitlementFormula = 'div[data-cy="input-entitlement-formula"]' + buttonApplySteficon = 'button[data-cy="button-apply-steficon"]' + selectDeliveryMechanism = 'div[data-cy="select-deliveryMechanisms[0].deliveryMechanism"]' + selectDeliveryMechanismsFSP = 'div[data-cy="select-deliveryMechanisms[0].fsp"]' + buttonNextSave = 'button[data-cy="button-next-save"]' + buttonSendForApproval = 'button[data-cy="button-send-for-approval"]' + buttonApprove = 'button[data-cy="button-approve"]' + buttonAuthorize = 'button[data-cy="button-authorize"]' + buttonMarkAsReleased = 'button[data-cy="button-mark-as-released"]' + buttonUploadReconciliationInfo = 'button[data-cy="button-import"]' + buttonImportSubmit = 'button[data-cy="button-import-submit"]' + errorsContainer = 'div[data-cy="errors-container"]' + + def getButtonLockPlan(self) -> WebElement: + return self.wait_for(self.buttonLockPlan) + + def getButtonSubmit(self) -> WebElement: + return self.wait_for(self.buttonSubmit) def getPageHeaderContainer(self) -> WebElement: return self.wait_for(self.pageHeaderContainer) @@ -56,6 +80,18 @@ def getStatusContainer(self) -> WebElement: def getButtonExportXlsx(self) -> WebElement: return self.wait_for(self.buttonExportXlsx) + def getButtonDownloadXlsx(self) -> WebElement: + return self.wait_for(self.buttonDownloadXlsx) + + def getButtonUploadReconciliationInfo(self) -> WebElement: + return self.wait_for(self.buttonUploadReconciliationInfo) + + def getErrorsContainer(self) -> WebElement: + return self.wait_for(self.errorsContainer) + + def getButtonImportSubmit(self) -> WebElement: + return self.wait_for(self.buttonImportSubmit) + def getLabelCreatedBy(self) -> WebElement: return self.wait_for(self.labelCreatedBy) @@ -151,3 +187,40 @@ def getLabelNumberOfPayments(self) -> WebElement: def getLabelReconciled(self) -> WebElement: return self.wait_for(self.labelReconciled) + + def getLabelTotalEntitledQuantity(self) -> WebElement: + return self.wait_for(self.labelTotalEntitledQuantity) + + def getInputEntitlementFormula(self) -> WebElement: + return self.wait_for(self.inputEntitlementFormula) + + def getButtonApplySteficon(self) -> WebElement: + return self.wait_for(self.buttonApplySteficon) + + def getSelectDeliveryMechanism(self) -> WebElement: + return self.wait_for(self.selectDeliveryMechanism) + + def getSelectDeliveryMechanismFSP(self) -> WebElement: + return self.wait_for(self.selectDeliveryMechanismsFSP) + + def getButtonNextSave(self) -> WebElement: + return self.wait_for(self.buttonNextSave) + + def getButtonSendForApproval(self) -> WebElement: + return self.wait_for(self.buttonSendForApproval) + + def getButtonApprove(self) -> WebElement: + return self.wait_for(self.buttonApprove) + + def getButtonAuthorize(self) -> WebElement: + return self.wait_for(self.buttonAuthorize) + + def getButtonMarkAsReleased(self) -> WebElement: + return self.wait_for(self.buttonMarkAsReleased) + + def checkStatus(self, status: str) -> None: + for _ in range(30): + if status == self.getStatusContainer().text: + break + sleep(1) + assert status in self.getStatusContainer().text diff --git a/backend/selenium_tests/payment_module/test_payment_module.py b/backend/selenium_tests/payment_module/test_payment_module.py index 83c857f016..a9a6cf6046 100644 --- a/backend/selenium_tests/payment_module/test_payment_module.py +++ b/backend/selenium_tests/payment_module/test_payment_module.py @@ -1,5 +1,9 @@ +import os +import zipfile from datetime import datetime +from time import sleep +import openpyxl import pytest from dateutil.relativedelta import relativedelta from page_object.payment_module.new_payment_plan import NewPaymentPlan @@ -9,18 +13,38 @@ from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory from hct_mis_api.apps.core.models import BusinessArea, DataCollectingType -from hct_mis_api.apps.payment.models import PaymentPlan +from hct_mis_api.apps.household.fixtures import create_household +from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices +from hct_mis_api.apps.payment.fixtures import ( + FinancialServiceProviderFactory, + FinancialServiceProviderXlsxTemplateFactory, + FspXlsxTemplatePerDeliveryMechanismFactory, +) +from hct_mis_api.apps.payment.models import FinancialServiceProvider, PaymentPlan from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program +from hct_mis_api.apps.steficon.fixtures import RuleCommitFactory, RuleFactory +from hct_mis_api.apps.steficon.models import Rule from hct_mis_api.apps.targeting.fixtures import ( TargetingCriteriaFactory, TargetPopulationFactory, ) from hct_mis_api.apps.targeting.models import TargetPopulation +from selenium_tests.helpers.date_time_format import FormatTime pytestmark = pytest.mark.django_db(transaction=True) +def find_file(file_name: str, search_in_dir: str = "./report/downloads/", number_of_ties: int = 1) -> str: + for _ in range(number_of_ties): + for file in os.listdir(search_in_dir): + if file_name in file: + return file + sleep(1) + else: + raise Exception(f"{file_name} file did not found in {search_in_dir}") + + @pytest.fixture def create_test_program() -> Program: BusinessArea.objects.filter(slug="afghanistan").update(is_payment_plan_applicable=True) @@ -36,13 +60,58 @@ def create_test_program() -> Program: @pytest.fixture -def create_payment_plan(create_test_program: Program) -> PaymentPlan: +def create_targeting(create_test_program: Program) -> None: targeting_criteria = TargetingCriteriaFactory() - TargetPopulationFactory( + + tp = TargetPopulationFactory( program=create_test_program, - status=TargetPopulation.STATUS_OPEN, + status=TargetPopulation.STATUS_READY_FOR_PAYMENT_MODULE, targeting_criteria=targeting_criteria, ) + households = [ + create_household( + household_args={"size": 2, "business_area": tp.business_area, "program": tp.program}, + )[0] + for _ in range(14) + ] + + tp.households.set(households) + business_area = BusinessArea.objects.get(slug="afghanistan") + rule = RuleFactory( + name="Test Rule", + type=Rule.TYPE_PAYMENT_PLAN, + deprecated=False, + enabled=True, + ) + rule.allowed_business_areas.add(business_area) + RuleCommitFactory(rule=rule, version=2) + + fsp_xlsx_template = FinancialServiceProviderXlsxTemplateFactory(name="TestName123") + + fsp_1 = FinancialServiceProviderFactory( + name="FSP_1", + vision_vendor_number="149-69-3686", + delivery_mechanisms=[DeliveryMechanismChoices.DELIVERY_TYPE_CASH], + distribution_limit=10_000, + communication_channel=FinancialServiceProvider.COMMUNICATION_CHANNEL_XLSX, + ) + fsp_1.allowed_business_areas.add(business_area) + FspXlsxTemplatePerDeliveryMechanismFactory( + financial_service_provider=fsp_1, + xlsx_template=fsp_xlsx_template, + delivery_mechanism=DeliveryMechanismChoices.DELIVERY_TYPE_CASH, + ) + + +@pytest.fixture +def clear_downloaded_files() -> None: + yield + for file in os.listdir("./report/downloads/"): + os.remove(os.path.join("./report/downloads", file)) + + +@pytest.fixture +def create_payment_plan(create_targeting: None) -> PaymentPlan: tp = TargetPopulation.objects.first() payment_plan = PaymentPlan.objects.update_or_create( business_area=BusinessArea.objects.only("is_payment_plan_applicable").get(slug="afghanistan"), @@ -103,11 +172,11 @@ def test_smoke_new_payment_plan( assert "New Payment Plan" in pageNewPaymentPlan.getPageHeaderTitle().text assert "SAVE" in pageNewPaymentPlan.getButtonSavePaymentPlan().text assert "Target Population" in pageNewPaymentPlan.getInputTargetPopulation().text - assert "Start Date*" in pageNewPaymentPlan.getInputStartDate().text - assert "End Date*" in pageNewPaymentPlan.getInputEndDate().text + assert "Start Date*" in pageNewPaymentPlan.wait_for(pageNewPaymentPlan.inputStartDate).text + assert "End Date*" in pageNewPaymentPlan.wait_for(pageNewPaymentPlan.inputEndDate).text assert "Currency" in pageNewPaymentPlan.getInputCurrency().text - assert "Dispersion Start Date*" in pageNewPaymentPlan.getInputDispersionStartDate().text - assert "Dispersion End Date*" in pageNewPaymentPlan.getInputDispersionEndDate().text + assert "Dispersion Start Date*" in pageNewPaymentPlan.wait_for(pageNewPaymentPlan.inputDispersionStartDate).text + assert "Dispersion End Date*" in pageNewPaymentPlan.wait_for(pageNewPaymentPlan.inputDispersionEndDate).text def test_smoke_details_payment_plan( self, @@ -157,3 +226,109 @@ def test_smoke_details_payment_plan( assert "Delivered Quantity" in pagePaymentModuleDetails.getTableLabel()[8].text assert "FSP Auth Code" in pagePaymentModuleDetails.getTableLabel()[9].text assert "Reconciliation" in pagePaymentModuleDetails.getTableLabel()[10].text + + def test_payment_plan_happy_path( + self, + clear_downloaded_files: None, + create_targeting: None, + pagePaymentModule: PaymentModule, + pagePaymentModuleDetails: PaymentModuleDetails, + pageNewPaymentPlan: NewPaymentPlan, + ) -> None: + targeting = TargetPopulation.objects.first() + program = Program.objects.get(name="Test Program") + pagePaymentModule.selectGlobalProgramFilter("Test Program").click() + pagePaymentModule.getNavPaymentModule().click() + pagePaymentModule.getButtonNewPaymentPlan().click() + pageNewPaymentPlan.getInputTargetPopulation().click() + pageNewPaymentPlan.select_listbox_element(targeting.name).click() + pageNewPaymentPlan.getInputStartDate().click() + pageNewPaymentPlan.getInputStartDate().send_keys( + FormatTime(time=program.start_date + relativedelta(day=12)).numerically_formatted_date + ) + pageNewPaymentPlan.getInputEndDate().click() + pageNewPaymentPlan.getInputEndDate().send_keys(FormatTime(time=program.end_date).numerically_formatted_date) + pageNewPaymentPlan.getInputCurrency().click() + pageNewPaymentPlan.select_listbox_element("Czech koruna").click() + pageNewPaymentPlan.getInputDispersionStartDate().click() + pageNewPaymentPlan.getInputDispersionStartDate().send_keys(FormatTime(22, 1, 2024).numerically_formatted_date) + pageNewPaymentPlan.getInputDispersionEndDate().click() + pageNewPaymentPlan.getInputDispersionEndDate().send_keys(FormatTime(30, 6, 2030).numerically_formatted_date) + pageNewPaymentPlan.getInputCurrency().click() + pageNewPaymentPlan.getButtonSavePaymentPlan().click() + assert "OPEN" in pagePaymentModuleDetails.getStatusContainer().text + assert "Test Program" in pagePaymentModuleDetails.getLabelProgramme().text + assert "CZK" in pagePaymentModuleDetails.getLabelCurrency().text + assert ( + FormatTime(time=program.start_date + relativedelta(day=12)).date_in_text_format + in pagePaymentModuleDetails.getLabelStartDate().text + ) + assert FormatTime(time=program.end_date).date_in_text_format in pagePaymentModuleDetails.getLabelEndDate().text + assert ( + FormatTime(22, 1, 2024).date_in_text_format in pagePaymentModuleDetails.getLabelDispersionStartDate().text + ) + assert FormatTime(30, 6, 2030).date_in_text_format in pagePaymentModuleDetails.getLabelDispersionEndDate().text + pagePaymentModuleDetails.getButtonLockPlan().click() + pagePaymentModuleDetails.getButtonSubmit().click() + pagePaymentModuleDetails.getInputEntitlementFormula().click() + pagePaymentModuleDetails.select_listbox_element("Test Rule").click() + pagePaymentModuleDetails.getButtonApplySteficon().click() + + for _ in range(10): + try: + pagePaymentModuleDetails.getButtonSetUpFsp().click() + break + except BaseException: + sleep(1) + else: + pagePaymentModuleDetails.getButtonSetUpFsp().click() + + pagePaymentModuleDetails.getSelectDeliveryMechanism().click() + pagePaymentModuleDetails.select_listbox_element("Cash").click() + pagePaymentModuleDetails.getButtonNextSave().click() + pagePaymentModuleDetails.getSelectDeliveryMechanismFSP().click() + pagePaymentModuleDetails.select_listbox_element("FSP_1").click() + pagePaymentModuleDetails.getButtonNextSave().click() + pagePaymentModuleDetails.checkStatus("LOCKED") + pagePaymentModuleDetails.getButtonLockPlan().click() + pagePaymentModuleDetails.getButtonSubmit().click() + pagePaymentModuleDetails.checkStatus("LOCKED FSP") + pagePaymentModuleDetails.getButtonSendForApproval().click() + pagePaymentModuleDetails.checkStatus("IN APPROVAL") + pagePaymentModuleDetails.getButtonApprove().click() + pagePaymentModuleDetails.getButtonSubmit().click() + pagePaymentModuleDetails.checkStatus("IN AUTHORIZATION") + pagePaymentModuleDetails.getButtonAuthorize().click() + pagePaymentModuleDetails.getButtonSubmit().click() + pagePaymentModuleDetails.checkStatus("IN REVIEW") + pagePaymentModuleDetails.getButtonMarkAsReleased().click() + pagePaymentModuleDetails.getButtonSubmit().click() + pagePaymentModuleDetails.checkStatus("ACCEPTED") + pagePaymentModuleDetails.getButtonExportXlsx().click() + pagePaymentModuleDetails.checkAlert("Exporting XLSX started") + + # ToDo: Refresh is workaround. Works on dev properly. + pagePaymentModule.driver.refresh() + pagePaymentModuleDetails.getButtonDownloadXlsx().click() + + zip_file = find_file(".zip", number_of_ties=15) + with zipfile.ZipFile(f"./report/downloads/{zip_file}", "r") as zip_ref: + zip_ref.extractall("./report/downloads/") + + xlsx_file = find_file(".xlsx") + wb1 = openpyxl.load_workbook(f"./report/downloads/{xlsx_file}") + ws1 = wb1.active + for cell in ws1["N:N"]: + if cell.row >= 2: + ws1.cell(row=cell.row, column=16, value=cell.value) + + wb1.save(f"./report/downloads/{xlsx_file}") + + pagePaymentModuleDetails.getButtonUploadReconciliationInfo().click() + pagePaymentModuleDetails.upload_file( + os.path.abspath(os.path.join("./report/downloads", xlsx_file)), timeout=120 + ) + pagePaymentModuleDetails.getButtonImportSubmit().click() + pagePaymentModuleDetails.checkStatus("FINISHED") + assert "14 (100%)" in pagePaymentModuleDetails.getLabelReconciled().text + assert "18.2 CZK (0.7 USD)" in pagePaymentModuleDetails.getLabelTotalEntitledQuantity().text From 36a0e53b704a993b51d07917df4f6e2686b8fd71 Mon Sep 17 00:00:00 2001 From: szymon-kellton <130459593+szymon-kellton@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:31:22 +0200 Subject: [PATCH 5/5] Selenium: Grievance Dashboard - happy path (#4003) * Added test_grievance_dashboard_happy_path * Added test_grievance_dashboard_happy_path * Test * Added test_grievance_dashboard_happy_path --- .../test_grievance_dashboard.py | 98 ++++++++++++++++++- .../grievance/details_grievance_page.py | 7 +- .../grievance/grievance_tickets.py | 8 ++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/backend/selenium_tests/grievance/grievance_dashboard/test_grievance_dashboard.py b/backend/selenium_tests/grievance/grievance_dashboard/test_grievance_dashboard.py index 8d8ced4052..76d3b0894a 100644 --- a/backend/selenium_tests/grievance/grievance_dashboard/test_grievance_dashboard.py +++ b/backend/selenium_tests/grievance/grievance_dashboard/test_grievance_dashboard.py @@ -1,5 +1,11 @@ +from datetime import timedelta + +from django.utils import timezone + import pytest +from page_object.grievance.details_grievance_page import GrievanceDetailsPage from page_object.grievance.grievance_dashboard import GrievanceDashboard +from page_object.grievance.grievance_tickets import GrievanceTickets from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.models import BusinessArea @@ -42,10 +48,40 @@ def add_grievances() -> None: GrievanceTicket._meta.get_field("created_at").auto_now_add = True +@pytest.fixture +def grievances() -> [GrievanceTicket]: + GrievanceTicket._meta.get_field("created_at").auto_now_add = False + GrievanceTicket._meta.get_field("updated_at").auto_now = False + grievances = list() + grievances.append( + generate_grievance( + created_at=str(timezone.now() - timedelta(days=20)), + updated_at=str(timezone.now()), + status=GrievanceTicket.STATUS_NEW, + ) + ) + grievances.append( + generate_grievance( + created_at=str(timezone.now() - timedelta(days=40)), + status=GrievanceTicket.STATUS_NEW, + ) + ) + grievances.append( + generate_grievance( + created_at=str(timezone.now() - timedelta(days=60)), + status=GrievanceTicket.STATUS_NEW, + category=GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION, + ) + ) + GrievanceTicket._meta.get_field("created_at").auto_now_add = True + GrievanceTicket._meta.get_field("updated_at").auto_now = True + return grievances + + def generate_grievance( - unicef_id: str, + unicef_id: str = "GRV-0000001", status: int = GrievanceTicket.STATUS_NEW, - category: int = GrievanceTicket.CATEGORY_POSITIVE_FEEDBACK, + category: int = GrievanceTicket.CATEGORY_REFERRAL, created_by: User | None = None, assigned_to: User | None = None, business_area: BusinessArea | None = None, @@ -54,11 +90,11 @@ def generate_grievance( household_unicef_id: str = "HH-20-0000.0001", updated_at: str = "2023-09-27T11:26:33.846Z", created_at: str = "2022-04-30T09:54:07.827000", -) -> None: +) -> GrievanceTicket: created_by = User.objects.first() if created_by is None else created_by assigned_to = User.objects.first() if assigned_to is None else assigned_to business_area = BusinessArea.objects.filter(slug="afghanistan").first() if business_area is None else business_area - GrievanceTicket.objects.create( + grievance_ticket = GrievanceTicket.objects.create( **{ "business_area": business_area, "unicef_id": unicef_id, @@ -77,6 +113,14 @@ def generate_grievance( } ) + from hct_mis_api.apps.grievance.models import TicketReferralDetails + + TicketReferralDetails.objects.create( + ticket=grievance_ticket, + ) + + return grievance_ticket + @pytest.mark.usefixtures("login") class TestSmokeGrievanceDashboard: @@ -105,3 +149,49 @@ def test_smoke_grievance_dashboard( "365 days" in pageGrievanceDashboard.getLabelizedFieldContainerTicketsAverageResolutionUserGenerated().text ) GrievanceTicket._meta.get_field("updated_at").auto_now = True + + def test_grievance_dashboard_happy_path( + self, + active_program: Program, + grievances: [GrievanceTicket], + pageGrievanceDashboard: GrievanceDashboard, + pageGrievanceTickets: GrievanceTickets, + pageGrievanceDetailsPage: GrievanceDetailsPage, + ) -> None: + pageGrievanceTickets.getNavGrievance().click() + pageGrievanceDashboard.getNavGrievanceDashboard().click() + assert "Grievance Dashboard" in pageGrievanceDashboard.getPageHeaderTitle().text + assert "3" in pageGrievanceDashboard.getTotalNumberOfTicketsTopNumber().text + assert "1" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfTicketsSystemGenerated().text + assert "2" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfTicketsUserGenerated().text + assert "0" in pageGrievanceDashboard.getTotalNumberOfClosedTicketsTopNumber().text + assert "0" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfClosedTicketsSystemGenerated().text + assert "0" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfClosedTicketsUserGenerated().text + assert "0 days" in pageGrievanceDashboard.getTicketsAverageResolutionTopNumber().text + assert ( + "0 days" in pageGrievanceDashboard.getLabelizedFieldContainerTicketsAverageResolutionSystemGenerated().text + ) + assert "0 days" in pageGrievanceDashboard.getLabelizedFieldContainerTicketsAverageResolutionUserGenerated().text + + pageGrievanceTickets.getNavGrievance().click() + pageGrievanceTickets.getTicketListRow()[0].click() + pageGrievanceDetailsPage.getButtonAssignToMe().click() + pageGrievanceDetailsPage.getButtonSetInProgress().click() + pageGrievanceDetailsPage.getButtonCloseTicket().click() + pageGrievanceTickets.getButtonConfirm().click() + pageGrievanceTickets.getNavGrievance().click() + pageGrievanceTickets.getNavGrievance().click() + pageGrievanceDashboard.getNavGrievanceDashboard().click() + assert "3" in pageGrievanceDashboard.getTotalNumberOfTicketsTopNumber().text + assert "1" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfTicketsSystemGenerated().text + assert "2" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfTicketsUserGenerated().text + assert "0" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfClosedTicketsSystemGenerated().text + assert "1" in pageGrievanceDashboard.getLabelizedFieldContainerTotalNumberOfClosedTicketsUserGenerated().text + assert "20.00 days" in pageGrievanceDashboard.getTicketsAverageResolutionTopNumber().text + assert ( + "0 days" in pageGrievanceDashboard.getLabelizedFieldContainerTicketsAverageResolutionSystemGenerated().text + ) + assert ( + "20 days" in pageGrievanceDashboard.getLabelizedFieldContainerTicketsAverageResolutionUserGenerated().text + ) + assert "1" in pageGrievanceDashboard.getTotalNumberOfClosedTicketsTopNumber().text diff --git a/backend/selenium_tests/page_object/grievance/details_grievance_page.py b/backend/selenium_tests/page_object/grievance/details_grievance_page.py index 2073b349f2..1814e30859 100644 --- a/backend/selenium_tests/page_object/grievance/details_grievance_page.py +++ b/backend/selenium_tests/page_object/grievance/details_grievance_page.py @@ -100,11 +100,14 @@ def getPageHeaderContainer(self) -> WebElement: def getTitle(self) -> WebElement: return self.wait_for(self.title) + def getButtonCloseTicket(self) -> WebElement: + return self.wait_for(self.buttonCloseTicket) + def getButtonAssignToMe(self) -> WebElement: return self.wait_for(self.buttonAssignToMe) - def getButtonCloseTicket(self) -> WebElement: - return self.wait_for(self.buttonCloseTicket) + def getButtonSetInProgress(self) -> WebElement: + return self.wait_for(self.buttonSetInProgress) def getButtonSendBack(self) -> WebElement: return self.wait_for(self.buttonSendBack) diff --git a/backend/selenium_tests/page_object/grievance/grievance_tickets.py b/backend/selenium_tests/page_object/grievance/grievance_tickets.py index b187aef56e..674796a0fc 100644 --- a/backend/selenium_tests/page_object/grievance/grievance_tickets.py +++ b/backend/selenium_tests/page_object/grievance/grievance_tickets.py @@ -12,6 +12,8 @@ class GrievanceTickets(BaseComponents): familyName = 'li[data-value="full_name"]' tabSystemGenerated = 'button[data-cy="tab-SYSTEM-GENERATED"]' tabUserGenerated = 'button[data-cy="tab-USER-GENERATED"]' + buttonCloseTicket = 'button[data-cy="button-close-ticket"]' + buttonConfirm = 'button[data-cy="button-confirm"]' creationDateFromFilter = 'div[data-cy="filters-creation-date-from"]' creationDateToFilter = 'div[data-cy="filters-creation-date-to"]' statusFilter = 'div[data-cy="filters-status"]' @@ -210,3 +212,9 @@ def getButtonSetUrgency(self) -> WebElement: def getButtonAddNote(self) -> WebElement: return self.wait_for(self.buttonAddNote) + + def getButtonCloseTicket(self) -> WebElement: + return self.wait_for(self.buttonCloseTicket) + + def getButtonConfirm(self) -> WebElement: + return self.wait_for(self.buttonConfirm)