Skip to content

Commit d640bda

Browse files
authored
Merge pull request #1794 from seleniumbase/better-commander
Improvements to SB Commander & More
2 parents 2421d21 + a5b5287 commit d640bda

File tree

9 files changed

+116
-58
lines changed

9 files changed

+116
-58
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
""" * Asserting that multiple elements are present or visible:
1+
"""Assert that multiple elements are present or visible:
22
HTML Presence: assert_elements_present()
33
HTML Visibility: assert_elements() <> assert_elements_visible()"""
44
from seleniumbase import BaseCase
55
BaseCase.main(__name__, __file__)
66

77

8-
class MyTestClass(BaseCase):
8+
class ListAssertTests(BaseCase):
99
def test_assert_list_of_elements(self):
1010
self.open("https://seleniumbase.io/demo_page")
1111
self.assert_elements_present("head", "style", "script")
1212
self.assert_elements("h1", "h2", "h3")
13-
my_list = ["#myDropdown", "#myButton", "#svgRect"]
13+
my_list = ["#myDropdown", "#myButton", "#mySlider"]
1414
self.assert_elements(my_list)

examples/test_override_driver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ def get_new_driver(self, *args, **kwargs):
2222
options.add_experimental_option("prefs", prefs)
2323
return webdriver.Chrome(options=options)
2424

25-
def test_simple(self):
25+
def test_driver_override(self):
2626
self.open("https://seleniumbase.io/demo_page")
2727
self.assert_text("Demo Page", "h1")

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
pip>=21.3.1;python_version<"3.7"
2-
pip>=22.3.1;python_version>="3.7"
2+
pip>=23.0.1;python_version>="3.7"
33
packaging>=21.3;python_version<"3.7"
44
packaging>=23.0;python_version>="3.7"
55
setuptools>=59.6.0;python_version<"3.7"
6-
setuptools>=65.7.0;python_version>="3.7"
6+
setuptools>=67.6.0;python_version>="3.7"
77
keyring>=23.4.1;python_version<"3.8"
88
keyring>=23.13.1;python_version>="3.8"
99
tomli>=1.2.3;python_version<"3.7"

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.13.8"
2+
__version__ = "4.13.9"

seleniumbase/behave/behave_sb.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,17 @@
7272
-D enable-ws (Enable Web Security on Chromium-based browsers.)
7373
-D enable-sync (Enable "Chrome Sync".)
7474
-D uc | -D undetected (Use undetected-chromedriver to evade bot-detection)
75-
-D uc-cdp-events (Capture CDP events when running in "--undetected" mode.)
75+
-D uc-cdp-events (Capture CDP events when running in "-D undetected" mode)
7676
-D remote-debug (Sync to Chrome Remote Debugger chrome://inspect/#devices)
7777
-D dashboard (Enable the SeleniumBase Dashboard. Saved at: dashboard.html)
7878
-D dash-title=STRING (Set the title shown for the generated dashboard.)
7979
-D enable-3d-apis (Enables WebGL and 3D APIs.)
80-
-D swiftshader (Use Chrome's "--use-gl=swiftshader" feature.)
80+
-D swiftshader (Use Chrome's SwiftShader Graphics Library.)
8181
-D incognito (Enable Chrome's Incognito mode.)
8282
-D guest (Enable Chrome's Guest mode.)
8383
-D devtools (Open Chrome's DevTools when the browser opens.)
84-
-D reuse-session | -D rs (Reuse browser session between tests.)
84+
-D reuse-session | -D rs (Reuse browser session for all tests.)
85+
-D reuse-class-session | -D rcs (Reuse session for tests in class/feature)
8586
-D crumbs (Delete all cookies between tests reusing a session.)
8687
-D disable-beforeunload (Disable the "beforeunload" event on Chrome.)
8788
-D window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
@@ -103,6 +104,7 @@
103104
from seleniumbase.core import log_helper
104105
from seleniumbase.core import download_helper
105106
from seleniumbase.core import proxy_helper
107+
from seleniumbase.core import session_helper
106108
from seleniumbase.fixtures import constants
107109
from seleniumbase import config as sb_config
108110

@@ -179,6 +181,7 @@ def get_configured_sb(context):
179181
sb.disable_gpu = False
180182
sb._multithreaded = False
181183
sb._reuse_session = False
184+
sb._reuse_class_session = False
182185
sb._crumbs = False
183186
sb._disable_beforeunload = False
184187
sb.visual_baseline = False
@@ -234,6 +237,7 @@ def get_configured_sb(context):
234237
sb_config._has_logs = None
235238
sb_config._has_exception = None
236239
sb_config.save_screenshot = None
240+
sb_config.reuse_class_session = None
237241

238242
browsers = set() # To error if selecting more than one
239243
valid_browsers = constants.ValidBrowsers.valid_browsers
@@ -541,6 +545,13 @@ def get_configured_sb(context):
541545
if low_key in ["rs", "reuse-session", "reuse_session"]:
542546
sb._reuse_session = True
543547
continue
548+
# Handle: -D rcs / rfs / reuse-class-session / reuse-feature-session
549+
if low_key in [
550+
"rcs", "rfs", "reuse-class-session", "reuse-feature-session"
551+
]:
552+
sb._reuse_session = True
553+
sb._reuse_class_session = True
554+
continue
544555
# Handle: -D crumbs
545556
if low_key == "crumbs":
546557
sb._crumbs = True
@@ -870,6 +881,7 @@ def get_configured_sb(context):
870881
sb_config.window_size = sb.window_size
871882
sb_config.maximize_option = sb.maximize_option
872883
sb_config.xvfb = sb.xvfb
884+
sb_config.reuse_class_session = sb._reuse_class_session
873885
sb_config.save_screenshot = sb.save_screenshot_after_test
874886
sb_config.no_screenshot = sb.no_screenshot_after_test
875887
sb_config._has_logs = False
@@ -1256,6 +1268,7 @@ def before_all(context):
12561268

12571269
def before_feature(context, feature):
12581270
sb_config.behave_feature = feature
1271+
session_helper.end_reused_class_session_as_needed()
12591272

12601273

12611274
def before_scenario(context, scenario):
@@ -1290,6 +1303,7 @@ def after_scenario(context, scenario):
12901303

12911304
def after_feature(context, feature):
12921305
sb_config.feature = feature
1306+
session_helper.end_reused_class_session_as_needed()
12931307

12941308

12951309
def after_all(context):

seleniumbase/console_scripts/sb_behave_gui.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ def do_behave_run(
9191
if selected_tests[test_number].get():
9292
full_run_command += " "
9393
test_to_run = test
94-
if test.startswith("(GROUP) "):
95-
test_to_run = test.split("(GROUP) ")[1]
94+
if test.startswith("(GROUP) "):
95+
test_to_run = test.split("(GROUP) ")[1]
9696
full_run_command += test_to_run.split(" => ")[0]
9797
else:
9898
full_run_command += test.split(" => ")[0]
@@ -108,6 +108,10 @@ def do_behave_run(
108108
full_run_command += " -D rs"
109109
elif "(-D rs -D crumbs)" in rs_string:
110110
full_run_command += " -D rs -D crumbs"
111+
elif "(-D rcs)" in rs_string:
112+
full_run_command += " -D rcs"
113+
elif "(-D rcs -D crumbs)" in rs_string:
114+
full_run_command += " -D rcs -D crumbs"
111115

112116
if quiet_mode:
113117
full_run_command += " --quiet"
@@ -173,7 +177,7 @@ def do_behave_run(
173177
send_window_to_front(root)
174178

175179

176-
def create_tkinter_gui(tests, command_string):
180+
def create_tkinter_gui(tests, command_string, t_count, f_count, s_tests):
177181
root = tk.Tk()
178182
root.title("SeleniumBase Behave Commander | GUI for Behave")
179183
root.minsize(820, 656)
@@ -193,8 +197,10 @@ def create_tkinter_gui(tests, command_string):
193197

194198
options_list = [
195199
"New Session Per Test (Default)",
196-
"Reuse Session for all tests (-D rs)",
197-
"Reuse Session / clear cookies (-D rs -D crumbs)",
200+
"Reuse Session for ALL the tests (-D rs)",
201+
"Reuse Session and clear cookies (-D rs -D crumbs)",
202+
"Reuse Session in the SAME class/feature (-D rcs)",
203+
"Reuse Session in class and clear cookies (-D rcs -D crumbs)",
198204
]
199205
rsx = tk.StringVar(root)
200206
rsx.set(options_list[2])
@@ -239,13 +245,17 @@ def create_tkinter_gui(tests, command_string):
239245
chk.pack()
240246

241247
tk.Label(root, text="").pack()
248+
plural = "s"
249+
if f_count == 1:
250+
plural = ""
242251
run_display = (
243-
"Select from %s tests: "
244-
"(If NO TESTS are selected, then ALL TESTS will run)"
245-
% len(tests)
252+
"Select from %s rows (%s feature%s with %s scenarios): "
253+
"(All tests will run if none are selected)"
254+
% (len(tests), f_count, plural, t_count)
246255
)
247-
if len(tests) == 1:
248-
run_display = "Only ONE TEST was found: (Will run automatically)"
256+
if t_count == 1:
257+
run_display = "Only ONE TEST was found and will be run:"
258+
tests = s_tests
249259
tk.Label(root, text=run_display, fg="blue").pack()
250260
text_area = ScrolledText(
251261
root, width=100, height=12, wrap="word", state=tk.DISABLED
@@ -399,6 +409,7 @@ def main():
399409
file_scenario_count = {}
400410
f_count = 0
401411
s_count = 0
412+
t_count = 0
402413
if is_windows:
403414
output = output.decode("latin1")
404415
else:
@@ -436,13 +447,14 @@ def main():
436447
feature_name = feature_name.split(" # ")[-1]
437448
s_count = file_scenario_count[str(f_count)]
438449
filename = filename.strip()
439-
t_name = "(GROUP) %s => %s" % (filename, feature_name)
440-
t_name += " <> (%s Total)" % s_count
450+
t_name = "(GROUP) %s => %s" % (filename, feature_name)
451+
t_name += " <> (%s Total)" % s_count
441452
f_tests.append(t_name)
442453
elif (
443454
row.startswith(" Scenario: ")
444455
or row.startswith(" Scenario Outline: ")
445456
):
457+
t_count += 1
446458
line_num = row.split(":")[-1]
447459
scenario_name = None
448460
if row.startswith(" Scenario: "):
@@ -470,7 +482,7 @@ def main():
470482
print(error_msg)
471483
return
472484

473-
create_tkinter_gui(tests, command_string)
485+
create_tkinter_gui(tests, command_string, t_count, f_count, s_tests)
474486

475487

476488
if __name__ == "__main__":

seleniumbase/console_scripts/sb_commander.py

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Launches SeleniumBase Commander | GUI for pytest.
1919
"""
2020
import colorama
21+
import os
2122
import subprocess
2223
import sys
2324

@@ -78,6 +79,14 @@ def do_pytest_run(
7879
save_screenshots,
7980
additional_options,
8081
):
82+
cleaned_tests = []
83+
for test in tests:
84+
if test.startswith("(FILE) "):
85+
clean_test = test.split("(FILE) ")[1].split(" => ")[0]
86+
cleaned_tests.append(clean_test)
87+
else:
88+
cleaned_tests.append(test)
89+
tests = cleaned_tests
8190
total_tests = len(tests)
8291
total_selected_tests = 0
8392
for selected_test in selected_tests:
@@ -111,13 +120,25 @@ def do_pytest_run(
111120
full_run_command += " --rs"
112121
elif "(--rs --crumbs)" in rs_string:
113122
full_run_command += " --rs --crumbs"
123+
elif "(--rcs)" in rs_string:
124+
full_run_command += " --rcs"
125+
elif "(--rcs --crumbs)" in rs_string:
126+
full_run_command += " --rcs --crumbs"
114127

115128
if "(-n=2)" in thread_string:
116129
full_run_command += " -n=2"
117130
elif "(-n=3)" in thread_string:
118131
full_run_command += " -n=3"
119132
elif "(-n=4)" in thread_string:
120133
full_run_command += " -n=4"
134+
elif "(-n=5)" in thread_string:
135+
full_run_command += " -n=5"
136+
elif "(-n=6)" in thread_string:
137+
full_run_command += " -n=6"
138+
elif "(-n=7)" in thread_string:
139+
full_run_command += " -n=7"
140+
elif "(-n=8)" in thread_string:
141+
full_run_command += " -n=8"
121142

122143
if demo_mode:
123144
full_run_command += " --demo"
@@ -159,7 +180,7 @@ def do_pytest_run(
159180
send_window_to_front(root)
160181

161182

162-
def create_tkinter_gui(tests, command_string):
183+
def create_tkinter_gui(tests, command_string, files, solo_tests):
163184
root = tk.Tk()
164185
root.title("SeleniumBase Commander | GUI for pytest")
165186
root.minsize(820, 658)
@@ -179,8 +200,10 @@ def create_tkinter_gui(tests, command_string):
179200

180201
options_list = [
181202
"New Session Per Test (Default)",
182-
"Reuse Session for all tests in thread (--rs)",
183-
"Reuse Session / clear cookies (--rs --crumbs)",
203+
"Reuse Session for ALL tests in thread (--rs)",
204+
"Reuse Session and also clear cookies (--rs --crumbs)",
205+
"Reuse Session for tests with same CLASS (--rcs)",
206+
"Reuse Session for class and clear cookies (--rcs --crumbs)",
184207
]
185208
rsx = tk.StringVar(root)
186209
rsx.set(options_list[2])
@@ -193,6 +216,15 @@ def create_tkinter_gui(tests, command_string):
193216
"Number of Threads: 3 (-n=3)",
194217
"Number of Threads: 4 (-n=4)",
195218
]
219+
try:
220+
if int(os.cpu_count()) >= 8:
221+
options_list.append("Number of Threads: 5 (-n=5)")
222+
options_list.append("Number of Threads: 6 (-n=6)")
223+
options_list.append("Number of Threads: 7 (-n=7)")
224+
options_list.append("Number of Threads: 8 (-n=8)")
225+
except Exception:
226+
pass
227+
196228
ntx = tk.StringVar(root)
197229
ntx.set(options_list[0])
198230
question_menu = tk.OptionMenu(root, ntx, *options_list)
@@ -244,13 +276,17 @@ def create_tkinter_gui(tests, command_string):
244276
chk.pack()
245277

246278
tk.Label(root, text="").pack()
279+
plural = "s"
280+
if len(files) == 1:
281+
plural = ""
247282
run_display = (
248-
"Select from %s tests: "
249-
"(If NO TESTS are selected, then ALL TESTS will run)"
250-
% len(tests)
283+
"Select from %s rows (%s file%s with %s tests): "
284+
"(All tests will run if none are selected)"
285+
% (len(tests), len(files), plural, len(solo_tests))
251286
)
252-
if len(tests) == 1:
253-
run_display = "Only ONE TEST was found: (Will run automatically)"
287+
if len(solo_tests) == 1:
288+
run_display = "Only ONE TEST was found and will be run:"
289+
tests = solo_tests
254290
tk.Label(root, text=run_display, fg="blue").pack()
255291
text_area = ScrolledText(
256292
root, width=100, height=12, wrap="word", state=tk.DISABLED
@@ -403,8 +439,25 @@ def main():
403439
error_msg = c5 + "ERROR: " + error_msg + cr
404440
print(error_msg)
405441
return
406-
407-
create_tkinter_gui(tests, command_string)
442+
groups = []
443+
for row in tests:
444+
if row.count("::") >= 1:
445+
g_name = "(FILE) %s" % row.split("::")[0]
446+
groups.append(g_name)
447+
files = []
448+
used_files = []
449+
for row in groups:
450+
if row not in used_files:
451+
used_files.append(row)
452+
plural = "s"
453+
if groups.count(row) == 1:
454+
plural = ""
455+
f_row = "%s => (%s Test%s)" % (row, groups.count(row), plural)
456+
files.append(f_row)
457+
solo_tests = tests
458+
tests = [*files, *tests]
459+
460+
create_tkinter_gui(tests, command_string, files, solo_tests)
408461

409462

410463
if __name__ == "__main__":

0 commit comments

Comments
 (0)