From ec77d7b434dbae81f12c2ffc6f975d7fa00f44a2 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 05:49:53 +0000 Subject: [PATCH 1/8] Add selenium safari runner --- pytest_pyodide/__init__.py | 2 ++ pytest_pyodide/fixture.py | 2 ++ pytest_pyodide/runner.py | 24 +++++++++++++++++++----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/pytest_pyodide/__init__.py b/pytest_pyodide/__init__.py index fc93ba8d..fe1d4fd8 100644 --- a/pytest_pyodide/__init__.py +++ b/pytest_pyodide/__init__.py @@ -7,6 +7,7 @@ PlaywrightFirefoxRunner, SeleniumChromeRunner, SeleniumFirefoxRunner, + SeleniumSafariRunner, ) from .server import spawn_web_server from .utils import parse_driver_timeout, set_webdriver_script_timeout @@ -23,6 +24,7 @@ "PlaywrightFirefoxRunner", "SeleniumChromeRunner", "SeleniumFirefoxRunner", + "SeleniumSafariRunner", "set_webdriver_script_timeout", "parse_driver_timeout", "run_in_pyodide", diff --git a/pytest_pyodide/fixture.py b/pytest_pyodide/fixture.py index 4638ce50..0e32753d 100644 --- a/pytest_pyodide/fixture.py +++ b/pytest_pyodide/fixture.py @@ -10,6 +10,7 @@ PlaywrightFirefoxRunner, SeleniumChromeRunner, SeleniumFirefoxRunner, + SeleniumSafariRunner, _BrowserBaseRunner, ) from .server import spawn_web_server @@ -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, diff --git a/pytest_pyodide/runner.py b/pytest_pyodide/runner.py index dd460216..ced424dd 100644 --- a/pytest_pyodide/runner.py +++ b/pytest_pyodide/runner.py @@ -100,6 +100,7 @@ def __str__(self): class _BrowserBaseRunner: browser = "" + script_timeout = 20 JavascriptException = JavascriptException def __init__( @@ -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, @@ -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: @@ -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() @@ -433,6 +433,20 @@ 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" @@ -487,7 +501,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() @@ -523,7 +537,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", "")) From 1d0c88833bff094bfc194e9c867c6e213c111aa9 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 06:01:46 +0000 Subject: [PATCH 2/8] Update ci --- .github/workflows/main.yml | 26 ++++++++++++++++++++------ pytest_pyodide/hook.py | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 24484eb9..83829c5c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 }, @@ -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 @@ -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 @@ -113,15 +119,23 @@ jobs: if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'chrome' }} uses: nanasess/setup-chromedriver@v1 + # Adapted from: https://github.com/Choices-js/Choices/blob/master/.github/workflows/browsers.yml + - name: Enable Safari Driver + if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'safari' && contains(runner.os, 'macos') }} + run: | + mkdir -p ~/Library/WebDriver/ + curl https://raw.githubusercontent.com/web-platform-tests/wpt/master/tools/ci/azure/com.apple.Safari.plist -o ~/Library/WebDriver/com.apple.Safari.plist + defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically 1 + - 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 diff --git a/pytest_pyodide/hook.py b/pytest_pyodide/hook.py index f718e332..58843333 100644 --- a/pytest_pyodide/hook.py +++ b/pytest_pyodide/hook.py @@ -12,7 +12,7 @@ from .utils import parse_xfail_browsers -RUNTIMES = ["firefox", "chrome", "node"] +RUNTIMES = ["firefox", "chrome", "safari", "node"] def pytest_configure(config): From f6967ab8ed1881a16446c2a93bde65b45a825c54 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 06:47:07 +0000 Subject: [PATCH 3/8] Fix safari permission --- .github/workflows/main.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 83829c5c..f515f182 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -119,13 +119,11 @@ jobs: if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'chrome' }} uses: nanasess/setup-chromedriver@v1 - # Adapted from: https://github.com/Choices-js/Choices/blob/master/.github/workflows/browsers.yml + # Adapted from: https://circleci.com/developer/orbs/orb/circleci/macos - name: Enable Safari Driver if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'safari' && contains(runner.os, 'macos') }} run: | - mkdir -p ~/Library/WebDriver/ - curl https://raw.githubusercontent.com/web-platform-tests/wpt/master/tools/ci/azure/com.apple.Safari.plist -o ~/Library/WebDriver/com.apple.Safari.plist - defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically 1 + sudo safaridriver --enable - name: Install requirements shell: bash -l {0} From 647106bc33613ef8544ec48b4b291a99366bcb35 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 07:47:49 +0000 Subject: [PATCH 4/8] Fix ci --- .github/workflows/main.yml | 5 ++--- pytest_pyodide/runner.py | 1 - pytest_pyodide/server.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f515f182..4cacb21a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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} @@ -119,7 +119,6 @@ jobs: if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'chrome' }} uses: nanasess/setup-chromedriver@v1 - # Adapted from: https://circleci.com/developer/orbs/orb/circleci/macos - name: Enable Safari Driver if: ${{ matrix.test-config.runner == 'selenium' && matrix.test-config.runtime == 'safari' && contains(runner.os, 'macos') }} run: | @@ -140,7 +139,7 @@ jobs: 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} diff --git a/pytest_pyodide/runner.py b/pytest_pyodide/runner.py index ced424dd..48912d05 100644 --- a/pytest_pyodide/runner.py +++ b/pytest_pyodide/runner.py @@ -443,7 +443,6 @@ def get_driver(self): from selenium.webdriver.safari.options import Options options = Options() - return Safari(options=options) diff --git a/pytest_pyodide/server.py b/pytest_pyodide/server.py index d34cf839..491449a5 100644 --- a/pytest_pyodide/server.py +++ b/pytest_pyodide/server.py @@ -12,7 +12,6 @@ @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() @@ -20,7 +19,7 @@ def spawn_web_server(dist_dir): try: p.start() - port = q.get() + port = q.get(timeout=20) hostname = "127.0.0.1" print( From 9563a560a288675e57a5f52104efd5f0ab6c2923 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 10:12:12 +0000 Subject: [PATCH 5/8] Add selenium_standalone_refresh fixture --- .github/workflows/main.yml | 4 +++- pytest_pyodide/fixture.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4cacb21a..0d616831 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,6 +123,8 @@ jobs: 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} @@ -144,7 +146,7 @@ jobs: - 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 }} \ diff --git a/pytest_pyodide/fixture.py b/pytest_pyodide/fixture.py index 0e32753d..a039b535 100644 --- a/pytest_pyodide/fixture.py +++ b/pytest_pyodide/fixture.py @@ -117,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( From 30022ea46422fccd6797b88aac26610af6d700c3 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Mon, 22 Aug 2022 10:33:03 +0000 Subject: [PATCH 6/8] Fix tests --- tests/test_decorator.py | 3 +++ tests/test_marker.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_decorator.py b/tests/test_decorator.py index e4ff345d..4b89b184 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -112,6 +112,7 @@ 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 @@ -119,6 +120,8 @@ def _load_package_error(*args, **kwargs): example_func(selenium) except OSError: exc = pytest.ExceptionInfo.from_current() + finally: + selenium.load_package = _load_package_original assert exc try: diff --git a/tests/test_marker.py b/tests/test_marker.py index 81544d8f..017f38d6 100644 --- a/tests/test_marker.py +++ b/tests/test_marker.py @@ -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): From 81699f10f52e7e727914a8084214bb2949cb11bd Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 23 Aug 2022 00:17:42 +0000 Subject: [PATCH 7/8] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2176fe16..9ef673e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [0.21.2] - Unreleased + +- 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)) From 7aad55abb9d06fe9c8e89222d23a87d4ab338b37 Mon Sep 17 00:00:00 2001 From: Roman Yurchak Date: Fri, 2 Sep 2022 11:37:38 +0200 Subject: [PATCH 8/8] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ef673e3..a71b27bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## [0.21.2] - Unreleased +## [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))