Skip to content

Commit f50c0c5

Browse files
authored
Merge pull request #1410 from manics/playwright
Add basic UI Playwright tests
2 parents b0cfd45 + 704b91c commit f50c0c5

File tree

9 files changed

+101
-3
lines changed

9 files changed

+101
-3
lines changed

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ jobs:
7171
- ubuntu_version: "20.04"
7272
python_version: "3.6"
7373
repo_type: venv
74+
# Playwright test
75+
- ubuntu_version: "24.04"
76+
python_version: "3.13"
77+
repo_type: ui
7478

7579
steps:
7680
- uses: actions/checkout@v4
@@ -83,6 +87,12 @@ jobs:
8387
pip install -r dev-requirements.txt
8488
pip freeze
8589
90+
- name: Install UI test dependencies
91+
if: matrix.repo_type == 'ui'
92+
run: |
93+
pip install -r playwright-requirements.txt
94+
playwright install firefox
95+
8696
- name: Install repo2docker
8797
run: |
8898
python -m build --wheel .

playwright-requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-r dev-requirements.txt
2+
pytest-playwright

tests/conftest.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def pytest_collect_file(parent, file_path):
3939
return RemoteRepoList.from_parent(parent, path=file_path)
4040

4141

42-
def make_test_func(args, skip_build=False, extra_run_kwargs=None):
42+
def make_test_func(args, skip_build=False, extra_run_kwargs=None, external_script=None):
4343
"""Generate a test function that runs repo2docker"""
4444

4545
def test():
@@ -82,6 +82,10 @@ def build_noop():
8282
success = True
8383
break
8484
assert success, f"Notebook never started in {container}"
85+
86+
if external_script:
87+
subprocess.check_call([external_script, f"http://localhost:{port}"])
88+
8589
finally:
8690
# stop the container
8791
container.stop()
@@ -202,12 +206,21 @@ class Repo2DockerTest(pytest.Function):
202206
"""A pytest.Item for running repo2docker"""
203207

204208
def __init__(
205-
self, name, parent, args=None, skip_build=False, extra_run_kwargs=None
209+
self,
210+
name,
211+
parent,
212+
args=None,
213+
skip_build=False,
214+
extra_run_kwargs=None,
215+
external_script=None,
206216
):
207217
self.args = args
208218
self.save_cwd = os.getcwd()
209219
f = parent.obj = make_test_func(
210-
args, skip_build=skip_build, extra_run_kwargs=extra_run_kwargs
220+
args,
221+
skip_build=skip_build,
222+
extra_run_kwargs=extra_run_kwargs,
223+
external_script=external_script,
211224
)
212225
super().__init__(name, parent, callobj=f)
213226

@@ -246,6 +259,17 @@ def collect(self):
246259
args.append(str(self.path.parent))
247260
yield Repo2DockerTest.from_parent(self, name="build", args=args)
248261

262+
# If external-verify exists it should be run on the host
263+
external_verify_script = self.path.parent / "external-verify"
264+
if external_verify_script.exists():
265+
yield Repo2DockerTest.from_parent(
266+
self,
267+
name=external_verify_script.name,
268+
args=args,
269+
skip_build=True,
270+
external_script=external_verify_script,
271+
)
272+
249273
yield Repo2DockerTest.from_parent(
250274
self,
251275
name=self.path.name,

tests/ui/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# User interface tests
2+
3+
This contains very basic [Playwright](https://playwright.dev/python/) tests to check the
4+
5+
- JupyterLab
6+
- RStudio
7+
- RShiny
8+
9+
interfaces can be accessed.

tests/ui/browser/environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dependencies:
2+
- r-base

tests/ui/browser/external-verify

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
# This script is run outside the container
3+
4+
set -eux
5+
6+
export TEST_REPO2DOCKER_URL="${1}/?token=token"
7+
pytest --verbose --color=yes --browser=firefox tests/ui/browser/external-verify.py

tests/ui/browser/external-verify.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
from subprocess import check_output
3+
from urllib.parse import urlsplit
4+
5+
import pytest
6+
from playwright.sync_api import Page, expect
7+
8+
9+
# To run this test manually:
10+
# - Run: repo2docker tests/ui/browser/
11+
# - Run: TEST_REPO2DOCKER_URL=<connection-url> python -mpytest --browser=firefox tests/ui/browser/external-verify.py [--headed]
12+
def test_user_interfaces(page: Page) -> None:
13+
url = os.getenv("TEST_REPO2DOCKER_URL")
14+
u = urlsplit(url)
15+
16+
# Includes token
17+
page.goto(url)
18+
19+
# Initial page should be Jupyter Notebook
20+
page.wait_for_url(f"{u.scheme}://{u.netloc}/tree")
21+
22+
# Check JupyterLab
23+
page.goto(f"{u.scheme}://{u.netloc}/lab")
24+
expect(page.get_by_text("Python 3 (ipykernel)").nth(1)).to_be_visible()
25+
26+
# Check JupyterLab RStudio launcher
27+
with page.expect_popup() as page1_info:
28+
page.get_by_text("RStudio [↗]").click()
29+
page1 = page1_info.value
30+
page1.wait_for_url(f"{u.scheme}://{u.netloc}/rstudio/")
31+
# Top-left logo
32+
expect(page1.locator("#rstudio_rstudio_logo")).to_be_visible()
33+
# Initial RStudio console text
34+
expect(page1.get_by_text("R version ")).to_be_visible()
35+
36+
# Check JupyterLab RShiny launcher
37+
with page.expect_popup() as page2_info:
38+
page.get_by_text("Shiny [↗]").click()
39+
page2 = page2_info.value
40+
page2.wait_for_url(f"{u.scheme}://{u.netloc}/shiny/")
41+
expect(page2.get_by_text("Index of /")).to_be_visible()

tests/ui/browser/test-extra-args.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- --env
2+
- JUPYTER_TOKEN=token

tests/ui/browser/verify

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#!/bin/sh

0 commit comments

Comments
 (0)