[Feature Branch] Playwright Migration by Nikhil#103
Conversation
There was a problem hiding this comment.
Pull Request Overview
Migration from Selenium to Playwright for the PatternFly testing framework. This PR replaces Selenium WebDriver infrastructure with Playwright for improved test automation capabilities and modern browser automation.
- Complete replacement of Selenium-based browser automation with Playwright
- Updated test fixtures and configuration to support Playwright browser instances
- Modernized chart interaction methods using Playwright's JavaScript evaluation capabilities
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| testing/conftest.py | Complete overhaul of test fixtures replacing Selenium with Playwright browser management |
| testing/charts/test_pie_chart.py | Updated anchor element selection from href-based to id-based targeting |
| testing/charts/test_bullet_chart.py | Simplified anchor element selection to use direct h3 targeting |
| src/widgetastic_patternfly5/components/menus/dropdown.py | Removed Selenium-specific exception handling for UnexpectedAlertPresentException |
| src/widgetastic_patternfly5/charts/line_chart.py | Updated mouse interactions and element clicking for Playwright compatibility |
| src/widgetastic_patternfly5/charts/legend.py | Replaced CSS property access with JavaScript evaluation for computed styles |
| src/widgetastic_patternfly5/charts/bullet_chart.py | Enhanced chart data reading with force clicking and improved offset handling |
| src/widgetastic_patternfly5/charts/alerts_timeline_chart.py | Updated XPath locators and added force clicking for path elements |
| pyproject.toml | Modified dependencies to use development branch of widgetastic.core with Playwright support |
| .github/workflows/tests.yaml | Redesigned CI workflow for Playwright browser setup and caching |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
[playwright] Menu widgets migration
[Playwright] Date time and forms and some component widget migration
[playwright] table, switch, slider, title
[playwright] OUIA and updated readme
c739849 to
064493f
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #103 +/- ##
==========================================
- Coverage 99.84% 0.00% -99.85%
==========================================
Files 48 37 -11
Lines 1932 2113 +181
==========================================
- Hits 1929 0 -1929
- Misses 3 2113 +2110
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Reviewer's GuideThis PR migrates the test suite and component code from Selenium to Playwright, introducing new fixtures, configuration options, updated APIs, and documentation and CI workflow enhancements to fully support Playwright-driven testing. Sequence diagram for Playwright browser fixture setup in pytestsequenceDiagram
participant pytest as actor pytest
participant Playwright as Playwright
participant BrowserInstance as PlaywrightBrowser
participant Context as BrowserContext
participant Page as Page
participant Widgetastic as Browser
pytest->>Playwright: Start Playwright
Playwright->>BrowserInstance: Launch browser (chromium/firefox)
BrowserInstance->>Context: Create browser context
Context->>Page: Create new page
Page->>Widgetastic: Pass page to Browser
Widgetastic-->>pytest: Ready for tests
Sequence diagram for component interaction using Playwright APIssequenceDiagram
participant Test as actor Test
participant Widget as PatternFlyWidget
participant Browser as Browser
participant Page as Page
Test->>Widget: Call fill()/click()/read()
Widget->>Browser: Use browser API (e.g., click, fill, is_checked)
Browser->>Page: Execute Playwright action
Page-->>Browser: Return result
Browser-->>Widget: Return result
Widget-->>Test: Return result
Class diagram for updated browser and component interactionclassDiagram
class PlaywrightBrowser {
+new_context(viewport)
+close()
}
class BrowserContext {
+new_page()
+close()
}
class Page {
+goto(url)
+close()
}
class Browser {
+click(locator, force)
+text(locator, parent)
+is_checked(locator)
+fill(value, locator)
+value_of_css_property(element, property)
+move_to_element(element)
+move_by_offset(origin, x, y)
+wait_for_element(locator)
+element(locator, parent)
+attributes(locator)
+tag(locator)
}
PlaywrightBrowser --> BrowserContext
BrowserContext --> Page
Page --> Browser
class BaseSwitch {
+is_enabled
+click()
+selected
+fill(value: bool)
+label
+read()
}
class BaseCalendarMonth {
+month
+year
+day
+fill(value)
+read()
}
class BaseDualListSelector {
+_available
+_chosen
+_left_title
+_right_title
+read(selected_only)
}
class BaseSlider {
+fill(value)
+read()
}
class BaseClipboardCopy {
+is_editable
+is_inline
}
class BaseTitle {
+heading_level
+text
+read()
}
Browser <.. BaseSwitch
Browser <.. BaseCalendarMonth
Browser <.. BaseDualListSelector
Browser <.. BaseSlider
Browser <.. BaseClipboardCopy
Browser <.. BaseTitle
%% All components now interact with Browser via Playwright-backed APIs
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey there - I've reviewed your changes - here's some feedback:
Blocking issues:
- An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. (link)
General comments:
- Consider making the
pagefixture function-scoped (instead of session-scoped) to provide each test with a fresh page and avoid cross-test state leakage. - Extract the
sync_playwright()context into its own long-lived session fixture rather than nesting it inside the browser-launch fixture to clearly separate Playwright startup from browser instantiation and ensure proper teardown ordering. - Avoid hard sleeps and force-click workarounds in chart components—use Playwright’s built-in wait-for/locator.wait_for APIs to wait for elements to be ready and interactable for more reliable tests.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider making the `page` fixture function-scoped (instead of session-scoped) to provide each test with a fresh page and avoid cross-test state leakage.
- Extract the `sync_playwright()` context into its own long-lived session fixture rather than nesting it inside the browser-launch fixture to clearly separate Playwright startup from browser instantiation and ensure proper teardown ordering.
- Avoid hard sleeps and force-click workarounds in chart components—use Playwright’s built-in wait-for/locator.wait_for APIs to wait for elements to be ready and interactable for more reliable tests.
## Individual Comments
### Comment 1
<location> `src/widgetastic_patternfly5/components/date_and_time/calendar_month.py:45-47` </location>
<code_context>
- el = self.browser.element(self.YEAR_INPUT_LOCATOR)
- el.send_keys(Keys.CONTROL + "a")
- el.send_keys(str(value) + Keys.ENTER)
+ self.browser.fill(str(value), self.YEAR_INPUT_LOCATOR)
+ # value attribute not setting at same time we need release that web element.
+ self.root_browser.click(".//body")
</code_context>
<issue_to_address>
**suggestion:** Clicking body after fill may be unreliable.
Using a body click to release input focus may not work consistently. Try using blur or a more reliable method to ensure the input value is set correctly.
```suggestion
self.browser.fill(str(value), self.YEAR_INPUT_LOCATOR)
# Ensure the input loses focus and value is set by triggering blur via JS
el = self.browser.element(self.YEAR_INPUT_LOCATOR)
self.browser.execute_script("arguments[0].blur();", el)
```
</issue_to_address>
### Comment 2
<location> `src/widgetastic_patternfly5/charts/bullet_chart.py:78` </location>
<code_context>
return None
- @property
+ @cached_property
def data(self):
"""Read graph and returns all Data Point objects."""
</code_context>
<issue_to_address>
**question (bug_risk):** Using cached_property for data may cause stale reads.
If the chart's data can change during a session, caching may lead to outdated results. Please verify if caching aligns with the chart's update behavior.
</issue_to_address>
### Comment 3
<location> `src/widgetastic_patternfly5/charts/bullet_chart.py:85` </location>
<code_context>
for el in self.browser.elements(self.ITEMS):
- self.browser.move_to_element(el)
- self.browser.click(el)
+ time.sleep(0.2)
+ # Sometime path elements are not interactable so use force click.
+ self.browser.click(el, force=True)
</code_context>
<issue_to_address>
**suggestion (testing):** Hardcoded sleep may slow down tests unnecessarily.
Consider replacing time.sleep with a wait that checks for element readiness to avoid unnecessary delays.
Suggested implementation:
```python
for el in self.browser.elements(self.ITEMS):
# Wait until the element is interactable before clicking.
self.browser.wait_for_element(el, state='visible', timeout=5)
# Sometime path elements are not interactable so use force click.
self.browser.click(el, force=True)
```
- If your browser object does not have a `wait_for_element` method, you may need to implement it or use an equivalent method (e.g., `wait_for`, `wait_until`, etc.).
- Adjust the `state` and `timeout` parameters as appropriate for your framework.
</issue_to_address>
### Comment 4
<location> `.github/workflows/tests.yaml:107` </location>
<code_context>
uses: codecov/codecov-action@v5
</code_context>
<issue_to_address>
**security (yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha):** An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.
*Source: opengrep*
</issue_to_address>
### Comment 5
<location> `src/widgetastic_patternfly5/charts/legend.py:35-37` </location>
<code_context>
color = self.browser.value_of_css_property(icon, "fill")
if not color:
color = self.browser.value_of_css_property(icon, "color")
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use `or` for providing a fallback value ([`use-or-for-fallback`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/use-or-for-fallback))
```suggestion
color = self.browser.value_of_css_property(icon, "fill") or self.browser.value_of_css_property(icon, "color")
```
<br/><details><summary>Explanation</summary>Thanks to the flexibility of Python's `or` operator, you can use a single
assignment statement, even if a variable can retrieve its value from various
sources. This is shorter and easier to read than using multiple assignments with
`if not` conditions.
</details>
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
f81f557 to
ce22a9d
Compare
ce22a9d to
26c43e8
Compare
Summary by Sourcery
Migrate end-to-end testing from Selenium to Playwright and modernize component interactions throughout the library
New Features:
Enhancements:
Build:
CI:
Documentation:
Tests: