-
Notifications
You must be signed in to change notification settings - Fork 14
[AB#307495]-setup selenium base + add create program test #5853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mmaciekk
wants to merge
24
commits into
develop
Choose a base branch
from
selenium-create-program
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
a701342
[AB#307495]-setup selenium base + add create program test
mmaciekk 1b32f33
fix tox.ini / pipeline setup for headless selenium required by seleni…
mmaciekk 463d279
admin panel url
mmaciekk cc1fd4e
add new rules to selenium_base, refactor test_create_program to adjus…
mmaciekk b675c81
replace sleep wit wait for element
mmaciekk 93c9b9e
add rule about wait for element instead of sleep, and remove flag fro…
mmaciekk d6d3f4e
format, lint
mmaciekk 8eb978f
PR comments addressed
mmaciekk 076de75
use select_option_by_name instead of select_listbox_element
mmaciekk c4a8074
add new pattern
mmaciekk 9661d3a
make selenium patterns more concise
mmaciekk 870a41b
remove if inside test function
mmaciekk dc12f1f
remove comments scroll and if
mmaciekk 2ca9194
final pr comments
mmaciekk a922ed1
Merge branch 'develop' into selenium-create-program
mmaciekk 423f41f
test pdu fields and other fields from program form
mmaciekk 3c136b9
fix add assert value
mmaciekk 0a32d07
fix edit pdu test + button next instead of clicking on step button
mmaciekk b9128dc
Merge remote-tracking branch 'origin' into selenium-create-program
mmaciekk 0e6587b
address Kamil's comments
mmaciekk 05b2593
uv.lock update
mmaciekk 9fa8449
rewrite flaky grievance test selenium base way
mmaciekk fab5d14
remove pytest.fixture(autouse=True)
mmaciekk 54a9968
Merge branch 'develop' into selenium-create-program
mmaciekk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| # Selenium E2E Test Patterns — HOPE Project | ||
|
|
||
| Reference guide for writing SeleniumBase E2E tests in the HOPE project. | ||
|
|
||
| **Key files:** | ||
|
|
||
| - `tests/e2e/helpers/selenium_base.py` — `HopeTestBrowser` (extends `seleniumbase.BaseCase`) | ||
| - `tests/e2e/new_selenium/conftest.py` — shared `browser`, `login`, | ||
| fixtures | ||
| - `tests/e2e/conftest.py` — autouse DB fixtures (`create_super_user`, `create_unicef_partner`, etc.) | ||
|
|
||
| --- | ||
|
|
||
| ## Test Style Rules (MANDATORY) | ||
|
|
||
| **These rules are non-negotiable. Violations will cause test failures or flaky runs.** | ||
|
|
||
| 1. **No `autouse=True`** on test data fixtures. Only infrastructure fixtures | ||
| (e.g. `test_failed_check`, `clear_default_cache`) may be autouse. | ||
| Test data fixtures must be explicitly requested by the test function. | ||
| 2. **No `browser.sleep()`** — use `wait_for_element_clickable`, `wait_for_element_visible`, | ||
| or `wait_for_element_absent` instead. Only as a last resort for CSS animations. | ||
| 3. Tests MUST be plain functions (`def test_*()`), never classes. | ||
| 4. One test = one scenario. Use `pytest.mark.parametrize` instead of loops. | ||
| 5. No `if / for / while` inside test bodies or test helper functions. | ||
| Loops in helpers are acceptable only for repetitive DOM actions (e.g. filling N round-name inputs). | ||
| 6. Test data created exclusively in fixtures using factories from | ||
| `extras.test_utils.factories` (not `old_factories`). | ||
| 7. Use `db` fixture, NOT `transaction=True` / `transactional_db`. | ||
| 8. Prefer CSS selectors with `data-cy` attributes over XPath. | ||
| 9. Use `login` fixture directly — do not alias (`browser = login`). | ||
| 10. Mock only external dependencies (network, S3, Celery). Never mock code under test. | ||
|
|
||
| --- | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| HopeTestBrowser (extends seleniumbase.BaseCase) | ||
| └── browser fixture (tests/e2e/new_selenium/conftest.py) | ||
| └── login fixture | ||
| └── test functions using login.click(), login.type(), login.assert_text() | ||
| ``` | ||
|
|
||
| `HopeTestBrowser` provides HOPE-specific helpers on top of SeleniumBase: | ||
|
|
||
| - `login(username, password)` — logs in via Django admin, clears browser storage | ||
| - `select_listbox_element(name)` — selects from MUI `ul[role="listbox"]` dropdowns | ||
| - `select_option_by_name(name)` — selects from `data-cy="select-option-*"` dropdowns | ||
| - `scroll_main_content(scroll_by)` — scrolls the MUI main content area | ||
|
|
||
| Existing raw Selenium tests (`Common → BaseComponents → PageObject`) are unaffected. | ||
|
|
||
| --- | ||
|
|
||
| ## Fixture Setup | ||
|
|
||
| The `browser` and `login` fixtures are defined in `tests/e2e/new_selenium/conftest.py`. | ||
| Do NOT redefine them. Create local `conftest.py` only for domain-specific data fixtures. | ||
|
|
||
| ### Autouse fixtures (from parent conftest.py) | ||
mmaciekk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| These fixtures already exist and run automatically. Do **not** add new autouse fixtures — this is prohibited by the new-style test rules (see rule 1 above). | ||
|
|
||
| | Fixture | Creates | | ||
| | ---------------------------------- | ----------------------------------------------------- | | ||
| | `create_super_user` | User (`superuser`/`testtest2`), partners, roles, DCTs | | ||
| | `create_unicef_partner` | UNICEF + UNICEF HQ partners | | ||
| | `create_role_with_all_permissions` | Role with all permissions | | ||
| | `clear_default_cache` | Clears Django cache | | ||
|
|
||
| ### On-demand fixtures | ||
|
|
||
| | Fixture | Creates | | ||
| | ------------------------- | ------------------------------------ | | ||
| | `business_area` | Afghanistan BA with flags, settings | | ||
| | `live_server_with_static` | Django live server with static files | | ||
|
|
||
| --- | ||
|
|
||
| ## SeleniumBase API Quick Reference | ||
|
|
||
| Full docs: [seleniumbase.io/help_docs/method_summary](https://seleniumbase.io/help_docs/method_summary/) | ||
|
|
||
| ### Wait instead of sleep | ||
|
|
||
| ```python | ||
| # BAD | ||
| browser.click(INPUT_NAME) # dismiss picker | ||
| browser.sleep(0.3) # flaky | ||
| browser.click(INPUT_END_DATE) | ||
|
|
||
| # GOOD | ||
| browser.click(INPUT_NAME) # dismiss picker | ||
| browser.wait_for_element_clickable(INPUT_END_DATE) | ||
| browser.click(INPUT_END_DATE) | ||
| ``` | ||
|
|
||
| ### Fetch element once for click + type | ||
|
|
||
| ```python | ||
| # BAD — two DOM lookups | ||
| browser.click(INPUT_START_DATE) | ||
| browser.send_keys(INPUT_START_DATE, "2024-01-01") | ||
|
|
||
| # GOOD — one lookup | ||
| el = browser.find_element(INPUT_START_DATE) | ||
| el.click() | ||
| el.send_keys("2024-01-01") | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Selector Conventions | ||
|
|
||
| All interactive elements use `data-cy` attributes. Common patterns: | ||
|
|
||
| ```python | ||
| # Buttons | ||
| 'a[data-cy="button-new-program"]' | ||
| 'button[data-cy="button-next"]' | ||
| 'button[data-cy="button-save"]' | ||
|
|
||
| # Inputs | ||
| 'input[data-cy="input-name"]' | ||
| 'textarea[data-cy="input-description"]' | ||
| 'input[name="startDate"]' # date inputs use name attr | ||
|
|
||
| # Dropdowns (click to open, then use helper) | ||
| 'div[data-cy="select-sector"]' # → browser.select_option_by_name("Child Protection") | ||
| 'div[data-cy="input-beneficiary-group"]' # → browser.select_listbox_element("Main Menu") | ||
|
|
||
| # Labels / display values | ||
| 'div[data-cy="label-Sector"]' | ||
| 'h5[data-cy="page-header-title"]' | ||
| 'div[data-cy="status-container"]' | ||
|
|
||
| # Navigation | ||
| 'a[data-cy="nav-Programmes"]' | ||
| 'a[data-cy="nav-Programme Details"]' | ||
| ``` | ||
|
|
||
| Discover selectors in the frontend source or browser DevTools — search for `data-cy`. | ||
|
|
||
| --- | ||
|
|
||
| ## Writing a New Test | ||
|
|
||
| ### 1. File structure | ||
|
|
||
| See `tests/e2e/new_selenium/program_details/test_create_program.py` as the first example of a new-style Selenium test. | ||
|
|
||
| ### 2. Template | ||
mmaciekk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ```python | ||
| import pytest | ||
| from extras.test_utils.selenium import HopeTestBrowser | ||
|
|
||
| pytestmark = pytest.mark.django_db() | ||
|
|
||
| # Selectors as module-level constants | ||
| HEADER = 'h5[data-cy="page-header-title"]' | ||
| BTN_ACTION = 'button[data-cy="button-action"]' | ||
|
|
||
|
|
||
| def test_feature_scenario(login: HopeTestBrowser) -> None: | ||
| # Navigate | ||
| login.click('a[data-cy="nav-Programmes"]') | ||
| login.wait_for_text("Expected Title", HEADER) | ||
|
|
||
| # Interact | ||
| login.click(BTN_ACTION) | ||
| login.type('input[data-cy="input-field"]', "value") | ||
|
|
||
| # Assert | ||
| login.assert_text("Expected Text", 'div[data-cy="label-Field"]') | ||
| ``` | ||
|
|
||
| ## Running Tests | ||
|
|
||
| ```bash | ||
| # Run SeleniumBase tests | ||
| tox -e tests -- tests/e2e/new_selenium/ | ||
|
|
||
| # Single test file | ||
| tox -e tests -- tests/e2e/new_selenium/program_details/test_create_program.py | ||
|
|
||
| # Single test | ||
| tox -e tests -- tests/e2e/new_selenium/program_details/test_create_program.py::test_name | ||
|
|
||
| # Visible browser | ||
| tox -e tests -- tests/e2e/new_selenium/ --headed | ||
|
|
||
| # Slow mode (demo) | ||
| tox -e tests -- tests/e2e/new_selenium/ --headed --demo | ||
|
|
||
| # Lint after changes | ||
| tox -e lint | ||
| ``` | ||
|
|
||
| tox runs: `pytest -q --create-db --no-migrations --dist=loadgroup {posargs:tests}` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from typing import Generator | ||
|
|
||
| import pytest | ||
| from seleniumbase import config as sb_config | ||
|
|
||
| from extras.test_utils.selenium import HopeTestBrowser | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def browser(live_server_with_static, request) -> Generator[HopeTestBrowser, None, None]: | ||
| sb = HopeTestBrowser("base_method") | ||
| sb.live_server_url = str(live_server_with_static) | ||
| sb.setUp() | ||
| sb._needs_tearDown = True | ||
| sb._using_sb_fixture = True | ||
| sb._using_sb_fixture_no_class = True | ||
| sb_config._sb_node[request.node.nodeid] = sb | ||
| yield sb | ||
| if sb._needs_tearDown: | ||
| sb.tearDown() | ||
| sb._needs_tearDown = False | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def login(browser: HopeTestBrowser) -> HopeTestBrowser: | ||
| browser.login() | ||
| return browser |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| from datetime import datetime | ||
|
|
||
| from dateutil.relativedelta import relativedelta | ||
| import pytest | ||
|
|
||
| from extras.test_utils.factories.core import DataCollectingTypeFactory | ||
| from extras.test_utils.factories.program import ProgramFactory | ||
| from hope.models import BeneficiaryGroup, BusinessArea, DataCollectingType, Program | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def social_worker_program() -> Program: | ||
| dct = DataCollectingTypeFactory(type=DataCollectingType.Type.SOCIAL) | ||
| beneficiary_group = BeneficiaryGroup.objects.get(name="People") | ||
| return ProgramFactory( | ||
| name="Social Program", | ||
| status=Program.ACTIVE, | ||
| business_area=BusinessArea.objects.get(slug="afghanistan"), | ||
| data_collecting_type=dct, | ||
| beneficiary_group=beneficiary_group, | ||
| start_date=datetime.now() - relativedelta(months=1), | ||
| end_date=datetime.now() + relativedelta(months=1), | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not a fan of these overrides
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@domdinicola Me neither, but here is the reason why.
seleniumbase/SeleniumBase#3619