Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Safari runner for selenium #27

Merged
merged 8 commits into from
Sep 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
id: cache-pyodide
with:
path: dist
key: ${{ runner.os }}-pyodide-${{ matrix.pyodide-version }}-${{ hashFiles('.github/**/*.yml') }}
key: pyodide-${{ matrix.pyodide-version }}-${{ hashFiles('.github/**/*.yml') }}

- name: Download Pyodide
shell: bash -l {0}
Expand All @@ -45,12 +45,13 @@ jobs:

test:
needs: download-pyodide
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
env:
DISPLAY: :99
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
pyodide-version: [0.21.0a3, 0.21.0]
test-config: [
{runner: selenium, runtime: firefox, firefox-version: latest, geckodriver-version: latest },
Expand All @@ -63,6 +64,11 @@ jobs:
{runner: playwright, runtime: firefox, playwright-version: 1.22.0, node-version: 18},
{runner: playwright, runtime: chrome, playwright-version: 1.22.0, node-version: 18},
]
include:
- os: macos-latest
pyodide-version: 0.21.0
test-config: {runner: selenium, runtime: safari }

steps:
- uses: actions/checkout@v2

Expand All @@ -87,9 +93,9 @@ jobs:
shell: bash -l {0}
if: ${{ matrix.test-config.runner == 'playwright' }}
run: |
pip install playwright==${{ matrix.test-config.playwright-version }}
python3 -m pip install playwright==${{ matrix.test-config.playwright-version }}
# TODO: install only browsers that are required
python -m playwright install --with-deps
python3 -m playwright install --with-deps

- name: Install firefox
uses: browser-actions/setup-firefox@latest
Expand All @@ -113,27 +119,34 @@ jobs:
if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'chrome' }}
uses: nanasess/setup-chromedriver@v1

- name: Enable Safari Driver
if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'safari' && contains(runner.os, 'macos') }}
run: |
sudo safaridriver --enable
# Only one Safari browser instance can be active at any given time
echo "STANDALONE_REFRESH=1" >> $GITHUB_ENV

- name: Install requirements
shell: bash -l {0}
run: |
pip install -e .
pip install pytest-cov
python3 -m pip install -e .
python3 -m pip install pytest-cov
# Currently we only install the package for dependencies.
# We then uninstall it otherwise tests fails due to pytest hook being
# registered twice.
pip uninstall -y pytest-pyodide
python3 -m pip uninstall -y pytest-pyodide
which npm && npm install node-fetch@2

- name: Get Pyodide from cache
uses: actions/cache@v3
with:
path: dist
key: ${{ runner.os }}-pyodide-${{ matrix.pyodide-version }}-${{ hashFiles('.github/**/*.yml') }}
key: pyodide-${{ matrix.pyodide-version }}-${{ hashFiles('.github/**/*.yml') }}

- name: Run tests
shell: bash -l {0}
run: |
pytest -v \
STANDALONE_REFRESH=${{ env.STANDALONE_REFRESH }} pytest -v \
--cov=pytest_pyodide \
--dist-dir=./dist/ \
--runner=${{ matrix.test-config.runner }} \
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.22.0] - 2022.09.02

- Add `selenium_standalone_refresh` fixture ([#27](https://github.com/pyodide/pytest-pyodide/pull/27))
- Add selenium safari support ([#27](https://github.com/pyodide/pytest-pyodide/pull/27))

## [0.21.1] - 2022.08.10

- Use forked tblib and clean up tracebacks ([#22](https://github.com/pyodide/pytest-pyodide/pull/22))
Expand Down
2 changes: 2 additions & 0 deletions pytest_pyodide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
PlaywrightFirefoxRunner,
SeleniumChromeRunner,
SeleniumFirefoxRunner,
SeleniumSafariRunner,
)
from .server import spawn_web_server
from .utils import parse_driver_timeout, set_webdriver_script_timeout
Expand All @@ -23,6 +24,7 @@
"PlaywrightFirefoxRunner",
"SeleniumChromeRunner",
"SeleniumFirefoxRunner",
"SeleniumSafariRunner",
"set_webdriver_script_timeout",
"parse_driver_timeout",
"run_in_pyodide",
Expand Down
26 changes: 26 additions & 0 deletions pytest_pyodide/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
PlaywrightFirefoxRunner,
SeleniumChromeRunner,
SeleniumFirefoxRunner,
SeleniumSafariRunner,
_BrowserBaseRunner,
)
from .server import spawn_web_server
Expand Down Expand Up @@ -75,6 +76,7 @@ def selenium_common(
browser_set = {
("selenium", "firefox"): SeleniumFirefoxRunner,
("selenium", "chrome"): SeleniumChromeRunner,
("selenium", "safari"): SeleniumSafariRunner,
("selenium", "node"): NodeRunner,
("playwright", "firefox"): PlaywrightFirefoxRunner,
("playwright", "chrome"): PlaywrightChromeRunner,
Expand Down Expand Up @@ -115,6 +117,30 @@ def selenium_standalone(request, runtime, web_server_main, playwright_browsers):
print(selenium.logs)


@pytest.fixture(scope="function")
def selenium_standalone_refresh(selenium):
"""
Experimental standalone fixture which refreshes a page instead of
instantiating a new webdriver session.

by setting STANDALONE_REFRESH env variable,
selenium_standalone_refresh fixture will override selenium_standalone
"""
selenium.clean_logs()

yield selenium

selenium.refresh()
selenium.load_pyodide()
selenium.initialize_pyodide()
selenium.save_state()
selenium.restore_state()


if os.environ.get("STANDALONE_REFRESH"):
selenium_standalone = selenium_standalone_refresh # type: ignore[assignment] # noqa: F811


@pytest.fixture(scope="module")
def selenium_esm(request, runtime, web_server_main, playwright_browsers):
with selenium_common(
Expand Down
2 changes: 1 addition & 1 deletion pytest_pyodide/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from .utils import parse_xfail_browsers

RUNTIMES = ["firefox", "chrome", "node"]
RUNTIMES = ["firefox", "chrome", "safari", "node"]


def pytest_configure(config):
Expand Down
23 changes: 18 additions & 5 deletions pytest_pyodide/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def __str__(self):

class _BrowserBaseRunner:
browser = ""
script_timeout = 20
JavascriptException = JavascriptException

def __init__(
Expand All @@ -108,7 +109,6 @@ def __init__(
server_hostname="127.0.0.1",
server_log=None,
load_pyodide=True,
script_timeout=20,
script_type="classic",
dist_dir=None,
*args,
Expand All @@ -121,8 +121,7 @@ def __init__(
self.script_type = script_type
self.dist_dir = dist_dir
self.driver = self.get_driver()
self.set_script_timeout(script_timeout)
self.script_timeout = script_timeout
self.set_script_timeout(self.script_timeout)
self.prepare_driver()
self.javascript_setup()
if load_pyodide:
Expand Down Expand Up @@ -321,6 +320,7 @@ def goto(self, page):

def set_script_timeout(self, timeout):
self.driver.set_script_timeout(timeout)
self.script_timeout = timeout

def quit(self):
self.driver.quit()
Expand Down Expand Up @@ -433,6 +433,19 @@ def collect_garbage(self):
self.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})


class SeleniumSafariRunner(_SeleniumBaseRunner):

browser = "safari"
script_timeout = 30

def get_driver(self):
from selenium.webdriver import Safari
from selenium.webdriver.safari.options import Options

options = Options()
return Safari(options=options)


class PlaywrightChromeRunner(_PlaywrightBaseRunner):
browser = "chrome"

Expand Down Expand Up @@ -487,7 +500,7 @@ def prepare_driver(self):
pass

def set_script_timeout(self, timeout):
self._timeout = timeout
self.script_timeout = timeout

def quit(self):
self.p.sendeof()
Expand Down Expand Up @@ -523,7 +536,7 @@ def run_js_inner(self, code, check_code):
self.p.sendline(cmd_id)
self.p.sendline(wrapped)
self.p.sendline(cmd_id)
self.p.expect_exact(f"{cmd_id}:UUID\r\n", timeout=self._timeout)
self.p.expect_exact(f"{cmd_id}:UUID\r\n", timeout=self.script_timeout)
self.p.expect_exact(f"{cmd_id}:UUID\r\n")
if self.p.before:
self._logs.append(self.p.before.decode()[:-2].replace("\r", ""))
Expand Down
3 changes: 1 addition & 2 deletions pytest_pyodide/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@

@contextlib.contextmanager
def spawn_web_server(dist_dir):

tmp_dir = tempfile.mkdtemp()
log_path = pathlib.Path(tmp_dir) / "http-server.log"
q: multiprocessing.Queue[str] = multiprocessing.Queue()
p = multiprocessing.Process(target=run_web_server, args=(q, log_path, dist_dir))

try:
p.start()
port = q.get()
port = q.get(timeout=20)
hostname = "127.0.0.1"

print(
Expand Down
3 changes: 3 additions & 0 deletions tests/test_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,16 @@ def test_local_fail_load_package(selenium_standalone):
def _load_package_error(*args, **kwargs):
raise OSError("STOP!")

_load_package_original = selenium.load_package
selenium.load_package = _load_package_error

exc = None
try:
example_func(selenium)
except OSError:
exc = pytest.ExceptionInfo.from_current()
finally:
selenium.load_package = _load_package_original

assert exc
try:
Expand Down
5 changes: 4 additions & 1 deletion tests/test_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@


@pytest.mark.xfail_browsers(
node="Should xfail", firefox="Should xfail", chrome="Should xfail"
node="Should xfail",
firefox="Should xfail",
chrome="Should xfail",
safari="Should xfail",
)
@run_in_pyodide
def test_xfail_browser(selenium):
Expand Down