Skip to content

Commit d3726e2

Browse files
Feature/bcss 20856 selenium to playwright datasets setup (#102)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description <!-- Describe your changes in detail. --> Adding two new scenarios from the selenium test framework. ## Context <!-- Why is this change required? What problem does it solve? --> Migration of tests from selenium to playwright ## Type of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> - [x] Refactoring (non-breaking change) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would change existing functionality) - [ ] Bug fix (non-breaking change which fixes an issue) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I am familiar with the [contributing guidelines](https://github.com/nhs-england-tools/playwright-python-blueprint/blob/main/CONTRIBUTING.md) - [x] I have followed the code style of the project - [x] I have added tests to cover my changes (where appropriate) - [x] I have updated the documentation accordingly - [ ] This PR is a result of pair or mob programming --- ## Sensitive Information Declaration To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including [PII (Personal Identifiable Information) / PID (Personal Identifiable Data)](https://digital.nhs.uk/data-and-information/keeping-data-safe-and-benefitting-the-public) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter. - [x] I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.
1 parent a815c5c commit d3726e2

File tree

17 files changed

+1156
-58
lines changed

17 files changed

+1156
-58
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ test-results/
1818
axe-reports/
1919
.env
2020
local.env
21+
.test_last_runs.json
2122

2223
# Please, add your custom content below!

conftest.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from _pytest.python import Function
1313
from pytest_html.report_data import ReportData
1414
from utils.load_properties_file import PropertiesFile
15+
from _pytest.config.argparsing import Parser
16+
from _pytest.fixtures import FixtureRequest
1517

1618
# Environment Variable Handling
1719

@@ -64,3 +66,43 @@ def pytest_runtest_makereport(item: Function) -> typing.Generator[None, None, No
6466
if outcome is not None:
6567
report = outcome.get_result()
6668
report.description = str(item.function.__doc__)
69+
70+
71+
# Command-Line Options for Pytest
72+
73+
74+
def pytest_addoption(parser: Parser) -> None:
75+
"""
76+
Add custom command-line options to pytest.
77+
78+
Args:
79+
parser (Parser): The pytest parser object used to define CLI options.
80+
81+
Adds:
82+
--subjects-to-run-for (int):
83+
The number of subjects to run the test setup for.
84+
Default is 10.
85+
86+
Example:
87+
pytest tests/test_setup.py::test_setup_subjects_as_a259 --subjects-to-run-for=5
88+
"""
89+
parser.addoption(
90+
"--subjects-to-run-for",
91+
action="store",
92+
default="10",
93+
help="Number of subjects to run the test setup for (default: 10)",
94+
)
95+
96+
97+
@pytest.fixture
98+
def subjects_to_run_for(request: FixtureRequest) -> int:
99+
"""
100+
Fixture to retrieve the value of the '--subjects-to-run-for' CLI argument.
101+
102+
Args:
103+
request (FixtureRequest): Provides access to the requesting test context.
104+
105+
Returns:
106+
int: The number of subjects specified via the CLI or default (10).
107+
"""
108+
return int(request.config.getoption("--subjects-to-run-for")) # type: ignore

docs/utility-guides/LastTestRun.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Utility Guide: last_test_run
2+
3+
The `last_test_run` utility provides a simple way to track when specific tests were last executed. It is designed to help you avoid running setups for tests multiple times and instead does a check to see if the test has been run today.
4+
5+
---
6+
7+
## Table of Contents
8+
9+
- [Utility Guide: last\_test\_run](#utility-guide-last_test_run)
10+
- [Table of Contents](#table-of-contents)
11+
- [Overview](#overview)
12+
- [When and Why to Use last\_test\_run](#when-and-why-to-use-last_test_run)
13+
- [Required Arguments](#required-arguments)
14+
- [last\_test\_run Methods](#last_test_run-methods)
15+
- [Example Usage](#example-usage)
16+
- [How It Works](#how-it-works)
17+
- [Implementation Details](#implementation-details)
18+
19+
---
20+
21+
## Overview
22+
23+
This utility manages a JSON file (`.test_last_runs.json`) that records the last date each test was run. It provides functions to load, save, and check this data, making it easy to implement "run once per day" logic in your test suite.
24+
25+
---
26+
27+
## When and Why to Use last_test_run
28+
29+
You might want to use this utility in scenarios such as:
30+
31+
- Avoiding repeated execution of slow or stateful tests within the same day.
32+
- Ensuring setup or tear down routines only run once per day.
33+
- Tracking test execution dates for reporting or debugging.
34+
35+
---
36+
37+
## Required Arguments
38+
39+
Each function in this utility requires specific arguments:
40+
41+
- `has_test_run_today(test_name: str) -> bool`:
42+
- `test_name` (str): The unique name of the test to check.
43+
44+
See the docstrings in the code for details on each function.
45+
46+
---
47+
48+
## last_test_run Methods
49+
50+
**The main methods provided are:**
51+
52+
- **load_last_run_data() -> Dict[str, Any]**
53+
Loads the last run data from the JSON file. Returns a dictionary mapping test names to their last run date.
54+
55+
- **save_last_run_data(data: Dict[str, Any]) -> None**
56+
Saves the provided dictionary to the JSON file.
57+
58+
- **has_test_run_today(test_name: str) -> `bool`**
59+
Checks if the given test has already run today. If not, updates the record to mark it as run today.
60+
61+
---
62+
63+
## Example Usage
64+
65+
```python
66+
from utils.last_test_run import has_test_run_today
67+
68+
def test_expensive_setup():
69+
if has_test_run_today("test_expensive_setup"):
70+
print("Setup already run today, skipping.")
71+
return
72+
# ... perform expensive setup ...
73+
print("Setup complete.")
74+
```
75+
76+
---
77+
78+
## How It Works
79+
80+
- The utility stores a mapping of test names to their last run date in `.test_last_runs.json`.
81+
- When `has_test_run_today` is called, it checks if the test has already run today.
82+
- If yes, it returns `True`.
83+
- If no, it updates the file and returns `False`.
84+
85+
---
86+
87+
## Implementation Details
88+
89+
- The JSON file is created in the project root if it does not exist.
90+
- If the file is empty or corrupted, the utility will safely return an empty dictionary and continue.
91+
- All functions are type-annotated and documented with docstrings for clarity.
92+
93+
---
94+
95+
> **Note:**
96+
> The `last_test_run` utility is available under `utils/last_test_run.py`.
97+
> See the source code for more details and to extend its functionality as needed.

pages/base_page.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def __init__(self, page: Page):
5353
"#ntshPageTitle"
5454
)
5555
self.main_menu__header = self.page.locator("#ntshPageTitle")
56+
self.log_in_page = self.page.get_by_role("button", name="Log in")
5657

5758
def click_main_menu_link(self) -> None:
5859
"""Click the Base Page 'Main Menu' link if it is visible."""
@@ -271,6 +272,10 @@ def handle_dialog(dialog: Dialog):
271272

272273
self.page.once("dialog", handle_dialog)
273274

275+
def go_to_log_in_page(self) -> None:
276+
"""Click on the Log in button to navigate to the login page."""
277+
self.click(self.log_in_page)
278+
274279
def safe_accept_dialog_select_option(self, locator: Locator, option: str) -> None:
275280
"""
276281
Safely accepts a dialog triggered by selecting a dropdown, avoiding the error:

0 commit comments

Comments
 (0)