Skip to content

Commit 5a2d259

Browse files
authored
[py][bidi]: add bidi command session.status and encapsulate in Session class (#15615)
* add bidi command session status * use Session class for session module * make webdriver bidi session private
1 parent 019f7c5 commit 5a2d259

File tree

4 files changed

+107
-30
lines changed

4 files changed

+107
-30
lines changed

py/selenium/webdriver/common/bidi/script.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
from dataclasses import dataclass
1919
from typing import List
2020

21-
from .session import session_subscribe
22-
from .session import session_unsubscribe
21+
from .session import Session
2322

2423

2524
class Script:
@@ -43,12 +42,14 @@ def remove_console_message_handler(self, id):
4342

4443
def _subscribe_to_log_entries(self):
4544
if not self.log_entry_subscribed:
46-
self.conn.execute(session_subscribe(LogEntryAdded.event_class))
45+
session = Session(self.conn)
46+
self.conn.execute(session.subscribe(LogEntryAdded.event_class))
4747
self.log_entry_subscribed = True
4848

4949
def _unsubscribe_from_log_entries(self):
5050
if self.log_entry_subscribed and LogEntryAdded.event_class not in self.conn.callbacks:
51-
self.conn.execute(session_unsubscribe(LogEntryAdded.event_class))
51+
session = Session(self.conn)
52+
self.conn.execute(session.unsubscribe(LogEntryAdded.event_class))
5253
self.log_entry_subscribed = False
5354

5455
def _handle_log_entry(self, type, handler):

py/selenium/webdriver/common/bidi/session.py

+37-26
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,43 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
from selenium.webdriver.common.bidi.common import command_builder
1819

19-
def session_subscribe(*events, browsing_contexts=None):
20-
cmd_dict = {
21-
"method": "session.subscribe",
22-
"params": {
20+
21+
class Session:
22+
23+
def __init__(self, conn):
24+
self.conn = conn
25+
26+
def subscribe(self, *events, browsing_contexts=None):
27+
params = {
2328
"events": events,
24-
},
25-
}
26-
if browsing_contexts is None:
27-
browsing_contexts = []
28-
if browsing_contexts:
29-
cmd_dict["params"]["browsingContexts"] = browsing_contexts
30-
_ = yield cmd_dict
31-
return None
32-
33-
34-
def session_unsubscribe(*events, browsing_contexts=None):
35-
cmd_dict = {
36-
"method": "session.unsubscribe",
37-
"params": {
29+
}
30+
if browsing_contexts is None:
31+
browsing_contexts = []
32+
if browsing_contexts:
33+
params["browsingContexts"] = browsing_contexts
34+
return command_builder("session.subscribe", params)
35+
36+
def unsubscribe(self, *events, browsing_contexts=None):
37+
params = {
3838
"events": events,
39-
},
40-
}
41-
if browsing_contexts is None:
42-
browsing_contexts = []
43-
if browsing_contexts:
44-
cmd_dict["params"]["browsingContexts"] = browsing_contexts
45-
_ = yield cmd_dict
46-
return None
39+
}
40+
if browsing_contexts is None:
41+
browsing_contexts = []
42+
if browsing_contexts:
43+
params["browsingContexts"] = browsing_contexts
44+
return command_builder("session.unsubscribe", params)
45+
46+
def status(self):
47+
"""
48+
The session.status command returns information about the remote end's readiness
49+
to create new sessions and may include implementation-specific metadata.
50+
51+
Returns
52+
-------
53+
dict
54+
Dictionary containing the ready state (bool), message (str) and metadata
55+
"""
56+
cmd = command_builder("session.status", {})
57+
return self.conn.execute(cmd)

py/selenium/webdriver/remote/webdriver.py

+15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from selenium.webdriver.common.bidi.browser import Browser
4747
from selenium.webdriver.common.bidi.network import Network
4848
from selenium.webdriver.common.bidi.script import Script
49+
from selenium.webdriver.common.bidi.session import Session
4950
from selenium.webdriver.common.by import By
5051
from selenium.webdriver.common.options import ArgOptions
5152
from selenium.webdriver.common.options import BaseOptions
@@ -262,6 +263,7 @@ def __init__(
262263
self._script = None
263264
self._network = None
264265
self._browser = None
266+
self._bidi_session = None
265267

266268
def __repr__(self):
267269
return f'<{type(self).__module__}.{type(self).__name__} (session="{self.session_id}")>'
@@ -1277,6 +1279,19 @@ def browser(self):
12771279

12781280
return self._browser
12791281

1282+
@property
1283+
def _session(self):
1284+
"""
1285+
Returns the BiDi session object for the current WebDriver session.
1286+
"""
1287+
if not self._websocket_connection:
1288+
self._start_bidi()
1289+
1290+
if self._bidi_session is None:
1291+
self._bidi_session = Session(self._websocket_connection)
1292+
1293+
return self._bidi_session
1294+
12801295
def _get_cdp_details(self):
12811296
import json
12821297

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Licensed to the Software Freedom Conservancy (SFC) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The SFC licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
import pytest
19+
20+
from selenium.webdriver.common.window import WindowTypes
21+
22+
23+
@pytest.mark.xfail_safari
24+
def test_session_status(driver):
25+
result = driver._session.status()
26+
assert result is not None
27+
assert "ready" in result
28+
assert "message" in result
29+
assert isinstance(result["ready"], bool)
30+
assert isinstance(result["message"], str)
31+
32+
33+
@pytest.mark.xfail_safari
34+
def test_session_status_not_closed_with_one_window(driver):
35+
# initial session status
36+
initial_status = driver._session.status()
37+
assert initial_status is not None
38+
39+
# Open new window and tab
40+
driver.switch_to.new_window(WindowTypes.WINDOW)
41+
driver.switch_to.new_window(WindowTypes.TAB)
42+
43+
# Close one window
44+
driver.close()
45+
46+
# Session should still be active
47+
status_after_closing = driver._session.status()
48+
assert status_after_closing is not None
49+
assert "ready" in status_after_closing
50+
assert "message" in status_after_closing

0 commit comments

Comments
 (0)