From 824f2acb0fdef96a94cca0d83c9e7b89c16bd958 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 25 Apr 2025 13:07:13 +0530 Subject: [PATCH 1/5] add bidi storage module --- py/selenium/webdriver/common/bidi/storage.py | 418 +++++++++++++++++++ py/selenium/webdriver/remote/webdriver.py | 25 ++ 2 files changed, 443 insertions(+) create mode 100644 py/selenium/webdriver/common/bidi/storage.py diff --git a/py/selenium/webdriver/common/bidi/storage.py b/py/selenium/webdriver/common/bidi/storage.py new file mode 100644 index 0000000000000..b531054ea689e --- /dev/null +++ b/py/selenium/webdriver/common/bidi/storage.py @@ -0,0 +1,418 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from typing import Dict +from typing import List +from typing import Optional +from typing import Union + +from selenium.webdriver.common.bidi.common import command_builder + + +class SameSite: + """Represents the possible same site values for cookies.""" + + STRICT = "strict" + LAX = "lax" + NONE = "none" + + +class BytesValue: + """Represents a bytes value.""" + + TYPE_BASE64 = "base64" + TYPE_STRING = "string" + + def __init__(self, type: str, value: str): + self.type = type + self.value = value + + def to_dict(self) -> Dict: + """Converts the BytesValue to a dictionary. + + Returns: + ------- + Dict: A dictionary representation of the BytesValue. + """ + return {"type": self.type, "value": self.value} + + +class Cookie: + """Represents a cookie.""" + + def __init__( + self, + name: str, + value: BytesValue, + domain: str, + path: Optional[str] = None, + size: Optional[int] = None, + http_only: Optional[bool] = None, + secure: Optional[bool] = None, + same_site: Optional[str] = None, + expiry: Optional[int] = None, + ): + self.name = name + self.value = value + self.domain = domain + self.path = path + self.size = size + self.http_only = http_only + self.secure = secure + self.same_site = same_site + self.expiry = expiry + + @classmethod + def from_dict(cls, data: Dict) -> "Cookie": + """Creates a Cookie instance from a dictionary. + + Parameters: + ----------- + data: A dictionary containing the cookie information. + + Returns: + ------- + Cookie: A new instance of Cookie. + """ + value = BytesValue(data.get("value", {}).get("type"), data.get("value", {}).get("value")) + + return cls( + name=data.get("name"), + value=value, + domain=data.get("domain"), + path=data.get("path"), + size=data.get("size"), + http_only=data.get("httpOnly"), + secure=data.get("secure"), + same_site=data.get("sameSite"), + expiry=data.get("expiry"), + ) + + +class CookieFilter: + """Represents a filter for cookies.""" + + def __init__( + self, + name: Optional[str] = None, + value: Optional[BytesValue] = None, + domain: Optional[str] = None, + path: Optional[str] = None, + size: Optional[int] = None, + http_only: Optional[bool] = None, + secure: Optional[bool] = None, + same_site: Optional[str] = None, + expiry: Optional[int] = None, + ): + self.name = name + self.value = value + self.domain = domain + self.path = path + self.size = size + self.http_only = http_only + self.secure = secure + self.same_site = same_site + self.expiry = expiry + + def to_dict(self) -> Dict: + """Converts the CookieFilter to a dictionary. + + Returns: + ------- + Dict: A dictionary representation of the CookieFilter. + """ + result = {} + if self.name is not None: + result["name"] = self.name + if self.value is not None: + result["value"] = self.value.to_dict() + if self.domain is not None: + result["domain"] = self.domain + if self.path is not None: + result["path"] = self.path + if self.size is not None: + result["size"] = self.size + if self.http_only is not None: + result["httpOnly"] = self.http_only + if self.secure is not None: + result["secure"] = self.secure + if self.same_site is not None: + result["sameSite"] = self.same_site + if self.expiry is not None: + result["expiry"] = self.expiry + return result + + +class PartitionKey: + """Represents a storage partition key.""" + + def __init__(self, user_context: Optional[str] = None, source_origin: Optional[str] = None): + self.user_context = user_context + self.source_origin = source_origin + + @classmethod + def from_dict(cls, data: Dict) -> "PartitionKey": + """Creates a PartitionKey instance from a dictionary. + + Parameters: + ----------- + data: A dictionary containing the partition key information. + + Returns: + ------- + PartitionKey: A new instance of PartitionKey. + """ + return cls( + user_context=data.get("userContext"), + source_origin=data.get("sourceOrigin"), + ) + + +class BrowsingContextPartitionDescriptor: + """Represents a browsing context partition descriptor.""" + + def __init__(self, context: str): + self.type = "context" + self.context = context + + def to_dict(self) -> Dict: + """Converts the BrowsingContextPartitionDescriptor to a dictionary. + + Returns: + ------- + Dict: A dictionary representation of the BrowsingContextPartitionDescriptor. + """ + return {"type": self.type, "context": self.context} + + +class StorageKeyPartitionDescriptor: + """Represents a storage key partition descriptor.""" + + def __init__(self, user_context: Optional[str] = None, source_origin: Optional[str] = None): + self.type = "storageKey" + self.user_context = user_context + self.source_origin = source_origin + + def to_dict(self) -> Dict: + """Converts the StorageKeyPartitionDescriptor to a dictionary. + + Returns: + ------- + Dict: A dictionary representation of the StorageKeyPartitionDescriptor. + """ + result = {"type": self.type} + if self.user_context is not None: + result["userContext"] = self.user_context + if self.source_origin is not None: + result["sourceOrigin"] = self.source_origin + return result + + +class PartialCookie: + """Represents a partial cookie for setting.""" + + def __init__( + self, + name: str, + value: BytesValue, + domain: str, + path: Optional[str] = None, + http_only: Optional[bool] = None, + secure: Optional[bool] = None, + same_site: Optional[str] = None, + expiry: Optional[int] = None, + ): + self.name = name + self.value = value + self.domain = domain + self.path = path + self.http_only = http_only + self.secure = secure + self.same_site = same_site + self.expiry = expiry + + def to_dict(self) -> Dict: + """Converts the PartialCookie to a dictionary. + + Returns: + ------- + Dict: A dictionary representation of the PartialCookie. + """ + result = { + "name": self.name, + "value": self.value.to_dict(), + "domain": self.domain, + } + if self.path is not None: + result["path"] = self.path + if self.http_only is not None: + result["httpOnly"] = self.http_only + if self.secure is not None: + result["secure"] = self.secure + if self.same_site is not None: + result["sameSite"] = self.same_site + if self.expiry is not None: + result["expiry"] = self.expiry + return result + + +class GetCookiesResult: + """Represents the result of a getCookies command.""" + + def __init__(self, cookies: List[Cookie], partition_key: PartitionKey): + self.cookies = cookies + self.partition_key = partition_key + + @classmethod + def from_dict(cls, data: Dict) -> "GetCookiesResult": + """Creates a GetCookiesResult instance from a dictionary. + + Parameters: + ----------- + data: A dictionary containing the get cookies result information. + + Returns: + ------- + GetCookiesResult: A new instance of GetCookiesResult. + """ + cookies = [Cookie.from_dict(cookie) for cookie in data.get("cookies", [])] + partition_key = PartitionKey.from_dict(data.get("partitionKey", {})) + return cls(cookies=cookies, partition_key=partition_key) + + +class SetCookieResult: + """Represents the result of a setCookie command.""" + + def __init__(self, partition_key: PartitionKey): + self.partition_key = partition_key + + @classmethod + def from_dict(cls, data: Dict) -> "SetCookieResult": + """Creates a SetCookieResult instance from a dictionary. + + Parameters: + ----------- + data: A dictionary containing the set cookie result information. + + Returns: + ------- + SetCookieResult: A new instance of SetCookieResult. + """ + partition_key = PartitionKey.from_dict(data.get("partitionKey", {})) + return cls(partition_key=partition_key) + + +class DeleteCookiesResult: + """Represents the result of a deleteCookies command.""" + + def __init__(self, partition_key: PartitionKey): + self.partition_key = partition_key + + @classmethod + def from_dict(cls, data: Dict) -> "DeleteCookiesResult": + """Creates a DeleteCookiesResult instance from a dictionary. + + Parameters: + ----------- + data: A dictionary containing the delete cookies result information. + + Returns: + ------- + DeleteCookiesResult: A new instance of DeleteCookiesResult. + """ + partition_key = PartitionKey.from_dict(data.get("partitionKey", {})) + return cls(partition_key=partition_key) + + +class Storage: + """BiDi implementation of the storage module.""" + + def __init__(self, conn): + self.conn = conn + + def get_cookies( + self, + filter: Optional[CookieFilter] = None, + partition: Optional[Union[BrowsingContextPartitionDescriptor, StorageKeyPartitionDescriptor]] = None, + ) -> GetCookiesResult: + """Retrieves cookies that match the given parameters. + + Parameters: + ----------- + filter: Optional filter to match cookies. + partition: Optional partition descriptor. + + Returns: + ------- + GetCookiesResult: The result of the get cookies command. + """ + params = {} + if filter is not None: + params["filter"] = filter.to_dict() + if partition is not None: + params["partition"] = partition.to_dict() + + result = self.conn.execute(command_builder("storage.getCookies", params)) + return GetCookiesResult.from_dict(result) + + def set_cookie( + self, + cookie: PartialCookie, + partition: Optional[Union[BrowsingContextPartitionDescriptor, StorageKeyPartitionDescriptor]] = None, + ) -> SetCookieResult: + """Sets a cookie in the browser. + + Parameters: + ----------- + cookie: The cookie to set. + partition: Optional partition descriptor. + + Returns: + ------- + SetCookieResult: The result of the set cookie command. + """ + params = {"cookie": cookie.to_dict()} + if partition is not None: + params["partition"] = partition.to_dict() + + result = self.conn.execute(command_builder("storage.setCookie", params)) + return SetCookieResult.from_dict(result) + + def delete_cookies( + self, + filter: Optional[CookieFilter] = None, + partition: Optional[Union[BrowsingContextPartitionDescriptor, StorageKeyPartitionDescriptor]] = None, + ) -> DeleteCookiesResult: + """Deletes cookies that match the given parameters. + + Parameters: + ----------- + filter: Optional filter to match cookies to delete. + partition: Optional partition descriptor. + + Returns: + ------- + DeleteCookiesResult: The result of the delete cookies command. + """ + params = {} + if filter is not None: + params["filter"] = filter.to_dict() + if partition is not None: + params["partition"] = partition.to_dict() + + result = self.conn.execute(command_builder("storage.deleteCookies", params)) + return DeleteCookiesResult.from_dict(result) diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index ff7c86b582f6d..ca7940967562b 100644 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -48,6 +48,7 @@ from selenium.webdriver.common.bidi.network import Network from selenium.webdriver.common.bidi.script import Script from selenium.webdriver.common.bidi.session import Session +from selenium.webdriver.common.bidi.storage import Storage from selenium.webdriver.common.by import By from selenium.webdriver.common.options import ArgOptions from selenium.webdriver.common.options import BaseOptions @@ -266,6 +267,7 @@ def __init__( self._browser = None self._bidi_session = None self._browsing_context = None + self._storage = None def __repr__(self): return f'<{type(self).__module__}.{type(self).__name__} (session="{self.session_id}")>' @@ -1317,6 +1319,29 @@ def browsing_context(self): return self._browsing_context + @property + def storage(self): + """Returns a storage module object for BiDi storage commands. + + Returns: + -------- + Storage: an object containing access to BiDi storage commands. + + Examples: + --------- + >>> cookie_filter = CookieFilter(name="example") + >>> result = driver.storage.get_cookies(filter=cookie_filter) + >>> driver.storage.set_cookie(cookie=PartialCookie("name", BytesValue(BytesValue.TYPE_STRING, "value"), "domain")) + >>> driver.storage.delete_cookies(filter=CookieFilter(name="example")) + """ + if not self._websocket_connection: + self._start_bidi() + + if self._storage is None: + self._storage = Storage(self._websocket_connection) + + return self._storage + def _get_cdp_details(self): import json From 56ba0da4198c766c8cff5896dd412b0698e4a624 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 25 Apr 2025 13:07:29 +0530 Subject: [PATCH 2/5] add tests for storage module --- .../webdriver/common/bidi_storage_tests.py | 382 ++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 py/test/selenium/webdriver/common/bidi_storage_tests.py diff --git a/py/test/selenium/webdriver/common/bidi_storage_tests.py b/py/test/selenium/webdriver/common/bidi_storage_tests.py new file mode 100644 index 0000000000000..50ab8569c5b7d --- /dev/null +++ b/py/test/selenium/webdriver/common/bidi_storage_tests.py @@ -0,0 +1,382 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import random +import time + +import pytest + +from selenium.webdriver.common.bidi.storage import BrowsingContextPartitionDescriptor +from selenium.webdriver.common.bidi.storage import BytesValue +from selenium.webdriver.common.bidi.storage import CookieFilter +from selenium.webdriver.common.bidi.storage import PartialCookie +from selenium.webdriver.common.bidi.storage import SameSite +from selenium.webdriver.common.bidi.storage import StorageKeyPartitionDescriptor +from selenium.webdriver.common.window import WindowTypes + + +def generate_unique_key(): + return f"key_{random.randint(0, 100000)}" + + +def assert_cookie_is_not_present_with_name(driver, key): + assert driver.get_cookie(key) is None + document_cookie = get_document_cookie_or_none(driver) + if document_cookie is not None: + assert key + "=" not in document_cookie + + +def assert_cookie_is_present_with_name(driver, key): + assert driver.get_cookie(key) is not None + document_cookie = get_document_cookie_or_none(driver) + if document_cookie is not None: + assert key + "=" in document_cookie + + +def assert_cookie_has_value(driver, key, value): + assert driver.get_cookie(key)["value"] == value + document_cookie = get_document_cookie_or_none(driver) + if document_cookie is not None: + assert f"{key}={value}" in document_cookie + + +def assert_no_cookies_are_present(driver): + assert len(driver.get_cookies()) == 0 + document_cookie = get_document_cookie_or_none(driver) + if document_cookie is not None: + assert document_cookie == "" + + +def assert_some_cookies_are_present(driver): + assert len(driver.get_cookies()) > 0 + document_cookie = get_document_cookie_or_none(driver) + if document_cookie is not None: + assert document_cookie != "" + + +def get_document_cookie_or_none(driver): + try: + return driver.execute_script("return document.cookie") + except Exception: + return None + + +def test_storage_initialized(driver): + """Test that the storage module is initialized properly.""" + assert driver.storage is not None + + +def test_get_cookie_by_name(driver, pages, webserver): + """Test getting a cookie by name.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + key = generate_unique_key() + value = "set" + assert_cookie_is_not_present_with_name(driver, key) + + driver.add_cookie({"name": key, "value": value}) + + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + + result = driver.storage.get_cookies(filter=cookie_filter) + + # Verify + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value + + +@pytest.mark.xfail_chrome +@pytest.mark.xfail_edge +def test_get_cookie_in_default_user_context(driver, pages, webserver): + """Test getting a cookie in the default user context.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + window_handle = driver.current_window_handle + key = generate_unique_key() + value = "set" + assert_cookie_is_not_present_with_name(driver, key) + + driver.add_cookie({"name": key, "value": value}) + + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + + driver.switch_to.new_window(WindowTypes.WINDOW) + + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + + params = cookie_filter + result_after_switching_context = driver.storage.get_cookies(filter=params, partition=descriptor) + + assert len(result_after_switching_context.cookies) > 0 + assert result_after_switching_context.cookies[0].value.value == value + + driver.switch_to.window(window_handle) + + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value + partition_key = result.partition_key + + assert partition_key.source_origin is not None + assert partition_key.user_context is not None + assert partition_key.user_context == "default" + + +def test_get_cookie_in_a_user_context(driver, pages, webserver): + """Test getting a cookie in a user context.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + user_context = driver.browser.create_user_context() + window_handle = driver.current_window_handle + + key = generate_unique_key() + value = "set" + + descriptor = StorageKeyPartitionDescriptor(user_context=user_context) + + parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) + + driver.storage.set_cookie(cookie=parameters, partition=descriptor) + + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + + # Create a new window with the user context + new_window = driver.browsing_context.create(type=WindowTypes.TAB, user_context=user_context) + + driver.switch_to.window(new_window) + + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value + partition_key = result.partition_key + + assert partition_key.user_context is not None + assert partition_key.user_context == user_context + + driver.switch_to.window(window_handle) + + browsing_context_partition_descriptor = BrowsingContextPartitionDescriptor(window_handle) + + result1 = driver.storage.get_cookies(filter=cookie_filter, partition=browsing_context_partition_descriptor) + + assert len(result1.cookies) == 0 + + # Clean up + driver.browsing_context.close(new_window) + driver.browser.remove_user_context(user_context) + + +def test_add_cookie(driver, pages, webserver): + """Test adding a cookie.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + key = generate_unique_key() + value = "foo" + + parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) + assert_cookie_is_not_present_with_name(driver, key) + + # Test + driver.storage.set_cookie(cookie=parameters) + + # Verify + assert_cookie_has_value(driver, key, value) + driver.get(pages.url("simpleTest.html")) + assert_cookie_has_value(driver, key, value) + + +@pytest.mark.xfail_chrome +@pytest.mark.xfail_edge +def test_add_and_get_cookie(driver, pages, webserver): + """Test adding and getting a cookie with all parameters.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + value = BytesValue(BytesValue.TYPE_STRING, "cod") + domain = webserver.host + + expiry = int(time.time() + 3600) # 1 hour from now + + path = "/simpleTest.html" + + cookie = PartialCookie( + "fish", value, domain, path=path, http_only=True, secure=False, same_site=SameSite.LAX, expiry=expiry + ) + + # Test + driver.storage.set_cookie(cookie=cookie) + + driver.get(pages.url("simpleTest.html")) + + cookie_filter = CookieFilter( + name="fish", + value=value, + domain=domain, + path=path, + http_only=True, + secure=False, + same_site=SameSite.LAX, + expiry=expiry, + ) + + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + key = result.partition_key + + # Verify + assert len(result.cookies) > 0 + result_cookie = result.cookies[0] + + assert result_cookie.name == "fish" + assert result_cookie.value.value == value.value + assert result_cookie.domain == domain + assert result_cookie.path == path + assert result_cookie.http_only is True + assert result_cookie.secure is False + assert result_cookie.same_site == SameSite.LAX + assert result_cookie.expiry == expiry + assert key.source_origin is not None + assert key.user_context is not None + assert key.user_context == "default" + + +@pytest.mark.xfail_edge +def test_get_all_cookies(driver, pages, webserver): + """Test getting all cookies.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + key1 = generate_unique_key() + key2 = generate_unique_key() + + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_not_present_with_name(driver, key2) + + # Test + params = CookieFilter() + result = driver.storage.get_cookies(filter=params) + + count_before = len(result.cookies) + + driver.add_cookie({"name": key1, "value": "value"}) + driver.add_cookie({"name": key2, "value": "value"}) + + driver.get(pages.url("simpleTest.html")) + result = driver.storage.get_cookies(filter=params) + + # Verify + assert len(result.cookies) == count_before + 2 + cookie_names = [cookie.name for cookie in result.cookies] + assert key1 in cookie_names + assert key2 in cookie_names + + +def test_delete_all_cookies(driver, pages, webserver): + """Test deleting all cookies.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + driver.add_cookie({"name": "foo", "value": "set"}) + assert_some_cookies_are_present(driver) + + # Test + driver.storage.delete_cookies(filter=CookieFilter()) + + # Verify + assert_no_cookies_are_present(driver) + + driver.get(pages.url("simpleTest.html")) + assert_no_cookies_are_present(driver) + + +def test_delete_cookie_with_name(driver, pages, webserver): + """Test deleting a cookie with a specific name.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + key1 = generate_unique_key() + key2 = generate_unique_key() + + driver.add_cookie({"name": key1, "value": "set"}) + driver.add_cookie({"name": key2, "value": "set"}) + + assert_cookie_is_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) + + # Test + driver.storage.delete_cookies(filter=CookieFilter(name=key1)) + + # Verify + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) + + driver.get(pages.url("simpleTest.html")) + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) + + +def test_add_cookies_with_different_paths(driver, pages, webserver): + """Test adding cookies with different paths that are related to ours.""" + # Setup + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + assert_no_cookies_are_present(driver) + + cookie1 = PartialCookie("fish", BytesValue(BytesValue.TYPE_STRING, "cod"), webserver.host, path="/simpleTest.html") + + cookie2 = PartialCookie("planet", BytesValue(BytesValue.TYPE_STRING, "earth"), webserver.host, path="/") + + # Test + driver.storage.set_cookie(cookie=cookie1) + driver.storage.set_cookie(cookie=cookie2) + + driver.get(pages.url("simpleTest.html")) + + # Verify + assert_cookie_is_present_with_name(driver, "fish") + assert_cookie_is_present_with_name(driver, "planet") + + driver.get(pages.url("formPage.html")) + assert_cookie_is_not_present_with_name(driver, "fish") From 3c65bfc8715fcc35c23e90585ae55839d0b4da17 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Fri, 25 Apr 2025 13:30:24 +0530 Subject: [PATCH 3/5] use autouse fixture for cookie deletion --- .../webdriver/common/bidi_storage_tests.py | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/py/test/selenium/webdriver/common/bidi_storage_tests.py b/py/test/selenium/webdriver/common/bidi_storage_tests.py index 50ab8569c5b7d..9dfec4e67fd96 100644 --- a/py/test/selenium/webdriver/common/bidi_storage_tests.py +++ b/py/test/selenium/webdriver/common/bidi_storage_tests.py @@ -75,6 +75,16 @@ def get_document_cookie_or_none(driver): return None +@pytest.fixture(autouse=True) +def setup(driver, pages): + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + + yield + + driver.quit() + + def test_storage_initialized(driver): """Test that the storage module is initialized properly.""" assert driver.storage is not None @@ -82,9 +92,6 @@ def test_storage_initialized(driver): def test_get_cookie_by_name(driver, pages, webserver): """Test getting a cookie by name.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) key = generate_unique_key() @@ -107,9 +114,6 @@ def test_get_cookie_by_name(driver, pages, webserver): @pytest.mark.xfail_edge def test_get_cookie_in_default_user_context(driver, pages, webserver): """Test getting a cookie in the default user context.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) window_handle = driver.current_window_handle @@ -149,9 +153,6 @@ def test_get_cookie_in_default_user_context(driver, pages, webserver): def test_get_cookie_in_a_user_context(driver, pages, webserver): """Test getting a cookie in a user context.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) user_context = driver.browser.create_user_context() @@ -198,9 +199,6 @@ def test_get_cookie_in_a_user_context(driver, pages, webserver): def test_add_cookie(driver, pages, webserver): """Test adding a cookie.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) key = generate_unique_key() @@ -222,9 +220,6 @@ def test_add_cookie(driver, pages, webserver): @pytest.mark.xfail_edge def test_add_and_get_cookie(driver, pages, webserver): """Test adding and getting a cookie with all parameters.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) value = BytesValue(BytesValue.TYPE_STRING, "cod") @@ -279,9 +274,6 @@ def test_add_and_get_cookie(driver, pages, webserver): @pytest.mark.xfail_edge def test_get_all_cookies(driver, pages, webserver): """Test getting all cookies.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) key1 = generate_unique_key() @@ -311,9 +303,6 @@ def test_get_all_cookies(driver, pages, webserver): def test_delete_all_cookies(driver, pages, webserver): """Test deleting all cookies.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) driver.add_cookie({"name": "foo", "value": "set"}) @@ -331,9 +320,6 @@ def test_delete_all_cookies(driver, pages, webserver): def test_delete_cookie_with_name(driver, pages, webserver): """Test deleting a cookie with a specific name.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) key1 = generate_unique_key() @@ -359,9 +345,6 @@ def test_delete_cookie_with_name(driver, pages, webserver): def test_add_cookies_with_different_paths(driver, pages, webserver): """Test adding cookies with different paths that are related to ours.""" - # Setup - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() assert_no_cookies_are_present(driver) cookie1 = PartialCookie("fish", BytesValue(BytesValue.TYPE_STRING, "cod"), webserver.host, path="/simpleTest.html") From 2752cff7b62d632d4196e1a9540a73a3ab760f89 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Sat, 3 May 2025 11:09:19 +0530 Subject: [PATCH 4/5] remove driver.quit() in fixture --- py/test/selenium/webdriver/common/bidi_storage_tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/py/test/selenium/webdriver/common/bidi_storage_tests.py b/py/test/selenium/webdriver/common/bidi_storage_tests.py index 9dfec4e67fd96..b821dc9e2b5e7 100644 --- a/py/test/selenium/webdriver/common/bidi_storage_tests.py +++ b/py/test/selenium/webdriver/common/bidi_storage_tests.py @@ -82,8 +82,6 @@ def setup(driver, pages): yield - driver.quit() - def test_storage_initialized(driver): """Test that the storage module is initialized properly.""" From 3228ebf253d230ce7c24b0f26680dfec762c8cea Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Mon, 5 May 2025 09:14:49 +0530 Subject: [PATCH 5/5] move tests to a test class --- .../webdriver/common/bidi_storage_tests.py | 400 +++++++++--------- 1 file changed, 196 insertions(+), 204 deletions(-) diff --git a/py/test/selenium/webdriver/common/bidi_storage_tests.py b/py/test/selenium/webdriver/common/bidi_storage_tests.py index b821dc9e2b5e7..bc07b76d7ac85 100644 --- a/py/test/selenium/webdriver/common/bidi_storage_tests.py +++ b/py/test/selenium/webdriver/common/bidi_storage_tests.py @@ -75,289 +75,281 @@ def get_document_cookie_or_none(driver): return None -@pytest.fixture(autouse=True) -def setup(driver, pages): - driver.get(pages.url("simpleTest.html")) - driver.delete_all_cookies() +class TestBidiStorage: - yield + @pytest.fixture(autouse=True) + def setup(self, driver, pages): + driver.get(pages.url("simpleTest.html")) + driver.delete_all_cookies() + def test_storage_initialized(self, driver): + """Test that the storage module is initialized properly.""" + assert driver.storage is not None -def test_storage_initialized(driver): - """Test that the storage module is initialized properly.""" - assert driver.storage is not None + def test_get_cookie_by_name(self, driver, pages, webserver): + """Test getting a cookie by name.""" + assert_no_cookies_are_present(driver) + key = generate_unique_key() + value = "set" + assert_cookie_is_not_present_with_name(driver, key) -def test_get_cookie_by_name(driver, pages, webserver): - """Test getting a cookie by name.""" - assert_no_cookies_are_present(driver) + driver.add_cookie({"name": key, "value": value}) - key = generate_unique_key() - value = "set" - assert_cookie_is_not_present_with_name(driver, key) + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) - driver.add_cookie({"name": key, "value": value}) + result = driver.storage.get_cookies(filter=cookie_filter) - # Test - cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + # Verify + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value - result = driver.storage.get_cookies(filter=cookie_filter) + @pytest.mark.xfail_chrome + @pytest.mark.xfail_edge + def test_get_cookie_in_default_user_context(self, driver, pages, webserver): + """Test getting a cookie in the default user context.""" + assert_no_cookies_are_present(driver) - # Verify - assert len(result.cookies) > 0 - assert result.cookies[0].value.value == value + window_handle = driver.current_window_handle + key = generate_unique_key() + value = "set" + assert_cookie_is_not_present_with_name(driver, key) + driver.add_cookie({"name": key, "value": value}) -@pytest.mark.xfail_chrome -@pytest.mark.xfail_edge -def test_get_cookie_in_default_user_context(driver, pages, webserver): - """Test getting a cookie in the default user context.""" - assert_no_cookies_are_present(driver) + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) - window_handle = driver.current_window_handle - key = generate_unique_key() - value = "set" - assert_cookie_is_not_present_with_name(driver, key) + driver.switch_to.new_window(WindowTypes.WINDOW) - driver.add_cookie({"name": key, "value": value}) + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) - # Test - cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + params = cookie_filter + result_after_switching_context = driver.storage.get_cookies(filter=params, partition=descriptor) - driver.switch_to.new_window(WindowTypes.WINDOW) + assert len(result_after_switching_context.cookies) > 0 + assert result_after_switching_context.cookies[0].value.value == value - descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + driver.switch_to.window(window_handle) - params = cookie_filter - result_after_switching_context = driver.storage.get_cookies(filter=params, partition=descriptor) + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) - assert len(result_after_switching_context.cookies) > 0 - assert result_after_switching_context.cookies[0].value.value == value + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) - driver.switch_to.window(window_handle) + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value + partition_key = result.partition_key - descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + assert partition_key.source_origin is not None + assert partition_key.user_context is not None + assert partition_key.user_context == "default" - result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + def test_get_cookie_in_a_user_context(self, driver, pages, webserver): + """Test getting a cookie in a user context.""" + assert_no_cookies_are_present(driver) - assert len(result.cookies) > 0 - assert result.cookies[0].value.value == value - partition_key = result.partition_key + user_context = driver.browser.create_user_context() + window_handle = driver.current_window_handle - assert partition_key.source_origin is not None - assert partition_key.user_context is not None - assert partition_key.user_context == "default" + key = generate_unique_key() + value = "set" + descriptor = StorageKeyPartitionDescriptor(user_context=user_context) -def test_get_cookie_in_a_user_context(driver, pages, webserver): - """Test getting a cookie in a user context.""" - assert_no_cookies_are_present(driver) + parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) - user_context = driver.browser.create_user_context() - window_handle = driver.current_window_handle + driver.storage.set_cookie(cookie=parameters, partition=descriptor) - key = generate_unique_key() - value = "set" + # Test + cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) - descriptor = StorageKeyPartitionDescriptor(user_context=user_context) + # Create a new window with the user context + new_window = driver.browsing_context.create(type=WindowTypes.TAB, user_context=user_context) - parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) + driver.switch_to.window(new_window) - driver.storage.set_cookie(cookie=parameters, partition=descriptor) + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) - # Test - cookie_filter = CookieFilter(name=key, value=BytesValue(BytesValue.TYPE_STRING, "set")) + assert len(result.cookies) > 0 + assert result.cookies[0].value.value == value + partition_key = result.partition_key - # Create a new window with the user context - new_window = driver.browsing_context.create(type=WindowTypes.TAB, user_context=user_context) + assert partition_key.user_context is not None + assert partition_key.user_context == user_context - driver.switch_to.window(new_window) + driver.switch_to.window(window_handle) - result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + browsing_context_partition_descriptor = BrowsingContextPartitionDescriptor(window_handle) - assert len(result.cookies) > 0 - assert result.cookies[0].value.value == value - partition_key = result.partition_key + result1 = driver.storage.get_cookies(filter=cookie_filter, partition=browsing_context_partition_descriptor) - assert partition_key.user_context is not None - assert partition_key.user_context == user_context + assert len(result1.cookies) == 0 - driver.switch_to.window(window_handle) + # Clean up + driver.browsing_context.close(new_window) + driver.browser.remove_user_context(user_context) - browsing_context_partition_descriptor = BrowsingContextPartitionDescriptor(window_handle) + def test_add_cookie(self, driver, pages, webserver): + """Test adding a cookie.""" + assert_no_cookies_are_present(driver) - result1 = driver.storage.get_cookies(filter=cookie_filter, partition=browsing_context_partition_descriptor) + key = generate_unique_key() + value = "foo" - assert len(result1.cookies) == 0 + parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) + assert_cookie_is_not_present_with_name(driver, key) - # Clean up - driver.browsing_context.close(new_window) - driver.browser.remove_user_context(user_context) + # Test + driver.storage.set_cookie(cookie=parameters) + # Verify + assert_cookie_has_value(driver, key, value) + driver.get(pages.url("simpleTest.html")) + assert_cookie_has_value(driver, key, value) -def test_add_cookie(driver, pages, webserver): - """Test adding a cookie.""" - assert_no_cookies_are_present(driver) + @pytest.mark.xfail_chrome + @pytest.mark.xfail_edge + def test_add_and_get_cookie(self, driver, pages, webserver): + """Test adding and getting a cookie with all parameters.""" + assert_no_cookies_are_present(driver) - key = generate_unique_key() - value = "foo" + value = BytesValue(BytesValue.TYPE_STRING, "cod") + domain = webserver.host - parameters = PartialCookie(key, BytesValue(BytesValue.TYPE_STRING, value), webserver.host) - assert_cookie_is_not_present_with_name(driver, key) + expiry = int(time.time() + 3600) - # Test - driver.storage.set_cookie(cookie=parameters) + path = "/simpleTest.html" - # Verify - assert_cookie_has_value(driver, key, value) - driver.get(pages.url("simpleTest.html")) - assert_cookie_has_value(driver, key, value) + cookie = PartialCookie( + "fish", value, domain, path=path, http_only=True, secure=False, same_site=SameSite.LAX, expiry=expiry + ) + # Test + driver.storage.set_cookie(cookie=cookie) -@pytest.mark.xfail_chrome -@pytest.mark.xfail_edge -def test_add_and_get_cookie(driver, pages, webserver): - """Test adding and getting a cookie with all parameters.""" - assert_no_cookies_are_present(driver) + driver.get(pages.url("simpleTest.html")) - value = BytesValue(BytesValue.TYPE_STRING, "cod") - domain = webserver.host + cookie_filter = CookieFilter( + name="fish", + value=value, + domain=domain, + path=path, + http_only=True, + secure=False, + same_site=SameSite.LAX, + expiry=expiry, + ) - expiry = int(time.time() + 3600) # 1 hour from now + descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) - path = "/simpleTest.html" + result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) + key = result.partition_key - cookie = PartialCookie( - "fish", value, domain, path=path, http_only=True, secure=False, same_site=SameSite.LAX, expiry=expiry - ) + # Verify + assert len(result.cookies) > 0 + result_cookie = result.cookies[0] - # Test - driver.storage.set_cookie(cookie=cookie) + assert result_cookie.name == "fish" + assert result_cookie.value.value == value.value + assert result_cookie.domain == domain + assert result_cookie.path == path + assert result_cookie.http_only is True + assert result_cookie.secure is False + assert result_cookie.same_site == SameSite.LAX + assert result_cookie.expiry == expiry + assert key.source_origin is not None + assert key.user_context is not None + assert key.user_context == "default" - driver.get(pages.url("simpleTest.html")) + @pytest.mark.xfail_edge + def test_get_all_cookies(self, driver, pages, webserver): + """Test getting all cookies.""" + assert_no_cookies_are_present(driver) - cookie_filter = CookieFilter( - name="fish", - value=value, - domain=domain, - path=path, - http_only=True, - secure=False, - same_site=SameSite.LAX, - expiry=expiry, - ) + key1 = generate_unique_key() + key2 = generate_unique_key() - descriptor = BrowsingContextPartitionDescriptor(driver.current_window_handle) + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_not_present_with_name(driver, key2) - result = driver.storage.get_cookies(filter=cookie_filter, partition=descriptor) - key = result.partition_key + # Test + params = CookieFilter() + result = driver.storage.get_cookies(filter=params) - # Verify - assert len(result.cookies) > 0 - result_cookie = result.cookies[0] + count_before = len(result.cookies) - assert result_cookie.name == "fish" - assert result_cookie.value.value == value.value - assert result_cookie.domain == domain - assert result_cookie.path == path - assert result_cookie.http_only is True - assert result_cookie.secure is False - assert result_cookie.same_site == SameSite.LAX - assert result_cookie.expiry == expiry - assert key.source_origin is not None - assert key.user_context is not None - assert key.user_context == "default" + driver.add_cookie({"name": key1, "value": "value"}) + driver.add_cookie({"name": key2, "value": "value"}) + driver.get(pages.url("simpleTest.html")) + result = driver.storage.get_cookies(filter=params) -@pytest.mark.xfail_edge -def test_get_all_cookies(driver, pages, webserver): - """Test getting all cookies.""" - assert_no_cookies_are_present(driver) + # Verify + assert len(result.cookies) == count_before + 2 + cookie_names = [cookie.name for cookie in result.cookies] + assert key1 in cookie_names + assert key2 in cookie_names - key1 = generate_unique_key() - key2 = generate_unique_key() + def test_delete_all_cookies(self, driver, pages, webserver): + """Test deleting all cookies.""" + assert_no_cookies_are_present(driver) - assert_cookie_is_not_present_with_name(driver, key1) - assert_cookie_is_not_present_with_name(driver, key2) + driver.add_cookie({"name": "foo", "value": "set"}) + assert_some_cookies_are_present(driver) - # Test - params = CookieFilter() - result = driver.storage.get_cookies(filter=params) + # Test + driver.storage.delete_cookies(filter=CookieFilter()) - count_before = len(result.cookies) + # Verify + assert_no_cookies_are_present(driver) - driver.add_cookie({"name": key1, "value": "value"}) - driver.add_cookie({"name": key2, "value": "value"}) + driver.get(pages.url("simpleTest.html")) + assert_no_cookies_are_present(driver) - driver.get(pages.url("simpleTest.html")) - result = driver.storage.get_cookies(filter=params) + def test_delete_cookie_with_name(self, driver, pages, webserver): + """Test deleting a cookie with a specific name.""" + assert_no_cookies_are_present(driver) - # Verify - assert len(result.cookies) == count_before + 2 - cookie_names = [cookie.name for cookie in result.cookies] - assert key1 in cookie_names - assert key2 in cookie_names + key1 = generate_unique_key() + key2 = generate_unique_key() + driver.add_cookie({"name": key1, "value": "set"}) + driver.add_cookie({"name": key2, "value": "set"}) -def test_delete_all_cookies(driver, pages, webserver): - """Test deleting all cookies.""" - assert_no_cookies_are_present(driver) + assert_cookie_is_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) - driver.add_cookie({"name": "foo", "value": "set"}) - assert_some_cookies_are_present(driver) + # Test + driver.storage.delete_cookies(filter=CookieFilter(name=key1)) - # Test - driver.storage.delete_cookies(filter=CookieFilter()) + # Verify + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) - # Verify - assert_no_cookies_are_present(driver) + driver.get(pages.url("simpleTest.html")) + assert_cookie_is_not_present_with_name(driver, key1) + assert_cookie_is_present_with_name(driver, key2) - driver.get(pages.url("simpleTest.html")) - assert_no_cookies_are_present(driver) + def test_add_cookies_with_different_paths(self, driver, pages, webserver): + """Test adding cookies with different paths that are related to ours.""" + assert_no_cookies_are_present(driver) + cookie1 = PartialCookie( + "fish", BytesValue(BytesValue.TYPE_STRING, "cod"), webserver.host, path="/simpleTest.html" + ) -def test_delete_cookie_with_name(driver, pages, webserver): - """Test deleting a cookie with a specific name.""" - assert_no_cookies_are_present(driver) + cookie2 = PartialCookie("planet", BytesValue(BytesValue.TYPE_STRING, "earth"), webserver.host, path="/") - key1 = generate_unique_key() - key2 = generate_unique_key() + # Test + driver.storage.set_cookie(cookie=cookie1) + driver.storage.set_cookie(cookie=cookie2) - driver.add_cookie({"name": key1, "value": "set"}) - driver.add_cookie({"name": key2, "value": "set"}) + driver.get(pages.url("simpleTest.html")) - assert_cookie_is_present_with_name(driver, key1) - assert_cookie_is_present_with_name(driver, key2) + # Verify + assert_cookie_is_present_with_name(driver, "fish") + assert_cookie_is_present_with_name(driver, "planet") - # Test - driver.storage.delete_cookies(filter=CookieFilter(name=key1)) - - # Verify - assert_cookie_is_not_present_with_name(driver, key1) - assert_cookie_is_present_with_name(driver, key2) - - driver.get(pages.url("simpleTest.html")) - assert_cookie_is_not_present_with_name(driver, key1) - assert_cookie_is_present_with_name(driver, key2) - - -def test_add_cookies_with_different_paths(driver, pages, webserver): - """Test adding cookies with different paths that are related to ours.""" - assert_no_cookies_are_present(driver) - - cookie1 = PartialCookie("fish", BytesValue(BytesValue.TYPE_STRING, "cod"), webserver.host, path="/simpleTest.html") - - cookie2 = PartialCookie("planet", BytesValue(BytesValue.TYPE_STRING, "earth"), webserver.host, path="/") - - # Test - driver.storage.set_cookie(cookie=cookie1) - driver.storage.set_cookie(cookie=cookie2) - - driver.get(pages.url("simpleTest.html")) - - # Verify - assert_cookie_is_present_with_name(driver, "fish") - assert_cookie_is_present_with_name(driver, "planet") - - driver.get(pages.url("formPage.html")) - assert_cookie_is_not_present_with_name(driver, "fish") + driver.get(pages.url("formPage.html")) + assert_cookie_is_not_present_with_name(driver, "fish")