475 lines
13 KiB
Python
475 lines
13 KiB
Python
"""
|
|
Launches SeleniumBase Commander | GUI for pytest.
|
|
|
|
Usage:
|
|
seleniumbase commander [OPTIONAL PATH or TEST FILE]
|
|
sbase commander [OPTIONAL PATH or TEST FILE]
|
|
seleniumbase gui [OPTIONAL PATH or TEST FILE]
|
|
sbase gui [OPTIONAL PATH or TEST FILE]
|
|
|
|
Examples:
|
|
sbase gui
|
|
sbase gui -k agent
|
|
sbase gui -m marker2
|
|
sbase gui test_suite.py
|
|
sbase gui offline_examples/
|
|
|
|
Output:
|
|
Launches SeleniumBase Commander | GUI for pytest.
|
|
"""
|
|
import colorama
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tkinter as tk
|
|
from seleniumbase.fixtures import shared_utils
|
|
from tkinter.scrolledtext import ScrolledText
|
|
|
|
if sys.version_info <= (3, 8):
|
|
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
|
|
raise Exception(
|
|
"\n* SBase Commander requires Python 3.8 or newer!"
|
|
"\n** You are currently using Python %s" % current_version
|
|
)
|
|
|
|
|
|
def set_colors(use_colors):
|
|
c0 = ""
|
|
c1 = ""
|
|
c2 = ""
|
|
c3 = ""
|
|
c4 = ""
|
|
c5 = ""
|
|
cr = ""
|
|
if use_colors:
|
|
c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX
|
|
c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX
|
|
c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX
|
|
c3 = colorama.Fore.BLACK + colorama.Back.LIGHTGREEN_EX
|
|
c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX
|
|
c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX
|
|
cr = colorama.Style.RESET_ALL
|
|
return c0, c1, c2, c3, c4, c5, cr
|
|
|
|
|
|
def send_window_to_front(root):
|
|
root.lift()
|
|
root.attributes("-topmost", True)
|
|
root.after_idle(root.attributes, "-topmost", False)
|
|
|
|
|
|
def do_pytest_run(
|
|
root,
|
|
tests,
|
|
selected_tests,
|
|
command_string,
|
|
browser_string,
|
|
rs_string,
|
|
thread_string,
|
|
verbose,
|
|
demo_mode,
|
|
mobile_mode,
|
|
dashboard,
|
|
html_report,
|
|
headless,
|
|
save_screenshots,
|
|
additional_options,
|
|
):
|
|
cleaned_tests = []
|
|
for test in tests:
|
|
if test.startswith("(FILE) "):
|
|
clean_test = test.split("(FILE) ")[1].split(" => ")[0]
|
|
cleaned_tests.append(clean_test)
|
|
else:
|
|
cleaned_tests.append(test)
|
|
tests = cleaned_tests
|
|
total_tests = len(tests)
|
|
total_selected_tests = 0
|
|
for selected_test in selected_tests:
|
|
if selected_tests[selected_test].get():
|
|
total_selected_tests += 1
|
|
|
|
full_run_command = '"%s" -m pytest' % sys.executable
|
|
if total_selected_tests == 0 or total_tests == total_selected_tests:
|
|
if command_string:
|
|
full_run_command += " "
|
|
full_run_command += command_string
|
|
else:
|
|
for test_number, test in enumerate(tests):
|
|
if selected_tests[test_number].get():
|
|
full_run_command += " "
|
|
if ' ' not in test:
|
|
full_run_command += test
|
|
elif '"' not in test:
|
|
full_run_command += '"%s"' % test
|
|
else:
|
|
full_run_command += test.replace(" ", "\\ ")
|
|
|
|
if "(--edge)" in browser_string:
|
|
full_run_command += " --edge"
|
|
elif "(--firefox)" in browser_string:
|
|
full_run_command += " --firefox"
|
|
elif "(--safari)" in browser_string:
|
|
full_run_command += " --safari"
|
|
|
|
if "(--rs)" in rs_string:
|
|
full_run_command += " --rs"
|
|
elif "(--rs --crumbs)" in rs_string:
|
|
full_run_command += " --rs --crumbs"
|
|
elif "(--rcs)" in rs_string:
|
|
full_run_command += " --rcs"
|
|
elif "(--rcs --crumbs)" in rs_string:
|
|
full_run_command += " --rcs --crumbs"
|
|
|
|
if "(-n=2)" in thread_string:
|
|
full_run_command += " -n=2"
|
|
elif "(-n=3)" in thread_string:
|
|
full_run_command += " -n=3"
|
|
elif "(-n=4)" in thread_string:
|
|
full_run_command += " -n=4"
|
|
elif "(-n=5)" in thread_string:
|
|
full_run_command += " -n=5"
|
|
elif "(-n=6)" in thread_string:
|
|
full_run_command += " -n=6"
|
|
elif "(-n=7)" in thread_string:
|
|
full_run_command += " -n=7"
|
|
elif "(-n=8)" in thread_string:
|
|
full_run_command += " -n=8"
|
|
|
|
if demo_mode:
|
|
full_run_command += " --demo"
|
|
|
|
if mobile_mode:
|
|
full_run_command += " --mobile"
|
|
|
|
if dashboard:
|
|
full_run_command += " --dashboard"
|
|
|
|
if html_report:
|
|
full_run_command += " --html=report.html"
|
|
|
|
if headless:
|
|
full_run_command += " --headless"
|
|
elif shared_utils.is_linux():
|
|
full_run_command += " --gui"
|
|
|
|
if save_screenshots:
|
|
full_run_command += " --screenshot"
|
|
|
|
capture_needed = False
|
|
if "--capture" not in additional_options:
|
|
capture_needed = True
|
|
|
|
additional_options = additional_options.strip()
|
|
if additional_options:
|
|
full_run_command += " "
|
|
full_run_command += additional_options
|
|
|
|
if verbose:
|
|
full_run_command += " -v"
|
|
|
|
if capture_needed:
|
|
full_run_command += " --capture=tee-sys"
|
|
|
|
print(full_run_command)
|
|
subprocess.Popen(full_run_command, shell=True)
|
|
send_window_to_front(root)
|
|
|
|
|
|
def create_tkinter_gui(tests, command_string, files, solo_tests):
|
|
root = tk.Tk()
|
|
root.title("SeleniumBase Commander | GUI for pytest")
|
|
if shared_utils.is_windows():
|
|
root.minsize(820, 696)
|
|
else:
|
|
root.minsize(820, 702)
|
|
tk.Label(root, text="").pack()
|
|
|
|
options_list = [
|
|
"Use Chrome Browser (Default)",
|
|
"Use Edge Browser (--edge)",
|
|
"Use Firefox Browser (--firefox)",
|
|
]
|
|
if shared_utils.is_mac():
|
|
options_list.append("Use Safari Browser (--safari)")
|
|
brx = tk.StringVar(root)
|
|
brx.set(options_list[0])
|
|
question_menu = tk.OptionMenu(root, brx, *options_list)
|
|
question_menu.pack()
|
|
|
|
options_list = [
|
|
"New Session Per Test (Default)",
|
|
"Reuse Session for ALL tests in thread (--rs)",
|
|
"Reuse Session and also clear cookies (--rs --crumbs)",
|
|
"Reuse Session for tests with same CLASS (--rcs)",
|
|
"Reuse Session for class and clear cookies (--rcs --crumbs)",
|
|
]
|
|
rsx = tk.StringVar(root)
|
|
rsx.set(options_list[0])
|
|
question_menu = tk.OptionMenu(root, rsx, *options_list)
|
|
question_menu.pack()
|
|
|
|
options_list = [
|
|
"Number of Threads: 1 (Default)",
|
|
"Number of Threads: 2 (-n=2)",
|
|
"Number of Threads: 3 (-n=3)",
|
|
"Number of Threads: 4 (-n=4)",
|
|
]
|
|
try:
|
|
if int(os.cpu_count()) >= 8:
|
|
options_list.append("Number of Threads: 5 (-n=5)")
|
|
options_list.append("Number of Threads: 6 (-n=6)")
|
|
options_list.append("Number of Threads: 7 (-n=7)")
|
|
options_list.append("Number of Threads: 8 (-n=8)")
|
|
except Exception:
|
|
pass
|
|
|
|
ntx = tk.StringVar(root)
|
|
ntx.set(options_list[0])
|
|
question_menu = tk.OptionMenu(root, ntx, *options_list)
|
|
question_menu.pack()
|
|
|
|
vox = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Verbose Output (-v)", variable=vox, pady=0
|
|
)
|
|
chk.pack()
|
|
chk.select()
|
|
|
|
dmx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Demo Mode (--demo)", variable=dmx, pady=0
|
|
)
|
|
chk.pack()
|
|
|
|
mmx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Mobile Mode (--mobile)", variable=mmx, pady=0
|
|
)
|
|
chk.pack()
|
|
|
|
dbx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Dashboard (--dashboard)", variable=dbx, pady=0
|
|
)
|
|
chk.pack()
|
|
chk.select()
|
|
|
|
hrx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Report (--html=report.html)", variable=hrx, pady=0
|
|
)
|
|
chk.pack()
|
|
chk.select()
|
|
|
|
hbx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Headless Browser (--headless)", variable=hbx, pady=0
|
|
)
|
|
chk.pack()
|
|
|
|
ssx = tk.IntVar()
|
|
chk = tk.Checkbutton(
|
|
root, text="Save Screenshots (--screenshot)", variable=ssx, pady=0
|
|
)
|
|
chk.pack()
|
|
|
|
tk.Label(root, text="").pack()
|
|
run_display = (
|
|
"Select from %s rows (%s files with %s tests): "
|
|
"(All tests will run if none are selected)"
|
|
% (len(tests), len(files), len(solo_tests))
|
|
)
|
|
if len(solo_tests) == 1:
|
|
run_display = "Only ONE TEST was found and will be run:"
|
|
tests = solo_tests
|
|
elif len(files) == 1:
|
|
run_display = (
|
|
"Select from %s tests: "
|
|
"(All tests will run if none are selected)"
|
|
% (len(solo_tests))
|
|
)
|
|
tests = solo_tests
|
|
tk.Label(root, text=run_display, bg="yellow", fg="magenta").pack()
|
|
text_area = ScrolledText(
|
|
root, width=100, height=12, wrap="word", state=tk.DISABLED
|
|
)
|
|
text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
|
count = 0
|
|
ara = {}
|
|
for row in tests:
|
|
row += " " * 200
|
|
ara[count] = tk.IntVar()
|
|
cb = None
|
|
if shared_utils.is_windows():
|
|
cb = tk.Checkbutton(
|
|
text_area,
|
|
text=(row),
|
|
bg="white",
|
|
fg="black",
|
|
anchor="w",
|
|
pady=0,
|
|
borderwidth=1,
|
|
highlightthickness=1,
|
|
variable=ara[count],
|
|
)
|
|
else:
|
|
cb = tk.Checkbutton(
|
|
text_area,
|
|
text=(row),
|
|
bg="white",
|
|
fg="black",
|
|
anchor="w",
|
|
pady=0,
|
|
variable=ara[count],
|
|
)
|
|
text_area.window_create("end", window=cb)
|
|
text_area.insert("end", "\n")
|
|
count += 1
|
|
|
|
tk.Label(root, text="").pack()
|
|
additional_options = ""
|
|
aopts = tk.StringVar(value=additional_options)
|
|
tk.Label(
|
|
root,
|
|
text='Additional "pytest" Options: (Eg. "--incognito --slow")',
|
|
bg="yellow", fg="blue",
|
|
).pack()
|
|
entry = tk.Entry(root, textvariable=aopts)
|
|
entry.pack()
|
|
entry.focus()
|
|
entry.bind(
|
|
"<Return>",
|
|
(
|
|
lambda _: do_pytest_run(
|
|
root,
|
|
tests,
|
|
ara,
|
|
command_string,
|
|
brx.get(),
|
|
rsx.get(),
|
|
ntx.get(),
|
|
vox.get(),
|
|
dmx.get(),
|
|
mmx.get(),
|
|
dbx.get(),
|
|
hrx.get(),
|
|
hbx.get(),
|
|
ssx.get(),
|
|
aopts.get(),
|
|
)
|
|
),
|
|
)
|
|
tk.Button(
|
|
root,
|
|
text="Run Selected Tests",
|
|
fg="green",
|
|
command=lambda: do_pytest_run(
|
|
root,
|
|
tests,
|
|
ara,
|
|
command_string,
|
|
brx.get(),
|
|
rsx.get(),
|
|
ntx.get(),
|
|
vox.get(),
|
|
dmx.get(),
|
|
mmx.get(),
|
|
dbx.get(),
|
|
hrx.get(),
|
|
hbx.get(),
|
|
ssx.get(),
|
|
aopts.get(),
|
|
),
|
|
).pack()
|
|
tk.Label(root, text="\n").pack()
|
|
|
|
# Bring form window to front
|
|
send_window_to_front(root)
|
|
# Use decoy to set correct focus on main window
|
|
decoy = tk.Tk()
|
|
decoy.geometry("1x1")
|
|
decoy.iconify()
|
|
decoy.update()
|
|
decoy.deiconify()
|
|
decoy.destroy()
|
|
# Start tkinter
|
|
root.mainloop()
|
|
|
|
|
|
def main():
|
|
use_colors = True
|
|
if shared_utils.is_linux():
|
|
use_colors = False
|
|
c0, c1, c2, c3, c4, c5, cr = set_colors(use_colors)
|
|
command_args = sys.argv[2:]
|
|
command_string = " ".join(command_args)
|
|
message = ""
|
|
message += c2
|
|
message += "*"
|
|
message += c4
|
|
message += " Starting the "
|
|
message += c0
|
|
message += "Selenium"
|
|
message += c1
|
|
message += "Base"
|
|
message += c2
|
|
message += " "
|
|
message += c3
|
|
message += "Commander"
|
|
message += c4
|
|
message += " Desktop App"
|
|
message += c2
|
|
message += "..."
|
|
message += cr
|
|
print(message)
|
|
|
|
proc = subprocess.Popen(
|
|
'"%s" -m pytest --collect-only -q --rootdir="./" %s'
|
|
% (sys.executable, command_string),
|
|
stdout=subprocess.PIPE,
|
|
shell=True,
|
|
)
|
|
(output, error) = proc.communicate()
|
|
if error:
|
|
error_msg = "Error collecting tests: %s" % str(error)
|
|
error_msg = c5 + error_msg + cr
|
|
print(error_msg)
|
|
return
|
|
tests = []
|
|
if shared_utils.is_windows():
|
|
output = output.decode("latin1")
|
|
else:
|
|
output = output.decode("utf-8")
|
|
for row in output.replace("\r", "").split("\n"):
|
|
if ("::") in row:
|
|
tests.append(row)
|
|
if not tests:
|
|
error_msg = "No tests found! Exiting SeleniumBase Commander..."
|
|
error_msg = c5 + "ERROR: " + error_msg + cr
|
|
print(error_msg)
|
|
return
|
|
groups = []
|
|
for row in tests:
|
|
if row.count("::") >= 1:
|
|
g_name = "(FILE) %s" % row.split("::")[0]
|
|
groups.append(g_name)
|
|
files = []
|
|
used_files = []
|
|
for row in groups:
|
|
if row not in used_files:
|
|
used_files.append(row)
|
|
plural = "s"
|
|
if groups.count(row) == 1:
|
|
plural = ""
|
|
f_row = "%s => (%s Test%s)" % (row, groups.count(row), plural)
|
|
files.append(f_row)
|
|
solo_tests = tests
|
|
tests = [*files, *tests]
|
|
|
|
create_tkinter_gui(tests, command_string, files, solo_tests)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print('To open SBase Commander, type "sbase commander" or "sbase gui"')
|