-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #119 from manics/raw_socket_proxy
Remove websockify, add Playwright test
- Loading branch information
Showing
11 changed files
with
140 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,3 +134,6 @@ dmypy.json | |
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
# Additional ignores | ||
screenshots/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pillow==10.3.0 | ||
playwright==1.44.0 | ||
pytest==8.2.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,6 @@ | ||
# Unfortunately the version of websockify on PyPI doesn't include the [compiled | ||
# wrapper library](https://github.com/novnc/websockify#wrap-a-program) which is | ||
# used by this extension, so either you'd have to manually compile it after pip | ||
# installing websockify, or use the conda package. | ||
# | ||
channels: | ||
- conda-forge | ||
dependencies: | ||
- jupyter-server-proxy>=1.4 | ||
- jupyter-server-proxy>=4.3.0 | ||
- jupyterhub-singleuser | ||
- pip | ||
- websockify |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from os import getenv | ||
|
||
import pytest | ||
from playwright.sync_api import sync_playwright | ||
|
||
HEADLESS = getenv("HEADLESS", "1") == "1" | ||
|
||
|
||
@pytest.fixture() | ||
def browser(): | ||
# browser_type in ["chromium", "firefox", "webkit"] | ||
with sync_playwright() as playwright: | ||
browser = playwright.firefox.launch(headless=HEADLESS) | ||
context = browser.new_context() | ||
page = context.new_page() | ||
yield page | ||
context.clear_cookies() | ||
browser.close() |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from os import getenv | ||
from pathlib import Path | ||
from shutil import which | ||
from subprocess import check_output | ||
from uuid import uuid4 | ||
|
||
from PIL import Image, ImageChops | ||
from playwright.sync_api import expect | ||
|
||
HERE = Path(__file__).absolute().parent | ||
|
||
CONTAINER_ID = getenv("CONTAINER_ID", "test") | ||
JUPYTER_HOST = getenv("JUPYTER_HOST", "http://localhost:8888") | ||
JUPYTER_TOKEN = getenv("JUPYTER_TOKEN", "secret") | ||
|
||
|
||
def compare_screenshot(test_image, threshold=2): | ||
# Compare images by calculating the mean absolute difference | ||
# Images must be the same size | ||
# threshold: Average difference per pixel, this depends on the image type | ||
# e.g. for 24 bit images (8 bit RGB pixels) threshold=1 means a maximum | ||
# difference of 1 bit per pixel per channel | ||
reference = Image.open(HERE / "reference" / "desktop.png") | ||
test = Image.open(test_image) | ||
|
||
# Absolute difference | ||
# Convert to RGB, alpha channel breaks ImageChops | ||
diff = ImageChops.difference(reference.convert("RGB"), test.convert("RGB")) | ||
diff_data = diff.getdata() | ||
|
||
m = sum(sum(px) for px in diff_data) / diff_data.size[0] / diff_data.size[1] | ||
assert m < threshold | ||
|
||
|
||
# To debug this set environment variable HEADLESS=0 | ||
def test_desktop(browser): | ||
page = browser | ||
page.goto(f"{JUPYTER_HOST}/lab?token={JUPYTER_TOKEN}") | ||
page.wait_for_url(f"{JUPYTER_HOST}/lab") | ||
|
||
# JupyterLab extension icon | ||
expect(page.get_by_text("Desktop [↗]")).to_be_visible() | ||
with page.expect_popup() as page1_info: | ||
page.get_by_text("Desktop [↗]").click() | ||
page1 = page1_info.value | ||
page1.wait_for_url(f"{JUPYTER_HOST}/desktop/") | ||
|
||
expect(page1.get_by_text("Status: Connected")).to_be_visible() | ||
expect(page1.locator("canvas")).to_be_visible() | ||
|
||
# Screenshot the desktop element only | ||
# May take a few seconds to load | ||
page1.wait_for_timeout(5000) | ||
# Use a non temporary folder so we can check it manually if necessary | ||
screenshot = Path("screenshots") / "desktop.png" | ||
page1.locator("canvas").screenshot(path=screenshot) | ||
|
||
# Open clipboard, enter random text, close clipboard | ||
clipboard_text = str(uuid4()) | ||
page1.get_by_role("link", name="Remote Clipboard").click() | ||
page1.wait_for_selector("#clipboard-text") | ||
page1.locator("#clipboard-text").click() | ||
page1.locator("#clipboard-text").fill(clipboard_text) | ||
page1.get_by_role("link", name="Remote Clipboard").click() | ||
|
||
# Exec into container to check clipboard contents | ||
for engine in ["docker", "podman"]: | ||
if which(engine): | ||
break | ||
else: | ||
raise RuntimeError("Container engine not found") | ||
clipboard = check_output( | ||
[engine, "exec", "-eDISPLAY=:1", CONTAINER_ID, "xclip", "-o"] | ||
) | ||
assert clipboard.decode() == clipboard_text | ||
|
||
compare_screenshot(screenshot) |