From 3c4524319c4d0fc716d1ccb9bacd0ed707ec4e52 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 9 Jan 2025 23:13:29 +0000 Subject: [PATCH 01/18] refactor: move healthcheck test into a healthcheck folder --- tests/playwright/{ => healthcheck_tests}/test_healthcheck.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/playwright/{ => healthcheck_tests}/test_healthcheck.py (100%) diff --git a/tests/playwright/test_healthcheck.py b/tests/playwright/healthcheck_tests/test_healthcheck.py similarity index 100% rename from tests/playwright/test_healthcheck.py rename to tests/playwright/healthcheck_tests/test_healthcheck.py From c3d24d8c923b76717a3e85dc9ed761a6e1fc0a09 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 9 Jan 2025 23:26:13 +0000 Subject: [PATCH 02/18] refactor: specify base URL as CLI argument --- tests/playwright/healthcheck_tests/test_healthcheck.py | 2 +- tests/playwright/run.sh | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/playwright/healthcheck_tests/test_healthcheck.py b/tests/playwright/healthcheck_tests/test_healthcheck.py index 51b6ce9672..87c65fc58f 100644 --- a/tests/playwright/healthcheck_tests/test_healthcheck.py +++ b/tests/playwright/healthcheck_tests/test_healthcheck.py @@ -2,6 +2,6 @@ def test_dev_healthcheck(page: Page): - page.goto("https://dev-benefits.calitp.org/healthcheck") + page.goto("/healthcheck") expect(page.get_by_text("Healthy")).to_be_visible() diff --git a/tests/playwright/run.sh b/tests/playwright/run.sh index d390fc1e27..4bb9559e7c 100755 --- a/tests/playwright/run.sh +++ b/tests/playwright/run.sh @@ -2,4 +2,6 @@ set -e -pytest +BASE_URL="https://dev-benefits.calitp.org" + +pytest --base-url $BASE_URL From ae402c83780b43cecda013695c7a29c804b28ae8 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Thu, 9 Jan 2025 23:27:06 +0000 Subject: [PATCH 03/18] feat: add agency flow smoke test --- .../smoke_tests/test_agency_flow.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/playwright/smoke_tests/test_agency_flow.py diff --git a/tests/playwright/smoke_tests/test_agency_flow.py b/tests/playwright/smoke_tests/test_agency_flow.py new file mode 100644 index 0000000000..94ce86cd55 --- /dev/null +++ b/tests/playwright/smoke_tests/test_agency_flow.py @@ -0,0 +1,67 @@ +from playwright.sync_api import Page, expect + + +def test_agency_card_flow(page: Page): + page.goto("/") + + # Index - select transit agency + page.get_by_role("link", name="Choose your Provider").click() + page.get_by_role("link", name="California State Transit").click() + + # Eligibility Index - select enrollment flow + page.get_by_label("Agency Cardholder").check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + # Eligibility Start - continue + page.get_by_role("button", name="Continue").click() + + # Eligibility Confirm - fill out form and submit + page.get_by_placeholder("12345").click() + + sub = "71162" + page.get_by_placeholder("12345").fill(sub) + page.keyboard.press("Tab") + + name = "Box" + page.get_by_placeholder("Hernandez-Demarcos").fill(name) + + page.get_by_role("button", name="Find my record").click() + + # Enrollment Index - fill out transit processor form in pop-up window + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + cardholder_name = "Test User" + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + card_number = "4111 1111 1111 1111" + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + expiration = "12/34" + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + security_code = "123" + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + + page.wait_for_timeout(10000) + + success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") + expect(success_message).to_be_visible() From 419cc0a9e702c58b558e1611e07a3c0a4d7a4182 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Fri, 10 Jan 2025 23:40:26 +0000 Subject: [PATCH 04/18] feat: add working older adult flow test with one-time password as MFA requires environment variables to be set for username, password, and authenticator secret --- pyproject.toml | 1 + tests/playwright/requirements.txt | 1 + .../smoke_tests/test_login_gov_flow.py | 78 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/playwright/smoke_tests/test_login_gov_flow.py diff --git a/pyproject.toml b/pyproject.toml index 0fafd65f43..3e8292a1aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ test = [ "pytest-django", "pytest-mock", "pytest-playwright", # only for writing tests. run tests using playwright Docker Compose service + "pyotp", "pytest-socket", ] diff --git a/tests/playwright/requirements.txt b/tests/playwright/requirements.txt index 93497efddf..fc5f0f216b 100644 --- a/tests/playwright/requirements.txt +++ b/tests/playwright/requirements.txt @@ -1,4 +1,5 @@ playwright==1.51.0 # needs to match version on Docker image +pyotp pytest pytest-playwright pytest-reporter-html1 diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/smoke_tests/test_login_gov_flow.py new file mode 100644 index 0000000000..ae5db794d0 --- /dev/null +++ b/tests/playwright/smoke_tests/test_login_gov_flow.py @@ -0,0 +1,78 @@ +import os + +from playwright.sync_api import Page, expect +import pyotp + + +def test_older_adult_flow(page: Page): + page.goto("/") + + # Index - select transit agency + page.get_by_role("link", name="Choose your Provider").click() + page.get_by_role("link", name="California State Transit").click() + + # Eligibility Index - select enrollment flow + page.get_by_label("Older Adult").check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + # Eligibility Start - continue to Login.gov + page.get_by_role("link", name="Get started with Login.gov").click() + + # sign in to Login.gov + page.get_by_label("Email address").click() + username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_USERNAME") + page.get_by_label("Email address").fill(username) + page.get_by_label("Email address").press("Tab") + + password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_PASSWORD") + page.get_by_label("Password", exact=True).fill(password) + + page.get_by_role("button", name="Sign in").click() + + # one-time password + # create instance of "authenticator app" + authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_AUTHENTICATOR_SECRET") + totp = pyotp.TOTP(authenticator_secret) + page.get_by_label("One-time code").click() + page.get_by_label("One-time code").fill(totp.now()) + + page.get_by_role("button", name="Submit").click() + + # Enrollment Index - fill out transit processor form in pop-up window + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + cardholder_name = "Test User" + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + card_number = "4111 1111 1111 1111" + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + expiration = "12/34" + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + security_code = "123" + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + + page.wait_for_timeout(10000) + + success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") + expect(success_message).to_be_visible() From 2a843daf8bebb61e483325c1e14c2eea5b9df749 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Fri, 10 Jan 2025 23:53:34 +0000 Subject: [PATCH 05/18] feat: add veteran and calfresh Login.gov flows --- .../smoke_tests/test_login_gov_flow.py | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/smoke_tests/test_login_gov_flow.py index ae5db794d0..2168e996c0 100644 --- a/tests/playwright/smoke_tests/test_login_gov_flow.py +++ b/tests/playwright/smoke_tests/test_login_gov_flow.py @@ -76,3 +76,151 @@ def test_older_adult_flow(page: Page): success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") expect(success_message).to_be_visible() + + +def test_us_veteran_flow(page: Page): + page.goto("/") + + # Index - select transit agency + page.get_by_role("link", name="Choose your Provider").click() + page.get_by_role("link", name="California State Transit").click() + + # Eligibility Index - select enrollment flow + page.get_by_label("U.S. Veteran").check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + # Eligibility Start - continue to Login.gov + page.get_by_role("link", name="Get started with Login.gov").click() + + # sign in to Login.gov + page.get_by_label("Email address").click() + username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_USERNAME") + page.get_by_label("Email address").fill(username) + page.get_by_label("Email address").press("Tab") + + password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_PASSWORD") + page.get_by_label("Password", exact=True).fill(password) + + page.get_by_role("button", name="Sign in").click() + + # one-time password + # create instance of "authenticator app" + authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_AUTHENTICATOR_SECRET") + totp = pyotp.TOTP(authenticator_secret) + page.get_by_label("One-time code").click() + page.get_by_label("One-time code").fill(totp.now()) + + page.get_by_role("button", name="Submit").click() + + # Enrollment Index - fill out transit processor form in pop-up window + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + cardholder_name = "Test User" + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + card_number = "4111 1111 1111 1111" + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + expiration = "12/34" + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + security_code = "123" + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + + page.wait_for_timeout(10000) + + success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") + expect(success_message).to_be_visible() + + +def test_calfresh_cardholder_flow(page: Page): + page.goto("/") + + # Index - select transit agency + page.get_by_role("link", name="Choose your Provider").click() + page.get_by_role("link", name="California State Transit").click() + + # Eligibility Index - select enrollment flow + page.get_by_label("CalFresh Cardholder").check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + # Eligibility Start - continue to Login.gov + page.get_by_role("link", name="Get started with Login.gov").click() + + # sign in to Login.gov + page.get_by_label("Email address").click() + username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_USERNAME") + page.get_by_label("Email address").fill(username) + page.get_by_label("Email address").press("Tab") + + password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_PASSWORD") + page.get_by_label("Password", exact=True).fill(password) + + page.get_by_role("button", name="Sign in").click() + + # one-time password + # create instance of "authenticator app" + authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_AUTHENTICATOR_SECRET") + totp = pyotp.TOTP(authenticator_secret) + page.get_by_label("One-time code").click() + page.get_by_label("One-time code").fill(totp.now()) + + page.get_by_role("button", name="Submit").click() + + # Enrollment Index - fill out transit processor form in pop-up window + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + cardholder_name = "Test User" + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + card_number = "4111 1111 1111 1111" + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + expiration = "12/34" + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + security_code = "123" + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + + page.wait_for_timeout(10000) + + reenrollment_error_message = page.get_by_text("You are still enrolled in this benefit") + expect(reenrollment_error_message).to_be_visible() From be322a9cd371adc9c4608a65f36fba8427b0e282 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Fri, 10 Jan 2025 23:54:16 +0000 Subject: [PATCH 06/18] refactor(agency_card): use Page object models for readability and reuse https://playwright.dev/python/docs/pom --- tests/playwright/smoke_tests/models.py | 91 +++++++++++++++++++ .../smoke_tests/test_agency_flow.py | 65 ++----------- 2 files changed, 100 insertions(+), 56 deletions(-) create mode 100644 tests/playwright/smoke_tests/models.py diff --git a/tests/playwright/smoke_tests/models.py b/tests/playwright/smoke_tests/models.py new file mode 100644 index 0000000000..1ced238559 --- /dev/null +++ b/tests/playwright/smoke_tests/models.py @@ -0,0 +1,91 @@ +from playwright.sync_api import Page + + +class Index: + def __init__(self, page: Page): + self.page = page + + def select_agency(self, agency_name): + page = self.page + page.get_by_role("link", name="Choose your Provider").click() + page.get_by_role("link", name=agency_name).click() + + return EligibilityIndex(page) + + +class EligibilityIndex: + def __init__(self, page: Page): + self.page = page + + def select_flow(self, flow_name): + page = self.page + page.get_by_label(flow_name).check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + return EligibilityStart(page) + + +class EligibilityStart: + def __init__(self, page: Page): + self.page = page + + def click_continue(self): + page = self.page + page.get_by_role("button", name="Continue").click() + + return EligibilityConfirm(page) + + +class EligibilityConfirm: + def __init__(self, page: Page): + self.page = page + + def submit_form(self, sub, name): + page = self.page + page.get_by_placeholder("12345").click() + + page.get_by_placeholder("12345").fill(sub) + page.keyboard.press("Tab") + + page.get_by_placeholder("Hernandez-Demarcos").fill(name) + + page.get_by_role("button", name="Find my record").click() + + return EnrollmentIndex(page) + + +class EnrollmentIndex: + def __init__(self, page: Page): + self.page = page + + def enroll(self, cardholder_name, card_number, expiration, security_code): + page = self.page + + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() diff --git a/tests/playwright/smoke_tests/test_agency_flow.py b/tests/playwright/smoke_tests/test_agency_flow.py index 94ce86cd55..8f5188e40d 100644 --- a/tests/playwright/smoke_tests/test_agency_flow.py +++ b/tests/playwright/smoke_tests/test_agency_flow.py @@ -1,66 +1,19 @@ from playwright.sync_api import Page, expect +from models import Index + def test_agency_card_flow(page: Page): page.goto("/") - # Index - select transit agency - page.get_by_role("link", name="Choose your Provider").click() - page.get_by_role("link", name="California State Transit").click() - - # Eligibility Index - select enrollment flow - page.get_by_label("Agency Cardholder").check() - page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading - page.get_by_role("button", name="Choose this benefit").click() - - # Eligibility Start - continue - page.get_by_role("button", name="Continue").click() - - # Eligibility Confirm - fill out form and submit - page.get_by_placeholder("12345").click() - - sub = "71162" - page.get_by_placeholder("12345").fill(sub) - page.keyboard.press("Tab") - - name = "Box" - page.get_by_placeholder("Hernandez-Demarcos").fill(name) - - page.get_by_role("button", name="Find my record").click() - - # Enrollment Index - fill out transit processor form in pop-up window - with page.expect_popup() as popup_info: - page.get_by_role("button", name="Enroll").click() - - popup = popup_info.value - popup.wait_for_timeout(3000) - - popup.get_by_text("Cardholder name").click() - - cardholder_name = "Test User" - popup.get_by_label("Cardholder name").fill(cardholder_name) - popup.keyboard.press("Tab") - - card_number = "4111 1111 1111 1111" - popup.get_by_label("Card number").fill(card_number) - popup.keyboard.press("Tab") - - expiration = "12/34" - popup.get_by_label("mm/yy").fill(expiration) - popup.keyboard.press("Tab") - - security_code = "123" - popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) - - # trigger form validation - not sure why their form behaves this way - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Tab") - - popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + index = Index(page) + eligibility_index = index.select_agency("California State Transit") + eligibility_start = eligibility_index.select_flow("Agency Cardholder") + eligibility_confirm = eligibility_start.click_continue() + enrollment_index = eligibility_confirm.submit_form("71162", "Box") + enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + # enrollment can take a bit page.wait_for_timeout(10000) success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") From 13a4886b01cc6171e474a790080067e47b0a1e96 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 00:05:41 +0000 Subject: [PATCH 07/18] refactor(login_gov): use Page object model pattern for older adult flow --- tests/playwright/smoke_tests/models.py | 30 +++++++++ .../smoke_tests/test_login_gov_flow.py | 67 +++---------------- 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/tests/playwright/smoke_tests/models.py b/tests/playwright/smoke_tests/models.py index 1ced238559..c126e576ab 100644 --- a/tests/playwright/smoke_tests/models.py +++ b/tests/playwright/smoke_tests/models.py @@ -36,6 +36,12 @@ def click_continue(self): return EligibilityConfirm(page) + def get_started_with_login_gov(self): + page = self.page + page.get_by_role("link", name="Get started with Login.gov").click() + + return LoginGov(page) + class EligibilityConfirm: def __init__(self, page: Page): @@ -55,6 +61,30 @@ def submit_form(self, sub, name): return EnrollmentIndex(page) +class LoginGov: + def __init__(self, page: Page): + self.page = page + + def sign_in(self, username, password): + page = self.page + page.get_by_label("Email address").click() + page.get_by_label("Email address").fill(username) + page.get_by_label("Email address").press("Tab") + + page.get_by_label("Password", exact=True).fill(password) + + page.get_by_role("button", name="Sign in").click() + + def enter_otp(self, one_time_password): + page = self.page + page.get_by_label("One-time code").click() + page.get_by_label("One-time code").fill(one_time_password) + + page.get_by_role("button", name="Submit").click() + + return EnrollmentIndex(page) + + class EnrollmentIndex: def __init__(self, page: Page): self.page = page diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/smoke_tests/test_login_gov_flow.py index 2168e996c0..978d8c6067 100644 --- a/tests/playwright/smoke_tests/test_login_gov_flow.py +++ b/tests/playwright/smoke_tests/test_login_gov_flow.py @@ -3,75 +3,30 @@ from playwright.sync_api import Page, expect import pyotp +from models import Index + def test_older_adult_flow(page: Page): page.goto("/") - # Index - select transit agency - page.get_by_role("link", name="Choose your Provider").click() - page.get_by_role("link", name="California State Transit").click() + index = Index(page) + eligibility_index = index.select_agency("California State Transit") + eligibility_start = eligibility_index.select_flow("Older Adult") - # Eligibility Index - select enrollment flow - page.get_by_label("Older Adult").check() - page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading - page.get_by_role("button", name="Choose this benefit").click() + login_gov = eligibility_start.get_started_with_login_gov() - # Eligibility Start - continue to Login.gov - page.get_by_role("link", name="Get started with Login.gov").click() - - # sign in to Login.gov - page.get_by_label("Email address").click() username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_USERNAME") - page.get_by_label("Email address").fill(username) - page.get_by_label("Email address").press("Tab") - password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_PASSWORD") - page.get_by_label("Password", exact=True).fill(password) - - page.get_by_role("button", name="Sign in").click() + login_gov.sign_in(username, password) - # one-time password - # create instance of "authenticator app" authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_OLDER_ADULT_AUTHENTICATOR_SECRET") + # create instance of "authenticator app" totp = pyotp.TOTP(authenticator_secret) - page.get_by_label("One-time code").click() - page.get_by_label("One-time code").fill(totp.now()) - - page.get_by_role("button", name="Submit").click() - - # Enrollment Index - fill out transit processor form in pop-up window - with page.expect_popup() as popup_info: - page.get_by_role("button", name="Enroll").click() + enrollment_index = login_gov.enter_otp(totp.now()) - popup = popup_info.value - popup.wait_for_timeout(3000) - - popup.get_by_text("Cardholder name").click() - - cardholder_name = "Test User" - popup.get_by_label("Cardholder name").fill(cardholder_name) - popup.keyboard.press("Tab") - - card_number = "4111 1111 1111 1111" - popup.get_by_label("Card number").fill(card_number) - popup.keyboard.press("Tab") - - expiration = "12/34" - popup.get_by_label("mm/yy").fill(expiration) - popup.keyboard.press("Tab") - - security_code = "123" - popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) - - # trigger form validation - not sure why their form behaves this way - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Tab") - - popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + # enrollment can take a bit page.wait_for_timeout(10000) success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") From 6b078d1c98c3146144e1268162ea34e533afb7c0 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 00:08:13 +0000 Subject: [PATCH 08/18] refactor(login_gov): use same pattern for veteran and calfresh flow --- .../smoke_tests/test_login_gov_flow.py | 130 +++--------------- 1 file changed, 18 insertions(+), 112 deletions(-) diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/smoke_tests/test_login_gov_flow.py index 978d8c6067..7951c4492b 100644 --- a/tests/playwright/smoke_tests/test_login_gov_flow.py +++ b/tests/playwright/smoke_tests/test_login_gov_flow.py @@ -36,71 +36,24 @@ def test_older_adult_flow(page: Page): def test_us_veteran_flow(page: Page): page.goto("/") - # Index - select transit agency - page.get_by_role("link", name="Choose your Provider").click() - page.get_by_role("link", name="California State Transit").click() - - # Eligibility Index - select enrollment flow - page.get_by_label("U.S. Veteran").check() - page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading - page.get_by_role("button", name="Choose this benefit").click() + index = Index(page) + eligibility_index = index.select_agency("California State Transit") + eligibility_start = eligibility_index.select_flow("U.S. Veteran") - # Eligibility Start - continue to Login.gov - page.get_by_role("link", name="Get started with Login.gov").click() + login_gov = eligibility_start.get_started_with_login_gov() - # sign in to Login.gov - page.get_by_label("Email address").click() username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_USERNAME") - page.get_by_label("Email address").fill(username) - page.get_by_label("Email address").press("Tab") - password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_PASSWORD") - page.get_by_label("Password", exact=True).fill(password) - - page.get_by_role("button", name="Sign in").click() + login_gov.sign_in(username, password) - # one-time password - # create instance of "authenticator app" authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_VETERAN_AUTHENTICATOR_SECRET") + # create instance of "authenticator app" totp = pyotp.TOTP(authenticator_secret) - page.get_by_label("One-time code").click() - page.get_by_label("One-time code").fill(totp.now()) - - page.get_by_role("button", name="Submit").click() - - # Enrollment Index - fill out transit processor form in pop-up window - with page.expect_popup() as popup_info: - page.get_by_role("button", name="Enroll").click() - - popup = popup_info.value - popup.wait_for_timeout(3000) - - popup.get_by_text("Cardholder name").click() - - cardholder_name = "Test User" - popup.get_by_label("Cardholder name").fill(cardholder_name) - popup.keyboard.press("Tab") - - card_number = "4111 1111 1111 1111" - popup.get_by_label("Card number").fill(card_number) - popup.keyboard.press("Tab") - - expiration = "12/34" - popup.get_by_label("mm/yy").fill(expiration) - popup.keyboard.press("Tab") - - security_code = "123" - popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) - - # trigger form validation - not sure why their form behaves this way - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Tab") + enrollment_index = login_gov.enter_otp(totp.now()) - popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + # enrollment can take a bit page.wait_for_timeout(10000) success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") @@ -110,71 +63,24 @@ def test_us_veteran_flow(page: Page): def test_calfresh_cardholder_flow(page: Page): page.goto("/") - # Index - select transit agency - page.get_by_role("link", name="Choose your Provider").click() - page.get_by_role("link", name="California State Transit").click() - - # Eligibility Index - select enrollment flow - page.get_by_label("CalFresh Cardholder").check() - page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading - page.get_by_role("button", name="Choose this benefit").click() + index = Index(page) + eligibility_index = index.select_agency("California State Transit") + eligibility_start = eligibility_index.select_flow("CalFresh Cardholder") - # Eligibility Start - continue to Login.gov - page.get_by_role("link", name="Get started with Login.gov").click() + login_gov = eligibility_start.get_started_with_login_gov() - # sign in to Login.gov - page.get_by_label("Email address").click() username = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_USERNAME") - page.get_by_label("Email address").fill(username) - page.get_by_label("Email address").press("Tab") - password = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_PASSWORD") - page.get_by_label("Password", exact=True).fill(password) - - page.get_by_role("button", name="Sign in").click() + login_gov.sign_in(username, password) - # one-time password - # create instance of "authenticator app" authenticator_secret = os.environ.get("PLAYWRIGHT_LOGIN_GOV_CALFRESH_AUTHENTICATOR_SECRET") + # create instance of "authenticator app" totp = pyotp.TOTP(authenticator_secret) - page.get_by_label("One-time code").click() - page.get_by_label("One-time code").fill(totp.now()) - - page.get_by_role("button", name="Submit").click() - - # Enrollment Index - fill out transit processor form in pop-up window - with page.expect_popup() as popup_info: - page.get_by_role("button", name="Enroll").click() - - popup = popup_info.value - popup.wait_for_timeout(3000) - - popup.get_by_text("Cardholder name").click() - - cardholder_name = "Test User" - popup.get_by_label("Cardholder name").fill(cardholder_name) - popup.keyboard.press("Tab") - - card_number = "4111 1111 1111 1111" - popup.get_by_label("Card number").fill(card_number) - popup.keyboard.press("Tab") - - expiration = "12/34" - popup.get_by_label("mm/yy").fill(expiration) - popup.keyboard.press("Tab") - - security_code = "123" - popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) - - # trigger form validation - not sure why their form behaves this way - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Tab") + enrollment_index = login_gov.enter_otp(totp.now()) - popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + # enrollment can take a bit page.wait_for_timeout(10000) reenrollment_error_message = page.get_by_text("You are still enrolled in this benefit") From 87e6235b6e646e5de946375502a64316f537777a Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 00:18:14 +0000 Subject: [PATCH 09/18] feat: add working Medicare.gov flow test --- .../smoke_tests/test_medicare_flow.py | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/playwright/smoke_tests/test_medicare_flow.py diff --git a/tests/playwright/smoke_tests/test_medicare_flow.py b/tests/playwright/smoke_tests/test_medicare_flow.py new file mode 100644 index 0000000000..66137ef4d6 --- /dev/null +++ b/tests/playwright/smoke_tests/test_medicare_flow.py @@ -0,0 +1,67 @@ +import os +from playwright.sync_api import Page, expect + + +def test_medicare_cardholder_flow(page: Page): + page.goto("/") + + page.click("text='Choose your Provider'") + page.get_by_role("link", name="California State Transit").click() + + page.get_by_label("Medicare Cardholder").check() + page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading + page.get_by_role("button", name="Choose this benefit").click() + + page.add_init_script("delete Object.getPrototypeOf(navigator).webdriver") + + page.get_by_role("button", name="Continue to Medicare.gov").click() + + page.get_by_label("Username", exact=True).click() + + username = os.environ.get("PLAYWRIGHT_MEDICARE_GOV_USERNAME") + page.get_by_label("Username", exact=True).fill(username) + + password = os.environ.get("PLAYWRIGHT_MEDICARE_GOV_PASSWORD") + page.get_by_label("Password").fill(password) + + page.get_by_role("button", name="Log in").click() + + page.get_by_role("button", name="Connect").click() + + # Enrollment Index - fill out transit processor form in pop-up window + with page.expect_popup() as popup_info: + page.get_by_role("button", name="Enroll").click() + + popup = popup_info.value + popup.wait_for_timeout(3000) + + popup.get_by_text("Cardholder name").click() + + cardholder_name = "Test User" + popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.keyboard.press("Tab") + + card_number = "4111 1111 1111 1111" + popup.get_by_label("Card number").fill(card_number) + popup.keyboard.press("Tab") + + expiration = "12/34" + popup.get_by_label("mm/yy").fill(expiration) + popup.keyboard.press("Tab") + + security_code = "123" + popup.get_by_text("Security code", exact=True).click() + popup.get_by_label("Security code").fill(security_code) + + # trigger form validation - not sure why their form behaves this way + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Shift+Tab") + popup.keyboard.press("Tab") + + popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + + page.wait_for_timeout(10000) + + success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") + expect(success_message).to_be_visible() From cef4db193aa167657592e8aebd4e65c062daf3d4 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 00:34:11 +0000 Subject: [PATCH 10/18] refactor(medicare_gov): use Page object model pattern --- tests/playwright/smoke_tests/models.py | 27 +++++++++ .../smoke_tests/test_medicare_flow.py | 59 ++++--------------- 2 files changed, 38 insertions(+), 48 deletions(-) diff --git a/tests/playwright/smoke_tests/models.py b/tests/playwright/smoke_tests/models.py index c126e576ab..a6d2651554 100644 --- a/tests/playwright/smoke_tests/models.py +++ b/tests/playwright/smoke_tests/models.py @@ -42,6 +42,12 @@ def get_started_with_login_gov(self): return LoginGov(page) + def continue_to_medicare_gov(self): + page = self.page + page.get_by_role("button", name="Continue to Medicare.gov").click() + + return MedicareGov(page) + class EligibilityConfirm: def __init__(self, page: Page): @@ -85,6 +91,27 @@ def enter_otp(self, one_time_password): return EnrollmentIndex(page) +class MedicareGov: + def __init__(self, page: Page): + self.page = page + + def log_in(self, username, password): + page = self.page + + page.get_by_label("Username", exact=True).click() + page.get_by_label("Username", exact=True).fill(username) + + page.get_by_label("Password").fill(password) + + page.get_by_role("button", name="Log in").click() + + def accept_consent_screen(self): + page = self.page + page.get_by_role("button", name="Connect").click() + + return EnrollmentIndex(page) + + class EnrollmentIndex: def __init__(self, page: Page): self.page = page diff --git a/tests/playwright/smoke_tests/test_medicare_flow.py b/tests/playwright/smoke_tests/test_medicare_flow.py index 66137ef4d6..7b97b830be 100644 --- a/tests/playwright/smoke_tests/test_medicare_flow.py +++ b/tests/playwright/smoke_tests/test_medicare_flow.py @@ -1,66 +1,29 @@ import os from playwright.sync_api import Page, expect +from models import Index + def test_medicare_cardholder_flow(page: Page): page.goto("/") - page.click("text='Choose your Provider'") - page.get_by_role("link", name="California State Transit").click() - - page.get_by_label("Medicare Cardholder").check() - page.wait_for_load_state("networkidle") # wait for reCAPTCHA to finish loading - page.get_by_role("button", name="Choose this benefit").click() + index = Index(page) + eligibility_index = index.select_agency("California State Transit") + eligibility_start = eligibility_index.select_flow("Medicare Cardholder") + # avoid looking like a bot page.add_init_script("delete Object.getPrototypeOf(navigator).webdriver") - page.get_by_role("button", name="Continue to Medicare.gov").click() - - page.get_by_label("Username", exact=True).click() + medicare_gov = eligibility_start.continue_to_medicare_gov() username = os.environ.get("PLAYWRIGHT_MEDICARE_GOV_USERNAME") - page.get_by_label("Username", exact=True).fill(username) - password = os.environ.get("PLAYWRIGHT_MEDICARE_GOV_PASSWORD") - page.get_by_label("Password").fill(password) - - page.get_by_role("button", name="Log in").click() - - page.get_by_role("button", name="Connect").click() - - # Enrollment Index - fill out transit processor form in pop-up window - with page.expect_popup() as popup_info: - page.get_by_role("button", name="Enroll").click() - - popup = popup_info.value - popup.wait_for_timeout(3000) - - popup.get_by_text("Cardholder name").click() - - cardholder_name = "Test User" - popup.get_by_label("Cardholder name").fill(cardholder_name) - popup.keyboard.press("Tab") - - card_number = "4111 1111 1111 1111" - popup.get_by_label("Card number").fill(card_number) - popup.keyboard.press("Tab") - - expiration = "12/34" - popup.get_by_label("mm/yy").fill(expiration) - popup.keyboard.press("Tab") - - security_code = "123" - popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) - - # trigger form validation - not sure why their form behaves this way - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Shift+Tab") - popup.keyboard.press("Tab") + medicare_gov.log_in(username, password) + enrollment_index = medicare_gov.accept_consent_screen() - popup.get_by_role("group", name="Enter your card details").get_by_role("button").click() + enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + # enrollment can take a bit page.wait_for_timeout(10000) success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") From 8057ab4d963f639c0989de4ce3f92506dc8bb9c6 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 00:49:07 +0000 Subject: [PATCH 11/18] refactor: use special User-Agent for smoke testing --- .../smoke_tests/test_agency_flow.py | 9 +++++--- .../smoke_tests/test_login_gov_flow.py | 23 +++++++++++++------ .../smoke_tests/test_medicare_flow.py | 9 +++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/tests/playwright/smoke_tests/test_agency_flow.py b/tests/playwright/smoke_tests/test_agency_flow.py index 8f5188e40d..4370745a26 100644 --- a/tests/playwright/smoke_tests/test_agency_flow.py +++ b/tests/playwright/smoke_tests/test_agency_flow.py @@ -1,10 +1,13 @@ -from playwright.sync_api import Page, expect +from playwright.sync_api import Browser, expect from models import Index -def test_agency_card_flow(page: Page): - page.goto("/") +def test_agency_card_flow(browser: Browser, base_url): + context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") + page = context.new_page() + + page.goto(base_url) index = Index(page) eligibility_index = index.select_agency("California State Transit") diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/smoke_tests/test_login_gov_flow.py index 7951c4492b..c013d745e4 100644 --- a/tests/playwright/smoke_tests/test_login_gov_flow.py +++ b/tests/playwright/smoke_tests/test_login_gov_flow.py @@ -1,13 +1,16 @@ import os -from playwright.sync_api import Page, expect +from playwright.sync_api import Browser, expect import pyotp from models import Index -def test_older_adult_flow(page: Page): - page.goto("/") +def test_older_adult_flow(browser: Browser, base_url): + context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") + page = context.new_page() + + page.goto(base_url) index = Index(page) eligibility_index = index.select_agency("California State Transit") @@ -33,8 +36,11 @@ def test_older_adult_flow(page: Page): expect(success_message).to_be_visible() -def test_us_veteran_flow(page: Page): - page.goto("/") +def test_us_veteran_flow(browser: Browser, base_url): + context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") + page = context.new_page() + + page.goto(base_url) index = Index(page) eligibility_index = index.select_agency("California State Transit") @@ -60,8 +66,11 @@ def test_us_veteran_flow(page: Page): expect(success_message).to_be_visible() -def test_calfresh_cardholder_flow(page: Page): - page.goto("/") +def test_calfresh_cardholder_flow(browser: Browser, base_url): + context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") + page = context.new_page() + + page.goto(base_url) index = Index(page) eligibility_index = index.select_agency("California State Transit") diff --git a/tests/playwright/smoke_tests/test_medicare_flow.py b/tests/playwright/smoke_tests/test_medicare_flow.py index 7b97b830be..ad74350730 100644 --- a/tests/playwright/smoke_tests/test_medicare_flow.py +++ b/tests/playwright/smoke_tests/test_medicare_flow.py @@ -1,11 +1,14 @@ import os -from playwright.sync_api import Page, expect +from playwright.sync_api import Browser, expect from models import Index -def test_medicare_cardholder_flow(page: Page): - page.goto("/") +def test_medicare_cardholder_flow(browser: Browser, base_url): + context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") + page = context.new_page() + + page.goto(base_url) index = Index(page) eligibility_index = index.select_agency("California State Transit") From c68c0ce912c50dbec2eaefa1b3eb4bdcc3281c2a Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Sat, 11 Jan 2025 01:10:34 +0000 Subject: [PATCH 12/18] chore: add comment linking to Playwright docs on page object models --- tests/playwright/smoke_tests/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/playwright/smoke_tests/models.py b/tests/playwright/smoke_tests/models.py index a6d2651554..a3fcf65ef8 100644 --- a/tests/playwright/smoke_tests/models.py +++ b/tests/playwright/smoke_tests/models.py @@ -1,3 +1,9 @@ +""" +Page object models for Playwright tests. + +https://playwright.dev/python/docs/pom +""" + from playwright.sync_api import Page From a0ce8f6880c2c8c6a4cd1f290b48f45196d5bd76 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 14 May 2025 17:06:33 +0000 Subject: [PATCH 13/18] chore: rename folder to qa --- tests/playwright/{smoke_tests => qa}/models.py | 0 tests/playwright/{smoke_tests => qa}/test_agency_flow.py | 0 tests/playwright/{smoke_tests => qa}/test_login_gov_flow.py | 0 tests/playwright/{smoke_tests => qa}/test_medicare_flow.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/playwright/{smoke_tests => qa}/models.py (100%) rename tests/playwright/{smoke_tests => qa}/test_agency_flow.py (100%) rename tests/playwright/{smoke_tests => qa}/test_login_gov_flow.py (100%) rename tests/playwright/{smoke_tests => qa}/test_medicare_flow.py (100%) diff --git a/tests/playwright/smoke_tests/models.py b/tests/playwright/qa/models.py similarity index 100% rename from tests/playwright/smoke_tests/models.py rename to tests/playwright/qa/models.py diff --git a/tests/playwright/smoke_tests/test_agency_flow.py b/tests/playwright/qa/test_agency_flow.py similarity index 100% rename from tests/playwright/smoke_tests/test_agency_flow.py rename to tests/playwright/qa/test_agency_flow.py diff --git a/tests/playwright/smoke_tests/test_login_gov_flow.py b/tests/playwright/qa/test_login_gov_flow.py similarity index 100% rename from tests/playwright/smoke_tests/test_login_gov_flow.py rename to tests/playwright/qa/test_login_gov_flow.py diff --git a/tests/playwright/smoke_tests/test_medicare_flow.py b/tests/playwright/qa/test_medicare_flow.py similarity index 100% rename from tests/playwright/smoke_tests/test_medicare_flow.py rename to tests/playwright/qa/test_medicare_flow.py From 00669df3b84858191c264666884152fad87e1cf0 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 14 May 2025 17:27:08 +0000 Subject: [PATCH 14/18] refactor: consolidate setting user-agent to fixture --- tests/playwright/qa/conftest.py | 6 ++++++ tests/playwright/qa/test_agency_flow.py | 5 ++--- tests/playwright/qa/test_login_gov_flow.py | 11 ++++------- tests/playwright/qa/test_medicare_flow.py | 5 ++--- 4 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 tests/playwright/qa/conftest.py diff --git a/tests/playwright/qa/conftest.py b/tests/playwright/qa/conftest.py new file mode 100644 index 0000000000..032ac28b1f --- /dev/null +++ b/tests/playwright/qa/conftest.py @@ -0,0 +1,6 @@ +import pytest + + +@pytest.fixture +def browser_context_args(): + return dict(user_agent="cal-itp/benefits-smoke-test") diff --git a/tests/playwright/qa/test_agency_flow.py b/tests/playwright/qa/test_agency_flow.py index 4370745a26..c750b72344 100644 --- a/tests/playwright/qa/test_agency_flow.py +++ b/tests/playwright/qa/test_agency_flow.py @@ -1,10 +1,9 @@ -from playwright.sync_api import Browser, expect +from playwright.sync_api import BrowserContext, expect from models import Index -def test_agency_card_flow(browser: Browser, base_url): - context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") +def test_agency_card_flow(context: BrowserContext, base_url): page = context.new_page() page.goto(base_url) diff --git a/tests/playwright/qa/test_login_gov_flow.py b/tests/playwright/qa/test_login_gov_flow.py index c013d745e4..3b5aa384a9 100644 --- a/tests/playwright/qa/test_login_gov_flow.py +++ b/tests/playwright/qa/test_login_gov_flow.py @@ -1,13 +1,12 @@ import os -from playwright.sync_api import Browser, expect +from playwright.sync_api import BrowserContext, expect import pyotp from models import Index -def test_older_adult_flow(browser: Browser, base_url): - context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") +def test_older_adult_flow(context: BrowserContext, base_url): page = context.new_page() page.goto(base_url) @@ -36,8 +35,7 @@ def test_older_adult_flow(browser: Browser, base_url): expect(success_message).to_be_visible() -def test_us_veteran_flow(browser: Browser, base_url): - context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") +def test_us_veteran_flow(context: BrowserContext, base_url): page = context.new_page() page.goto(base_url) @@ -66,8 +64,7 @@ def test_us_veteran_flow(browser: Browser, base_url): expect(success_message).to_be_visible() -def test_calfresh_cardholder_flow(browser: Browser, base_url): - context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") +def test_calfresh_cardholder_flow(context: BrowserContext, base_url): page = context.new_page() page.goto(base_url) diff --git a/tests/playwright/qa/test_medicare_flow.py b/tests/playwright/qa/test_medicare_flow.py index ad74350730..52eff03def 100644 --- a/tests/playwright/qa/test_medicare_flow.py +++ b/tests/playwright/qa/test_medicare_flow.py @@ -1,11 +1,10 @@ import os -from playwright.sync_api import Browser, expect +from playwright.sync_api import BrowserContext, expect from models import Index -def test_medicare_cardholder_flow(browser: Browser, base_url): - context = browser.new_context(user_agent="cal-itp/benefits-smoke-test") +def test_medicare_cardholder_flow(context: BrowserContext, base_url): page = context.new_page() page.goto(base_url) From ef81952f925b59110e698f800e69d2fac922bc90 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 14 May 2025 17:27:58 +0000 Subject: [PATCH 15/18] fix: the role of this element needs to be "link" for some reason --- tests/playwright/qa/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playwright/qa/models.py b/tests/playwright/qa/models.py index a3fcf65ef8..ff36c574ad 100644 --- a/tests/playwright/qa/models.py +++ b/tests/playwright/qa/models.py @@ -38,7 +38,7 @@ def __init__(self, page: Page): def click_continue(self): page = self.page - page.get_by_role("button", name="Continue").click() + page.get_by_role("link", name="Continue").click() return EligibilityConfirm(page) From cdbdc79477e6e48484a042a652d4b62f68012675 Mon Sep 17 00:00:00 2001 From: Luis Alvergue Date: Wed, 14 May 2025 18:38:40 +0000 Subject: [PATCH 16/18] refactor(tests): introduce fixtures for agency card flows --- tests/playwright/qa/__init__.py | 0 tests/playwright/qa/conftest.py | 49 ++++++++++++++++++++++ tests/playwright/qa/models.py | 12 +++--- tests/playwright/qa/test_agency_flow.py | 31 +++++++------- tests/playwright/qa/test_login_gov_flow.py | 2 +- tests/playwright/qa/test_medicare_flow.py | 2 +- 6 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 tests/playwright/qa/__init__.py diff --git a/tests/playwright/qa/__init__.py b/tests/playwright/qa/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/playwright/qa/conftest.py b/tests/playwright/qa/conftest.py index 032ac28b1f..c529f689b0 100644 --- a/tests/playwright/qa/conftest.py +++ b/tests/playwright/qa/conftest.py @@ -1,6 +1,55 @@ +from dataclasses import dataclass import pytest +@dataclass +class AgencyFlow: + agency: str + flow: str + + +@dataclass +class AgencyCardCredentials: + sub: str + name: str + + +@dataclass +class PaymentCard: + cardholder_name: str + card_number: str + expiration: str + security_code: str + + @pytest.fixture def browser_context_args(): return dict(user_agent="cal-itp/benefits-smoke-test") + + +@pytest.fixture +def agency_card_flows(): + return [ + ( + AgencyFlow(agency="California State Transit", flow="Agency Cardholder"), + AgencyCardCredentials(sub="71162", name="Box"), + ), + ( + AgencyFlow(agency="Monterey-Salinas Transit", flow="Courtesy Card"), + AgencyCardCredentials(sub="71162", name="Box"), + ), + ( + AgencyFlow(agency="Santa Barbara MTD", flow="Reduced Fare Mobility ID"), + AgencyCardCredentials(sub="1234", name="Barbara"), + ), + ] + + +@pytest.fixture +def valid_payment_card(): + return PaymentCard( + cardholder_name="Test User", + card_number="4111 1111 1111 1111", + expiration="12/34", + security_code="123", + ) diff --git a/tests/playwright/qa/models.py b/tests/playwright/qa/models.py index ff36c574ad..9df5cb9520 100644 --- a/tests/playwright/qa/models.py +++ b/tests/playwright/qa/models.py @@ -6,6 +6,8 @@ from playwright.sync_api import Page +from .conftest import PaymentCard + class Index: def __init__(self, page: Page): @@ -122,7 +124,7 @@ class EnrollmentIndex: def __init__(self, page: Page): self.page = page - def enroll(self, cardholder_name, card_number, expiration, security_code): + def enroll(self, payment_card: PaymentCard): page = self.page with page.expect_popup() as popup_info: @@ -133,17 +135,17 @@ def enroll(self, cardholder_name, card_number, expiration, security_code): popup.get_by_text("Cardholder name").click() - popup.get_by_label("Cardholder name").fill(cardholder_name) + popup.get_by_label("Cardholder name").fill(payment_card.cardholder_name) popup.keyboard.press("Tab") - popup.get_by_label("Card number").fill(card_number) + popup.get_by_label("Card number").fill(payment_card.card_number) popup.keyboard.press("Tab") - popup.get_by_label("mm/yy").fill(expiration) + popup.get_by_label("mm/yy").fill(payment_card.expiration) popup.keyboard.press("Tab") popup.get_by_text("Security code", exact=True).click() - popup.get_by_label("Security code").fill(security_code) + popup.get_by_label("Security code").fill(payment_card.security_code) # trigger form validation - not sure why their form behaves this way popup.keyboard.press("Shift+Tab") diff --git a/tests/playwright/qa/test_agency_flow.py b/tests/playwright/qa/test_agency_flow.py index c750b72344..0af99a7e96 100644 --- a/tests/playwright/qa/test_agency_flow.py +++ b/tests/playwright/qa/test_agency_flow.py @@ -1,22 +1,25 @@ from playwright.sync_api import BrowserContext, expect -from models import Index +from .models import Index -def test_agency_card_flow(context: BrowserContext, base_url): - page = context.new_page() +def test_agency_card_flow(context: BrowserContext, base_url, agency_card_flows, valid_payment_card): + for agency_flow, cred in agency_card_flows: + print(agency_flow) - page.goto(base_url) + page = context.new_page() - index = Index(page) - eligibility_index = index.select_agency("California State Transit") - eligibility_start = eligibility_index.select_flow("Agency Cardholder") - eligibility_confirm = eligibility_start.click_continue() - enrollment_index = eligibility_confirm.submit_form("71162", "Box") - enrollment_index.enroll("Test User", "4111 1111 1111 1111", "12/34", "123") + page.goto(base_url) - # enrollment can take a bit - page.wait_for_timeout(10000) + index = Index(page) + eligibility_index = index.select_agency(agency_flow.agency) + eligibility_start = eligibility_index.select_flow(agency_flow.flow) + eligibility_confirm = eligibility_start.click_continue() + enrollment_index = eligibility_confirm.submit_form(cred.sub, cred.name) + enrollment_index.enroll(valid_payment_card) - success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") - expect(success_message).to_be_visible() + # enrollment can take a bit + page.wait_for_timeout(15000) + + success_message = page.get_by_text("You can now use your contactless card to tap to ride with a reduced fare!") + expect(success_message).to_be_visible() diff --git a/tests/playwright/qa/test_login_gov_flow.py b/tests/playwright/qa/test_login_gov_flow.py index 3b5aa384a9..5854af1d3c 100644 --- a/tests/playwright/qa/test_login_gov_flow.py +++ b/tests/playwright/qa/test_login_gov_flow.py @@ -3,7 +3,7 @@ from playwright.sync_api import BrowserContext, expect import pyotp -from models import Index +from .models import Index def test_older_adult_flow(context: BrowserContext, base_url): diff --git a/tests/playwright/qa/test_medicare_flow.py b/tests/playwright/qa/test_medicare_flow.py index 52eff03def..339d819c36 100644 --- a/tests/playwright/qa/test_medicare_flow.py +++ b/tests/playwright/qa/test_medicare_flow.py @@ -1,7 +1,7 @@ import os from playwright.sync_api import BrowserContext, expect -from models import Index +from .models import Index def test_medicare_cardholder_flow(context: BrowserContext, base_url): From 0a37808b663a127be46a5083a0956d5e77a4ca43 Mon Sep 17 00:00:00 2001 From: Luis Alvergue Date: Wed, 14 May 2025 18:55:36 +0000 Subject: [PATCH 17/18] refactor(tests): use id selectors for confirm form --- tests/playwright/qa/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/playwright/qa/models.py b/tests/playwright/qa/models.py index 9df5cb9520..a51785b11b 100644 --- a/tests/playwright/qa/models.py +++ b/tests/playwright/qa/models.py @@ -63,12 +63,12 @@ def __init__(self, page: Page): def submit_form(self, sub, name): page = self.page - page.get_by_placeholder("12345").click() + page.locator("#sub").click() - page.get_by_placeholder("12345").fill(sub) + page.locator("#sub").fill(sub) page.keyboard.press("Tab") - page.get_by_placeholder("Hernandez-Demarcos").fill(name) + page.locator("#name").fill(name) page.get_by_role("button", name="Find my record").click() From 6fb0b90f91ce8701ed246e0af7c22397b523bea6 Mon Sep 17 00:00:00 2001 From: Luis Alvergue Date: Wed, 28 May 2025 18:05:04 +0000 Subject: [PATCH 18/18] chore: WIP - start definition of yaml config --- tests/playwright/qa/config.yml | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/playwright/qa/config.yml diff --git a/tests/playwright/qa/config.yml b/tests/playwright/qa/config.yml new file mode 100644 index 0000000000..7bb4005721 --- /dev/null +++ b/tests/playwright/qa/config.yml @@ -0,0 +1,69 @@ +test: + - agency: "California State Transit" + flow: "Older Adult" + username: "username" + password: "password" + totp_secret: "secret" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + - agency: "California State Transit" + flow: "U.S. Veteran" + username: "username" + password: "password" + totp_secret: "secret" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + - agency: "California State Transit" + flow: "CalFresh Cardholder" + username: "username" + password: "password" + totp_secret: "secret" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + - agency: "California State Transit" + flow: "Agency Cardholder" + sub: "sub" + name: "name" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + - agency: "California State Transit" + flow: "Medicare Cardholder" + username: "username" + password: "password" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + +older_adult: + name: "Older Adult" + tests: + - agency: "California State Transit" + username: "username" + password: "password" + totp_secret: "secret" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv" + +veteran: + name: "U.S. Veteran" + tests: + - agency: "California State Transit" + name: "U.S. Veteran" + username: "username" + password: "password" + totp_secret: "secret" + card_user: "user" + card_number: "number" + card_expiration: "exp" + card_ccv: "ccv"