diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7af3a13a9..476854a42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,6 +103,55 @@ jobs: if: matrix.os == 'ubuntu-latest' run: xvfb-run pytest -vv tests/async --browser=${{ matrix.browser }} --timeout 90 + stable: + name: Stable + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + browser-channel: [chrome] + include: + - os: windows-latest + browser-channel: msedge + - os: macos-latest + browser-channel: msedge + runs-on: ${{ matrix.os }} + steps: + - name: Install Media Pack + if: matrix.os == 'windows-latest' + shell: powershell + run: Install-WindowsFeature Server-Media-Foundation + - uses: actions/checkout@v2 + - uses: microsoft/playwright-github-action@v1 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install -r local-requirements.txt + pip install -e . + - name: Build package + run: python setup.py bdist_wheel + - name: Install ffmpeg + run: python -m playwright install ffmpeg + - name: Common Tests + run: pytest -vv tests/common --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90 + - name: Test Sync API + if: matrix.os != 'ubuntu-latest' + run: pytest -vv tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90 + - name: Test Sync API + if: matrix.os == 'ubuntu-latest' + run: xvfb-run pytest -vv tests/sync --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90 + - name: Test Async API + if: matrix.os != 'ubuntu-latest' + run: pytest -vv tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90 + - name: Test Async API + if: matrix.os == 'ubuntu-latest' + run: xvfb-run pytest -vv tests/async --browser=chromium --browser-channel=${{ matrix.browser-channel }} --timeout 90 + test-package-installations: name: Test package installations runs-on: ubuntu-latest diff --git a/README.md b/README.md index feb91bcec..6725e0c79 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 90.0.4421.0 | ✅ | ✅ | ✅ | -| WebKit 14.1 | ✅ | ✅ | ✅ | -| Firefox 86.0b10 | ✅ | ✅ | ✅ | +| Chromium 90.0.4430.0 | ✅ | ✅ | ✅ | +| WebKit 14.2 | ✅ | ✅ | ✅ | +| Firefox 87.0b10 | ✅ | ✅ | ✅ | Headless execution is supported for all browsers on all platforms. diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index 2dce90ad7..27f869ff4 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -49,6 +49,7 @@ def executable_path(self) -> str: async def launch( self, executablePath: Union[str, Path] = None, + channel: str = None, args: List[str] = None, ignoreDefaultArgs: Union[bool, List[str]] = None, handleSIGINT: bool = None, @@ -76,6 +77,7 @@ async def launch( async def launch_persistent_context( self, userDataDir: Union[str, Path], + channel: str = None, executablePath: Union[str, Path] = None, args: List[str] = None, ignoreDefaultArgs: Union[bool, List[str]] = None, diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 623c807eb..0c4ae2846 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -92,6 +92,8 @@ def resource_type(self) -> str: following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`. + > NOTE: The resource types are available as constants in [ResourceTypes]. + Returns ------- str @@ -2837,7 +2839,7 @@ async def evaluate_handle( Returns the return value of `expression` as a `JSHandle`. The only difference between `frame.evaluate()` and `frame.evaluate_handle()` is that - [method: Frame.evaluateHandle`] returns `JSHandle`. + `frame.evaluate_handle()` returns `JSHandle`. If the function, passed to the `frame.evaluate_handle()`, returns a [Promise], then `frame.evaluate_handle()` would wait for the promise to resolve and return its value. @@ -4359,7 +4361,7 @@ async def evaluate(self, expression: str, arg: typing.Any = None) -> typing.Any: wait for the promise to resolve and return its value. If the function passed to the `worker.evaluate()` returns a non-[Serializable] value, then - `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that are + `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`. Parameters @@ -4629,7 +4631,7 @@ def suggested_filename(self) -> str: async def delete(self) -> NoneType: """Download.delete - Deletes the downloaded file. + Deletes the downloaded file. Will wait for the download to finish if necessary. """ return mapping.from_maybe_impl( @@ -4639,7 +4641,7 @@ async def delete(self) -> NoneType: async def failure(self) -> typing.Optional[str]: """Download.failure - Returns download error if any. + Returns download error if any. Will wait for the download to finish if necessary. Returns ------- @@ -4653,7 +4655,8 @@ async def failure(self) -> typing.Optional[str]: async def path(self) -> typing.Optional[pathlib.Path]: """Download.path - Returns path to the downloaded file in case of successful download. + Returns path to the downloaded file in case of successful download. The method will wait for the download to finish if + necessary. Returns ------- @@ -4667,7 +4670,7 @@ async def path(self) -> typing.Optional[pathlib.Path]: async def save_as(self, path: typing.Union[str, pathlib.Path]) -> NoneType: """Download.save_as - Saves the download to a user-specified path. + Saves the download to a user-specified path. It is safe to call this method while the download is still in progress. Parameters ---------- @@ -7666,9 +7669,16 @@ def expect_response( Returns the matched response. ```py - first_response = await page.wait_for_response(\"https://example.com/resource\") - final_response = await page.wait_for_response(lambda response: response.url == \"https://example.com\" and response.status === 200) - return final_response.ok + async with page.expect_response(\"https://example.com/resource\") as response_info: + await page.click(\"input\") + response = response_info.value + return response.ok + + # or with a lambda + async with page.expect_response(lambda response: response.url == \"https://example.com\" and response.status === 200) as response_info: + await page.click(\"input\") + response = response_info.value + return response.ok ``` Parameters @@ -8623,7 +8633,7 @@ async def new_context( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. proxy : Union[{server: str, bypass: Union[str, NoneType], username: Union[str, NoneType], password: Union[str, NoneType]}, NoneType] @@ -8760,7 +8770,7 @@ async def new_page( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. proxy : Union[{server: str, bypass: Union[str, NoneType], username: Union[str, NoneType], password: Union[str, NoneType]}, NoneType] @@ -8872,6 +8882,7 @@ async def launch( self, *, executable_path: typing.Union[str, pathlib.Path] = None, + channel: str = None, args: typing.List[str] = None, ignore_default_args: typing.Union[bool, typing.List[str]] = None, handle_sigint: bool = None, @@ -8922,6 +8933,16 @@ async def launch( Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use at your own risk. + channel : Union[str, NoneType] + Chromium distribution channel, one of + - chrome + - chrome-beta + - chrome-dev + - chrome-canary + - msedge + - msedge-beta + - msedge-dev + - msedge-canary args : Union[List[str], NoneType] Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/). @@ -8970,6 +8991,7 @@ async def launch( "browser_type.launch", self._impl_obj.launch( executablePath=executable_path, + channel=channel, args=args, ignoreDefaultArgs=ignore_default_args, handleSIGINT=handle_sigint, @@ -8992,6 +9014,7 @@ async def launch_persistent_context( self, user_data_dir: typing.Union[str, pathlib.Path], *, + channel: str = None, executable_path: typing.Union[str, pathlib.Path] = None, args: typing.List[str] = None, ignore_default_args: typing.Union[bool, typing.List[str]] = None, @@ -9043,6 +9066,16 @@ async def launch_persistent_context( [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). Note that Chromium's user data directory is the **parent** directory of the "Profile Path" seen at `chrome://version`. + channel : Union[str, NoneType] + Chromium distribution channel, one of + - chrome + - chrome-beta + - chrome-dev + - chrome-canary + - msedge + - msedge-beta + - msedge-dev + - msedge-canary executable_path : Union[pathlib.Path, str, NoneType] Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to the current working directory. **BEWARE**: Playwright is only guaranteed to work with the bundled @@ -9118,7 +9151,7 @@ async def launch_persistent_context( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. chromium_sandbox : Union[bool, NoneType] @@ -9144,6 +9177,7 @@ async def launch_persistent_context( "browser_type.launch_persistent_context", self._impl_obj.launch_persistent_context( userDataDir=user_data_dir, + channel=channel, executablePath=executable_path, args=args, ignoreDefaultArgs=ignore_default_args, diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 3b672a5cf..a6521137a 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -92,6 +92,8 @@ def resource_type(self) -> str: following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`. + > NOTE: The resource types are available as constants in [ResourceTypes]. + Returns ------- str @@ -2822,7 +2824,7 @@ def evaluate_handle(self, expression: str, arg: typing.Any = None) -> "JSHandle" Returns the return value of `expression` as a `JSHandle`. The only difference between `frame.evaluate()` and `frame.evaluate_handle()` is that - [method: Frame.evaluateHandle`] returns `JSHandle`. + `frame.evaluate_handle()` returns `JSHandle`. If the function, passed to the `frame.evaluate_handle()`, returns a [Promise], then `frame.evaluate_handle()` would wait for the promise to resolve and return its value. @@ -4338,7 +4340,7 @@ def evaluate(self, expression: str, arg: typing.Any = None) -> typing.Any: wait for the promise to resolve and return its value. If the function passed to the `worker.evaluate()` returns a non-[Serializable] value, then - `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that are + `worker.evaluate()` returns `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`. Parameters @@ -4604,7 +4606,7 @@ def suggested_filename(self) -> str: def delete(self) -> NoneType: """Download.delete - Deletes the downloaded file. + Deletes the downloaded file. Will wait for the download to finish if necessary. """ return mapping.from_maybe_impl( @@ -4614,7 +4616,7 @@ def delete(self) -> NoneType: def failure(self) -> typing.Optional[str]: """Download.failure - Returns download error if any. + Returns download error if any. Will wait for the download to finish if necessary. Returns ------- @@ -4628,7 +4630,8 @@ def failure(self) -> typing.Optional[str]: def path(self) -> typing.Optional[pathlib.Path]: """Download.path - Returns path to the downloaded file in case of successful download. + Returns path to the downloaded file in case of successful download. The method will wait for the download to finish if + necessary. Returns ------- @@ -4642,7 +4645,7 @@ def path(self) -> typing.Optional[pathlib.Path]: def save_as(self, path: typing.Union[str, pathlib.Path]) -> NoneType: """Download.save_as - Saves the download to a user-specified path. + Saves the download to a user-specified path. It is safe to call this method while the download is still in progress. Parameters ---------- @@ -7620,9 +7623,16 @@ def expect_response( Returns the matched response. ```py - first_response = page.wait_for_response(\"https://example.com/resource\") - final_response = page.wait_for_response(lambda response: response.url == \"https://example.com\" and response.status === 200) - return final_response.ok + with page.expect_response(\"https://example.com/resource\") as response_info: + page.click(\"input\") + response = response_info.value + return response.ok + + # or with a lambda + with page.expect_response(lambda response: response.url == \"https://example.com\" and response.status === 200) as response_info: + page.click(\"input\") + response = response_info.value + return response.ok ``` Parameters @@ -8569,7 +8579,7 @@ def new_context( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. proxy : Union[{server: str, bypass: Union[str, NoneType], username: Union[str, NoneType], password: Union[str, NoneType]}, NoneType] @@ -8706,7 +8716,7 @@ def new_page( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. proxy : Union[{server: str, bypass: Union[str, NoneType], username: Union[str, NoneType], password: Union[str, NoneType]}, NoneType] @@ -8818,6 +8828,7 @@ def launch( self, *, executable_path: typing.Union[str, pathlib.Path] = None, + channel: str = None, args: typing.List[str] = None, ignore_default_args: typing.Union[bool, typing.List[str]] = None, handle_sigint: bool = None, @@ -8868,6 +8879,16 @@ def launch( Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use at your own risk. + channel : Union[str, NoneType] + Chromium distribution channel, one of + - chrome + - chrome-beta + - chrome-dev + - chrome-canary + - msedge + - msedge-beta + - msedge-dev + - msedge-canary args : Union[List[str], NoneType] Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/). @@ -8916,6 +8937,7 @@ def launch( "browser_type.launch", self._impl_obj.launch( executablePath=executable_path, + channel=channel, args=args, ignoreDefaultArgs=ignore_default_args, handleSIGINT=handle_sigint, @@ -8938,6 +8960,7 @@ def launch_persistent_context( self, user_data_dir: typing.Union[str, pathlib.Path], *, + channel: str = None, executable_path: typing.Union[str, pathlib.Path] = None, args: typing.List[str] = None, ignore_default_args: typing.Union[bool, typing.List[str]] = None, @@ -8989,6 +9012,16 @@ def launch_persistent_context( [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile). Note that Chromium's user data directory is the **parent** directory of the "Profile Path" seen at `chrome://version`. + channel : Union[str, NoneType] + Chromium distribution channel, one of + - chrome + - chrome-beta + - chrome-dev + - chrome-canary + - msedge + - msedge-beta + - msedge-dev + - msedge-canary executable_path : Union[pathlib.Path, str, NoneType] Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to the current working directory. **BEWARE**: Playwright is only guaranteed to work with the bundled @@ -9064,7 +9097,7 @@ def launch_persistent_context( Specifies if viewport supports touch events. Defaults to false. color_scheme : Union["dark", "light", "no-preference", NoneType] Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See - `page.emulate_media()` for more details. Defaults to '`light`'. + `page.emulate_media()` for more details. Defaults to `'light'`. accept_downloads : Union[bool, NoneType] Whether to automatically download all the attachments. Defaults to `false` where all the downloads are canceled. chromium_sandbox : Union[bool, NoneType] @@ -9090,6 +9123,7 @@ def launch_persistent_context( "browser_type.launch_persistent_context", self._impl_obj.launch_persistent_context( userDataDir=user_data_dir, + channel=channel, executablePath=executable_path, args=args, ignoreDefaultArgs=ignore_default_args, diff --git a/setup.py b/setup.py index 4d4d4462f..7f65242db 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ from auditwheel.wheeltools import InWheel from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.9.0-1614037901000" +driver_version = "1.10.0-next-1616094973000" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_browsercontext.py b/tests/async/test_browsercontext.py index c45df3484..607895988 100644 --- a/tests/async/test_browsercontext.py +++ b/tests/async/test_browsercontext.py @@ -409,8 +409,10 @@ async def test_expose_function_should_be_callable_from_inside_add_init_script( await context.expose_function("woof", lambda arg: args.append(arg)) await context.add_init_script("woof('context')") page = await context.new_page() - await page.add_init_script("woof('page')") + await page.evaluate("undefined") + assert args == ["context"] args = [] + await page.add_init_script("woof('page')") await page.reload() assert args == ["context", "page"] diff --git a/tests/async/test_issues.py b/tests/async/test_issues.py index 0c46ec064..560c857c4 100644 --- a/tests/async/test_issues.py +++ b/tests/async/test_issues.py @@ -16,8 +16,10 @@ @pytest.mark.only_browser("chromium") -async def test_issue_189(browser_type): - browser = await browser_type.launch(ignore_default_args=["--mute-audio"]) +async def test_issue_189(browser_type, launch_arguments): + browser = await browser_type.launch( + **launch_arguments, ignore_default_args=["--mute-audio"] + ) page = await browser.new_page() assert await page.evaluate("1 + 1") == 2 await browser.close() diff --git a/tests/async/test_keyboard.py b/tests/async/test_keyboard.py index 13fba1426..1d12c6c22 100644 --- a/tests/async/test_keyboard.py +++ b/tests/async/test_keyboard.py @@ -480,6 +480,7 @@ async def test_should_be_able_to_prevent_select_all(page, server, is_mac): @pytest.mark.only_platform("darwin") +@pytest.mark.skip_browser("firefox") # Upstream issue async def test_should_support_macos_shortcuts(page, server, is_firefox, is_mac): await page.goto(server.PREFIX + "/input/textarea.html") textarea = await page.query_selector("textarea") diff --git a/tests/async/test_launcher.py b/tests/async/test_launcher.py index 86414a30c..3265f61d5 100644 --- a/tests/async/test_launcher.py +++ b/tests/async/test_launcher.py @@ -80,7 +80,9 @@ async def test_browser_type_launch_server_should_fire_close_event( await asyncio.gather(close_event, browser_server.close()) -async def test_browser_type_executable_path_should_work(browser_type): +async def test_browser_type_executable_path_should_work(browser_type, browser_channel): + if browser_channel: + return executable_path = browser_type.executable_path assert os.path.exists(executable_path) assert os.path.realpath(executable_path) == os.path.realpath(executable_path) diff --git a/tests/async/test_video.py b/tests/async/test_video.py index 87852450c..bb6210da7 100644 --- a/tests/async/test_video.py +++ b/tests/async/test_video.py @@ -32,9 +32,12 @@ async def test_short_video_should_exist(browser, tmpdir, server): assert os.path.exists(path) -async def test_short_video_should_exist_persistent_context(browser_type, tmpdir): +async def test_short_video_should_exist_persistent_context( + browser_type, tmpdir, launch_arguments +): context = await browser_type.launch_persistent_context( str(tmpdir), + **launch_arguments, viewport={"width": 320, "height": 240}, record_video_dir=str(tmpdir) + "1", ) diff --git a/tests/common/test_threads.py b/tests/common/test_threads.py index dcbbfc61d..f8181f03b 100644 --- a/tests/common/test_threads.py +++ b/tests/common/test_threads.py @@ -17,13 +17,13 @@ from playwright.sync_api import sync_playwright -def test_running_in_thread(browser_name): +def test_running_in_thread(browser_name, launch_arguments): result = [] class TestThread(threading.Thread): def run(self): with sync_playwright() as playwright: - browser = getattr(playwright, browser_name).launch() + browser = getattr(playwright, browser_name).launch(**launch_arguments) # This should not throw ^^. browser.new_page() browser.close() diff --git a/tests/conftest.py b/tests/conftest.py index cccce0d63..2c43f2133 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,8 +48,10 @@ def assetdir(): @pytest.fixture(scope="session") def launch_arguments(pytestconfig): + print(pytestconfig.getoption("--browser-channel")) return { "headless": not pytestconfig.getoption("--headful"), + "channel": pytestconfig.getoption("--browser-channel"), } @@ -86,6 +88,11 @@ def browser_name(pytestconfig): return pytestconfig.getoption("browser") +@pytest.fixture(scope="session") +def browser_channel(pytestconfig): + return pytestconfig.getoption("--browser-channel") + + @pytest.fixture(scope="session") def is_webkit(browser_name): return browser_name == "webkit" @@ -160,6 +167,12 @@ def pytest_addoption(parser): default=[], help="Browsers which should be used. By default on all the browsers.", ) + group.addoption( + "--browser-channel", + action="store", + default=None, + help="Browser channel to be used.", + ) parser.addoption( "--headful", action="store_true", diff --git a/tests/sync/test_video.py b/tests/sync/test_video.py index 80507418d..f0ae19fb0 100644 --- a/tests/sync/test_video.py +++ b/tests/sync/test_video.py @@ -43,8 +43,12 @@ def test_record_video_to_path(browser, tmpdir, server): assert os.path.exists(path) -def test_record_video_to_path_persistent(browser_type, tmpdir, server): - context = browser_type.launch_persistent_context(tmpdir, record_video_dir=tmpdir) +def test_record_video_to_path_persistent( + browser_type, tmpdir, server, launch_arguments +): + context = browser_type.launch_persistent_context( + tmpdir, **launch_arguments, record_video_dir=tmpdir + ) page = context.pages[0] page.goto(server.PREFIX + "/grid.html") path = page.video.path()