Skip to content

Commit

Permalink
Add workflow that only runs playwright tests of changed files (stream…
Browse files Browse the repository at this point in the history
…lit#8739)

## Describe your changes

This workflow only runs playwright tests that have been changed within a
pull request. This makes it a lot faster to get the relevant test
results and screenshots. This will also create trace files for all
failed tests that contain a lot more detailed information about the
failed test.

To test this workflow, I added a couple of reasonable changes to e2e
tests.

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
  • Loading branch information
lukasmasuch authored May 23, 2024
1 parent ad3dc4e commit 2164f52
Show file tree
Hide file tree
Showing 36 changed files with 130 additions and 22 deletions.
86 changes: 86 additions & 0 deletions .github/workflows/playwright-changed-files.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Playwright E2E Tests - Changed Files

on:
pull_request:
types: [opened, synchronize, reopened]

# Avoid duplicate workflows on same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-playwright-changed-files
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest-8-cores

defaults:
run:
shell: bash --login -eo pipefail {0}

steps:
- name: Checkout Streamlit code
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
persist-credentials: false
submodules: "recursive"
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v44
with:
path: e2e_playwright
files: "**/*_test.py"
- name: Check changed files
id: check_changed_files
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_and_modified_files }}
CHANGED_FILES_COUNT: ${{ steps.changed-files.outputs.all_changed_and_modified_files_count }}
run: |
echo "Changed files count: ${CHANGED_FILES_COUNT}"
echo "$CHANGED_FILES"
if [[ "${CHANGED_FILES_COUNT}" -gt 5 || "${CHANGED_FILES_COUNT}" -lt 1 ]]; then
# We limit the workflow to a max of 5 changed files, since otherwise it would
# take too long and would not provide any benefit compared to the main playwright
# workflow.
echo "This workflow only supports between 1-5 changed files. Otherwise its skipping running the tests.";
echo "run_tests=false" >> $GITHUB_OUTPUT
else
echo "run_tests=true" >> $GITHUB_OUTPUT
fi
- name: Use output
run: |
echo "The output value is: ${{ steps.check_changed_files.outputs.run_tests }}"
- name: Set Python version vars
uses: ./.github/actions/build_info
- name: Set up Python ${{ env.PYTHON_MAX_VERSION }}
uses: actions/setup-python@v5
with:
python-version: "${{ env.PYTHON_MAX_VERSION }}"
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Setup virtual env
uses: ./.github/actions/make_init
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Run make develop
run: make develop
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Install playwright
run: python -m playwright install --with-deps
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Run make protobuf
run: make protobuf
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Run make frontend-fast
run: make frontend-fast
- if: steps.check_changed_files.outputs.run_tests == 'true'
name: Run changed playwright tests
run: |
cd e2e_playwright;
rm -rf ./test-results;
pytest ${{ steps.changed-files.outputs.all_changed_and_modified_files }} --browser webkit --browser chromium --browser firefox --video retain-on-failure --screenshot only-on-failure --full-page-screenshot --tracing retain-on-failure --output ./test-results/ -n auto --durations=5 -r aR -v
- name: Upload failed test results
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright_test_results
path: e2e_playwright/test-results
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
4 changes: 0 additions & 4 deletions e2e_playwright/main_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
# limitations under the License.

import streamlit as st

# Not possible to test the urls in the menu as they are hidden behind
# the click handler of the button
# https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/testing-dom__tab-handling-links/cypress/integration/tab_handling_anchor_links_spec.js
from streamlit.commands.page_config import MenuItems

menu_items: MenuItems = {"about": "_*This can be markdown!*_"}
Expand Down
32 changes: 27 additions & 5 deletions e2e_playwright/st_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,30 @@

import streamlit as st

data = {"foo": "bar"}
st.json(data)
st.json(data, expanded=False)
data = {"Hello World": "Foo Bar"}
st.json(data)
st.subheader("Simple dict:")
st.json({"foo": "bar"})

st.subheader("Collapsed")
st.json({"foo": "bar"}, expanded=False)

st.subheader("Keep whitespaces:")
st.json({"Hello World": "Foo Bar"})

st.subheader("Complex dict:")
st.json(
{
"array": [1, 2],
"boolean": True,
"null": None,
"integer": 123,
"float": 123.45,
"object": {"a": "b", "c": "d"},
"string": "Hello World",
}
)

st.subheader("Simple List:")
st.json(["a", "b"])

st.subheader("Empty dict:")
st.json({})
26 changes: 15 additions & 11 deletions e2e_playwright/st_json_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@
from e2e_playwright.conftest import ImageCompareFunction


def test_st_exception_displays_correctly(
themed_app: Page, assert_snapshot: ImageCompareFunction
):
json_elements = themed_app.get_by_test_id("stJson")
def test_st_json_displays_correctly(app: Page, assert_snapshot: ImageCompareFunction):
"""Test st.json renders the data correctly."""
json_elements = app.get_by_test_id("stJson")
expect(json_elements).to_have_count(6)

expect(json_elements.nth(0)).to_contain_text("foo")
expect(json_elements.nth(0)).to_contain_text("bar")

expect(json_elements.nth(1)).to_contain_text("...")
assert_snapshot(json_elements.nth(0), name="st_json-simple_dict")
assert_snapshot(json_elements.nth(1), name="st_json-collapsed")
assert_snapshot(json_elements.nth(2), name="st_json-with_white_spaces")
# The complex dict is screenshot tested in the themed test below
assert_snapshot(json_elements.nth(4), name="st_json-simple_list")
assert_snapshot(json_elements.nth(5), name="st_json-empty_dict")

expect(json_elements.nth(2)).to_contain_text("Hello World")
expect(json_elements.nth(2)).to_contain_text("Foo Bar")

assert_snapshot(json_elements.nth(2), name="st_json-with_white_spaces")
def test_st_json_displays_correctly_when_themed(
themed_app: Page, assert_snapshot: ImageCompareFunction
):
"""Test st.json uses renders the data correctly with different themes."""
json_elements = themed_app.get_by_test_id("stJson")
assert_snapshot(json_elements.nth(3), name="st_json-complex_dict")
2 changes: 1 addition & 1 deletion e2e_playwright/st_select_slider_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def test_select_slider_contains_correct_format_func_value_and_in_session_state(

# Move mouse to 500, 0 pixels on the screen to simulate dragging left
app.mouse.move(500, 0)
wait_for_app_run(app)
app.mouse.up()
wait_for_app_run(app)

expect(app.get_by_text("Value 1: ('orange', 'yellow')")).to_have_count(2)

Expand Down
2 changes: 1 addition & 1 deletion e2e_playwright/st_snow_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
from playwright.sync_api import Page, expect


def test_balloons_are_present_on_page(app: Page):
def test_snow_is_present_on_page(app: Page):
expect(app.get_by_test_id("snow")).to_have_count(1)

0 comments on commit 2164f52

Please sign in to comment.