364 lines
11 KiB
Python
364 lines
11 KiB
Python
"""
|
|
** recorder **
|
|
|
|
Launches the SeleniumBase Recorder Desktop App.
|
|
|
|
Usage:
|
|
seleniumbase recorder [OPTIONS]
|
|
sbase recorder [OPTIONS]
|
|
|
|
Options:
|
|
--uc / --undetected (Use undetectable mode.)
|
|
--cdp (Same as "--uc" and "--undetectable".)
|
|
--behave (Also output Behave/Gherkin files.)
|
|
|
|
Output:
|
|
Launches the SeleniumBase Recorder Desktop App.
|
|
"""
|
|
import colorama
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tkinter as tk
|
|
from seleniumbase import config as sb_config
|
|
from seleniumbase.fixtures import page_utils
|
|
from seleniumbase.fixtures import shared_utils
|
|
from tkinter import messagebox
|
|
|
|
sb_config.rec_subprocess_p = None
|
|
sb_config.rec_subprocess_used = False
|
|
sys_executable = sys.executable
|
|
if " " in sys_executable:
|
|
sys_executable = "python"
|
|
if sys.version_info <= (3, 8):
|
|
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
|
|
raise Exception(
|
|
"\n* Recorder Desktop 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 = ""
|
|
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.LIGHTRED_EX + colorama.Back.LIGHTYELLOW_EX
|
|
c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX
|
|
cr = colorama.Style.RESET_ALL
|
|
return c0, c1, c2, c3, c4, cr
|
|
|
|
|
|
def send_window_to_front(window):
|
|
window.lift()
|
|
window.attributes("-topmost", True)
|
|
window.after_idle(window.attributes, "-topmost", False)
|
|
|
|
|
|
def show_already_recording_warning():
|
|
messagebox.showwarning(
|
|
"SeleniumBase Recorder: Already Running!",
|
|
"Please finalize the active recording from the terminal\n"
|
|
'where you opened the Recorder: Type "c" and hit Enter.',
|
|
)
|
|
|
|
|
|
def file_name_error(file_name):
|
|
error_msg = None
|
|
if not file_name.endswith(".py"):
|
|
error_msg = 'File name must end with ".py"!'
|
|
elif "*" in file_name or len(str(file_name)) < 4:
|
|
error_msg = "Invalid file name!"
|
|
elif file_name.startswith("-"):
|
|
error_msg = 'File name cannot start with "-"!'
|
|
elif "/" in str(file_name) or "\\" in str(file_name):
|
|
error_msg = "File must be created in the current directory!"
|
|
return error_msg
|
|
|
|
|
|
def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
|
|
poll = None
|
|
if sb_config.rec_subprocess_used:
|
|
poll = sb_config.rec_subprocess_p.poll()
|
|
if not sb_config.rec_subprocess_used or poll is not None:
|
|
pass
|
|
else:
|
|
show_already_recording_warning()
|
|
send_window_to_front(window)
|
|
poll = sb_config.rec_subprocess_p.poll()
|
|
if poll is None:
|
|
return
|
|
|
|
file_name = file_name.strip()
|
|
error_msg = file_name_error(file_name)
|
|
if error_msg:
|
|
messagebox.showwarning(
|
|
"Invalid filename", "Invalid filename: %s" % error_msg
|
|
)
|
|
return
|
|
|
|
url = url.strip()
|
|
if not page_utils.is_valid_url(url):
|
|
if page_utils.is_valid_url("https://" + url):
|
|
url = "https://" + url
|
|
if not page_utils.is_valid_url(url):
|
|
messagebox.showwarning(
|
|
"Invalid URL", "Enter a valid URL! (Eg. seleniumbase.io)"
|
|
)
|
|
else:
|
|
if os.path.exists(os.getcwd() + "/" + file_name):
|
|
if not overwrite_enabled:
|
|
msgbox = tk.messagebox.askquestion(
|
|
"Overwrite?",
|
|
'Are you sure you want to overwrite "%s"?' % file_name,
|
|
icon="warning",
|
|
)
|
|
if msgbox == "yes":
|
|
os.remove(file_name)
|
|
else:
|
|
tk.messagebox.showinfo("Cancelled", "Recording Cancelled!")
|
|
return
|
|
else:
|
|
os.remove(file_name)
|
|
add_on = ""
|
|
command_args = sys.argv[2:]
|
|
if (
|
|
"--rec-behave" in command_args
|
|
or "--behave" in command_args
|
|
or "--gherkin" in command_args
|
|
):
|
|
add_on = " --rec-behave"
|
|
command = (
|
|
"%s -m seleniumbase mkrec %s --url=%s --gui"
|
|
% (sys_executable, file_name, url)
|
|
)
|
|
if '"' not in url:
|
|
command = (
|
|
'%s -m seleniumbase mkrec %s --url="%s" --gui'
|
|
% (sys_executable, file_name, url)
|
|
)
|
|
elif "'" not in url:
|
|
command = (
|
|
"%s -m seleniumbase mkrec %s --url='%s' --gui"
|
|
% (sys_executable, file_name, url)
|
|
)
|
|
if not use_chrome:
|
|
command += " --edge"
|
|
if (
|
|
"--uc" in command_args
|
|
or "--cdp" in command_args
|
|
or "--undetected" in command_args
|
|
or "--undetectable" in command_args
|
|
):
|
|
command += " --uc"
|
|
if "--ee" in command_args:
|
|
command += " --ee"
|
|
command += add_on
|
|
poll = None
|
|
if sb_config.rec_subprocess_used:
|
|
poll = sb_config.rec_subprocess_p.poll()
|
|
if not sb_config.rec_subprocess_used or poll is not None:
|
|
sb_config.rec_subprocess_p = subprocess.Popen(command, shell=True)
|
|
sb_config.rec_subprocess_used = True
|
|
else:
|
|
show_already_recording_warning()
|
|
send_window_to_front(window)
|
|
|
|
|
|
def do_playback(file_name, use_chrome, window, demo_mode=False):
|
|
file_name = file_name.strip()
|
|
error_msg = file_name_error(file_name)
|
|
if error_msg:
|
|
messagebox.showwarning(
|
|
"Invalid filename", "Invalid filename: %s" % error_msg
|
|
)
|
|
return
|
|
if not os.path.exists(os.getcwd() + "/" + file_name):
|
|
messagebox.showwarning(
|
|
"File does not exist",
|
|
'File "%s" does not exist in the current directory!' % file_name,
|
|
)
|
|
return
|
|
command = "%s -m pytest %s -q -s" % (sys_executable, file_name)
|
|
if shared_utils.is_linux():
|
|
command += " --gui"
|
|
if not use_chrome:
|
|
command += " --edge"
|
|
if demo_mode:
|
|
command += " --demo"
|
|
command_args = sys.argv[2:]
|
|
if (
|
|
"--uc" in command_args
|
|
or "--cdp" in command_args
|
|
or "--undetected" in command_args
|
|
or "--undetectable" in command_args
|
|
):
|
|
command += " --uc"
|
|
poll = None
|
|
if sb_config.rec_subprocess_used:
|
|
poll = sb_config.rec_subprocess_p.poll()
|
|
if not sb_config.rec_subprocess_used or poll is not None:
|
|
print(command)
|
|
subprocess.Popen(command, shell=True)
|
|
else:
|
|
messagebox.showwarning(
|
|
"SeleniumBase Recorder: Already Running!",
|
|
"Please finalize the active recording from the terminal\n"
|
|
'where you opened the Recorder: Type "c" and hit Enter.',
|
|
)
|
|
send_window_to_front(window)
|
|
|
|
|
|
def create_tkinter_gui():
|
|
default_file_name = "new_recording.py"
|
|
window = tk.Tk()
|
|
window.title("SeleniumBase Recorder App")
|
|
window.geometry("344x388")
|
|
frame = tk.Frame(window)
|
|
frame.pack()
|
|
|
|
tk.Label(window, text="").pack()
|
|
fname = tk.StringVar(value=default_file_name)
|
|
tk.Label(window, text="Enter filename to save recording as:").pack()
|
|
entry = tk.Entry(window, textvariable=fname)
|
|
entry.pack()
|
|
cbx = tk.IntVar()
|
|
chk = tk.Checkbutton(window, text="Overwrite existing files", variable=cbx)
|
|
chk.pack()
|
|
chk.select()
|
|
cbb = tk.IntVar()
|
|
chkb = tk.Checkbutton(window, text="Use Chrome over Edge", variable=cbb)
|
|
chkb.pack()
|
|
chkb.select()
|
|
tk.Label(window, text="").pack()
|
|
url = tk.StringVar()
|
|
tk.Label(window, text="Enter the URL to start recording on:").pack()
|
|
entry = tk.Entry(window, textvariable=url)
|
|
entry.pack()
|
|
entry.focus()
|
|
entry.bind(
|
|
"<Return>",
|
|
(
|
|
lambda _: do_recording(
|
|
fname.get(), url.get(), cbx.get(), cbb.get(), window
|
|
)
|
|
),
|
|
)
|
|
tk.Button(
|
|
window,
|
|
text="Record",
|
|
fg="red",
|
|
command=lambda: do_recording(
|
|
fname.get(), url.get(), cbx.get(), cbb.get(), window
|
|
),
|
|
).pack()
|
|
tk.Label(window, text="").pack()
|
|
tk.Label(window, text="Playback recording (Normal Mode):").pack()
|
|
tk.Button(
|
|
window,
|
|
text="Playback",
|
|
fg="green",
|
|
command=lambda: do_playback(fname.get(), cbb.get(), window),
|
|
).pack()
|
|
tk.Label(window, text="").pack()
|
|
tk.Label(window, text="Playback recording (Demo Mode):").pack()
|
|
try:
|
|
tk.Button(
|
|
window,
|
|
text="Playback (Demo Mode)",
|
|
fg="teal",
|
|
command=lambda: do_playback(
|
|
fname.get(), cbb.get(), window, demo_mode=True
|
|
),
|
|
).pack()
|
|
except Exception:
|
|
tk.Button(
|
|
window,
|
|
text="Playback (Demo Mode)",
|
|
fg="blue",
|
|
command=lambda: do_playback(
|
|
fname.get(), cbb.get(), window, demo_mode=True
|
|
),
|
|
).pack()
|
|
|
|
# Bring form window to front
|
|
send_window_to_front(window)
|
|
# 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
|
|
window.mainloop()
|
|
end_program()
|
|
|
|
|
|
def recorder_still_running():
|
|
poll = None
|
|
if sb_config.rec_subprocess_used:
|
|
try:
|
|
poll = sb_config.rec_subprocess_p.poll()
|
|
except Exception:
|
|
return False
|
|
else:
|
|
return False
|
|
if poll is not None:
|
|
return False
|
|
return True
|
|
|
|
|
|
def show_still_running_warning():
|
|
"""Give the user a chance to end the recording safely via the
|
|
pytest pdb Debug Mode so that processes such as chromedriver
|
|
and Python don't remain open and hanging in the background."""
|
|
messagebox.showwarning(
|
|
"SeleniumBase Recorder: Still Running!",
|
|
"Please finalize the active recording from the terminal\n"
|
|
'where you opened the Recorder: Type "c" and hit Enter.\n'
|
|
"(Then you can safely close this alert.)",
|
|
)
|
|
|
|
|
|
def end_program():
|
|
if recorder_still_running():
|
|
show_still_running_warning()
|
|
|
|
|
|
def main():
|
|
use_colors = True
|
|
if shared_utils.is_linux():
|
|
use_colors = False
|
|
c0, c1, c2, c3, c4, cr = set_colors(use_colors)
|
|
message = ""
|
|
message += c2
|
|
message += "*"
|
|
message += c4
|
|
message += " Starting the "
|
|
message += c0
|
|
message += "Selenium"
|
|
message += c1
|
|
message += "Base"
|
|
message += c2
|
|
message += " "
|
|
message += c3
|
|
message += "Recorder"
|
|
message += c4
|
|
message += " Desktop App"
|
|
message += c2
|
|
message += "..."
|
|
message += cr
|
|
print(message)
|
|
create_tkinter_gui()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print('To open the Recorder Desktop App: "seleniumbase recorder"')
|