Skip to content

[Feature Branch] Playwright Migration by Nikhil#103

Merged
mshriver merged 18 commits into
mainfrom
feature-playwright
Oct 22, 2025
Merged

[Feature Branch] Playwright Migration by Nikhil#103
mshriver merged 18 commits into
mainfrom
feature-playwright

Conversation

@mshriver

@mshriver mshriver commented Oct 16, 2025

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Migrate end-to-end testing from Selenium to Playwright and modernize component interactions throughout the library

New Features:

  • Add Playwright-based fixtures and CLI options for browser, headless mode, and slow motion
  • Introduce pytest_report_header to display browser configuration in test reports

Enhancements:

  • Replace Selenium-based context and page fixtures with Playwright browser, context, and page fixtures
  • Update component implementations to use Playwright APIs (force clicks, is_visible, text, is_checked, etc.)
  • Refactor test fixtures and assertions to leverage unified "view" fixtures and Playwright methods
  • Bump widgetastic.core dependency to version 2 and add pytest-timeout to dev requirements
  • Upgrade ruff pre-commit hook to v0.14.1

Build:

  • Update pyproject.toml dependencies to require widgetastic.core>=2.0.0a2

CI:

  • Revamp GitHub Actions workflow to install and cache Playwright browsers, enable concurrency cancellation, and split coverage and non-coverage runs
  • Switch CI matrix to Chromium and Firefox with Playwright CLI flags
  • Upgrade Codecov action to v5

Documentation:

  • Overhaul README to document Playwright setup, installation steps, supported components, and new pytest options

Tests:

  • Refactor tests to use Playwright fixtures, update locators and assertions (use text_content, is_visible, etc.), and simplify fixtures for select and modal tests

Copilot AI review requested due to automatic review settings October 16, 2025 09:41
@mshriver mshriver marked this pull request as draft October 16, 2025 09:42
@mshriver mshriver added the enhancement New feature or request label Oct 16, 2025

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread testing/conftest.py Outdated
Comment thread src/widgetastic_patternfly5/charts/line_chart.py
Comment thread src/widgetastic_patternfly5/charts/bullet_chart.py
Comment thread pyproject.toml Outdated
Comment thread .github/workflows/tests.yaml Outdated
Comment thread .github/workflows/tests.yaml Outdated

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New security issues found

Comment thread .github/workflows/tests.yaml
@digitronik digitronik marked this pull request as ready for review October 22, 2025 10:58
@codecov

codecov Bot commented Oct 22, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (982f644) to head (26c43e8).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
src/widgetastic_patternfly5/components/switch.py 0.00% 16 Missing ⚠️
src/widgetastic_patternfly5/charts/bullet_chart.py 0.00% 7 Missing ⚠️
...astic_patternfly5/components/dual_list_selector.py 0.00% 6 Missing ⚠️
...tastic_patternfly5/charts/alerts_timeline_chart.py 0.00% 4 Missing ⚠️
src/widgetastic_patternfly5/components/slider.py 0.00% 4 Missing ⚠️
...dgetastic_patternfly5/components/menus/dropdown.py 0.00% 3 Missing ⚠️
src/widgetastic_patternfly5/charts/line_chart.py 0.00% 2 Missing ⚠️
...tastic_patternfly5/components/forms/form_select.py 0.00% 2 Missing ⚠️
...c/widgetastic_patternfly5/components/pagination.py 0.00% 2 Missing ⚠️
src/widgetastic_patternfly5/components/table.py 0.00% 2 Missing ⚠️
... and 4 more

❗ There is a different number of reports uploaded between BASE (982f644) and HEAD (26c43e8). Click for more details.

HEAD has 14 uploads less than BASE
Flag BASE (982f644) HEAD (26c43e8)
unittests 15 1
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     
Flag Coverage Δ
unittests 0.00% <0.00%> (-99.85%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sourcery-ai

sourcery-ai Bot commented Oct 22, 2025

Copy link
Copy Markdown

Reviewer's Guide

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

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

Sequence diagram for component interaction using Playwright APIs

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

Class diagram for updated browser and component interaction

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

File-Level Changes

Change Details Files
Migrate test fixtures from Selenium to Playwright
  • Replaced Selenium imports and options with Playwright sync API
  • Added --browser, --headless, and --slowmo pytest options
  • Implemented playwright_browser_instance, browser_context, and page fixtures
  • Added pytest_report_header to surface test configuration
testing/conftest.py
Revise documentation to reflect Playwright usage
  • Added overview and installation steps for Playwright
  • Updated test running and CLI options sections
  • Included table of pytest flags and examples
README.md
Update GitHub Actions to install and cache Playwright browsers
  • Introduced setup-browsers job with caching of downloaded browsers
  • Adjusted test job to depend on setup and run Playwright-based tests
  • Refactored matrix to include run-coverage flag and reruns
.github/workflows/tests.yaml
Adapt component tests to new fixtures and Playwright APIs
  • Refactored module fixtures to use shared view and page fixtures
  • Replaced is_displayed/text with is_visible and text_content
  • Updated XPath anchors and URL joins in chart and select tests
testing/components/menus/test_select.py
testing/components/test_dual_list_selector.py
testing/components/test_group_dropdown.py
testing/components/test_modal.py
testing/charts/test_pie_chart.py
testing/charts/test_bullet_chart.py
Refactor component library methods for Playwright compatibility
  • Swapped WebDriver calls (send_keys, is_selected) for Playwright element API (fill, press, is_checked)
  • Added force clicks, viewport context, and cached properties where needed
  • Unified text retrieval via browser.text and attributes methods
src/widgetastic_patternfly5/components/switch.py
src/widgetastic_patternfly5/components/calendar_month.py
src/widgetastic_patternfly5/components/dual_list_selector.py
src/widgetastic_patternfly5/components/slider.py
src/widgetastic_patternfly5/components/forms/form_select.py
src/widgetastic_patternfly5/components/menus/dropdown.py
src/widgetastic_patternfly5/components/menus/menu.py
src/widgetastic_patternfly5/components/menus/select.py
src/widgetastic_patternfly5/components/navigation.py
src/widgetastic_patternfly5/components/clipboard_copy.py
src/widgetastic_patternfly5/components/pagination.py
src/widgetastic_patternfly5/components/title.py
src/widgetastic_patternfly5/charts/bullet_chart.py
src/widgetastic_patternfly5/charts/alerts_timeline_chart.py
src/widgetastic_patternfly5/charts/line_chart.py
Bump dependencies and code quality tooling
  • Upgraded widgetastic.core to 2.0.0a2 and added pytest-timeout
  • Updated ruff pre-commit hook version
  • Adjusted dev dependencies in pyproject.toml
pyproject.toml
.pre-commit-config.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/widgetastic_patternfly5/components/date_and_time/calendar_month.py Outdated
Comment thread src/widgetastic_patternfly5/charts/bullet_chart.py
Comment thread src/widgetastic_patternfly5/charts/bullet_chart.py
Comment thread .github/workflows/tests.yaml
Comment thread src/widgetastic_patternfly5/charts/legend.py Outdated
@digitronik digitronik self-assigned this Oct 22, 2025
@mshriver mshriver merged commit c072d89 into main Oct 22, 2025
10 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request playwright

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants