Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3c45243
refactor: move healthcheck test into a healthcheck folder
angela-tran Jan 9, 2025
c3d24d8
refactor: specify base URL as CLI argument
angela-tran Jan 9, 2025
ae402c8
feat: add agency flow smoke test
angela-tran Jan 9, 2025
419cc0a
feat: add working older adult flow test with one-time password as MFA
angela-tran Jan 10, 2025
2a843da
feat: add veteran and calfresh Login.gov flows
angela-tran Jan 10, 2025
be322a9
refactor(agency_card): use Page object models for readability and reuse
angela-tran Jan 10, 2025
13a4886
refactor(login_gov): use Page object model pattern for older adult flow
angela-tran Jan 11, 2025
6b078d1
refactor(login_gov): use same pattern for veteran and calfresh flow
angela-tran Jan 11, 2025
87e6235
feat: add working Medicare.gov flow test
angela-tran Jan 11, 2025
cef4db1
refactor(medicare_gov): use Page object model pattern
angela-tran Jan 11, 2025
8057ab4
refactor: use special User-Agent for smoke testing
angela-tran Jan 11, 2025
c68c0ce
chore: add comment linking to Playwright docs on page object models
angela-tran Jan 11, 2025
a0ce8f6
chore: rename folder to qa
angela-tran May 14, 2025
00669df
refactor: consolidate setting user-agent to fixture
angela-tran May 14, 2025
ef81952
fix: the role of this element needs to be "link" for some reason
angela-tran May 14, 2025
cdbdc79
refactor(tests): introduce fixtures for agency card flows
lalver1 May 14, 2025
0a37808
refactor(tests): use id selectors for confirm form
lalver1 May 14, 2025
6fb0b90
chore: WIP - start definition of yaml config
lalver1 May 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Empty file.
69 changes: 69 additions & 0 deletions tests/playwright/qa/config.yml
Original file line number Diff line number Diff line change
@@ -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"
55 changes: 55 additions & 0 deletions tests/playwright/qa/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +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",
)
156 changes: 156 additions & 0 deletions tests/playwright/qa/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
"""
Page object models for Playwright tests.

https://playwright.dev/python/docs/pom
"""

from playwright.sync_api import Page

from .conftest import PaymentCard


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("link", name="Continue").click()

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)

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):
self.page = page

def submit_form(self, sub, name):
page = self.page
page.locator("#sub").click()

page.locator("#sub").fill(sub)
page.keyboard.press("Tab")

page.locator("#name").fill(name)

page.get_by_role("button", name="Find my record").click()

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 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

def enroll(self, payment_card: PaymentCard):
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(payment_card.cardholder_name)
popup.keyboard.press("Tab")

popup.get_by_label("Card number").fill(payment_card.card_number)
popup.keyboard.press("Tab")

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(payment_card.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()
25 changes: 25 additions & 0 deletions tests/playwright/qa/test_agency_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from playwright.sync_api import BrowserContext, expect

from .models import Index


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 = context.new_page()

page.goto(base_url)

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)

# 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()
Loading