diff --git a/script/selenium/__init__.py b/script/selenium/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/script/selenium/darkreader-firefox.xpi b/script/selenium/darkreader-firefox.xpi new file mode 100644 index 0000000..a89e099 Binary files /dev/null and b/script/selenium/darkreader-firefox.xpi differ diff --git a/script/selenium/install.md b/script/selenium/install.md new file mode 100644 index 0000000..448a05d --- /dev/null +++ b/script/selenium/install.md @@ -0,0 +1,5 @@ +sudo apt install libcairo2-dev python3-dev pkg-config libxt-dev libgirepository1.0-dev + +pip install pycairo PyGObject + +https://github.com/szemate/pyselenium-video-recorder diff --git a/script/selenium/odoo_debug-4.0.xpi b/script/selenium/odoo_debug-4.0.xpi new file mode 100644 index 0000000..588206f Binary files /dev/null and b/script/selenium/odoo_debug-4.0.xpi differ diff --git a/script/selenium/selenium_video.py b/script/selenium/selenium_video.py new file mode 100644 index 0000000..a0ea83c --- /dev/null +++ b/script/selenium/selenium_video.py @@ -0,0 +1,113 @@ +"""Video recorder for Selenium WebDriver.""" + +import threading + +import gi + +gi.require_version("Gst", "1.0") +from gi.repository import Gst + +Gst.init(None) + + +class VideoRecorder(object): + """Record video of a Selenium test run in WebM format through repeated + screenshot captures. + + :type driver: selenium.webdriver.Remote + :param filename: Path of the video file to create, or `None` to view live + video output. + :param framerate: Rate of screenshot capture as fraction [fps]. + :param width: Crop screenshots to this width [pixels]. + :param height: Crop screenshots to this height [pixels]. + """ + + def __init__( # pylint: disable=too-many-arguments + self, + driver, + filename="video.webm", + framerate="25/1", + width=None, + height=None, + ): + self.driver = driver + _patch_driver(self.driver) + + if filename and not filename.endswith(".webm"): + filename += ".webm" + + caps = ["video/x-raw", "framerate={}".format(framerate)] + if width: + caps.append("width={}".format(width)) + if height: + caps.append("height={}".format(height)) + + elements = [ + "appsrc name=src is-live=true do-timestamp=true caps=image/png", + "pngdec", + "videoconvert", + "videorate", + "videocrop name=crop", + ",".join(caps), + "queue", + ] + if filename: + elements += [ + "vp8enc", + "webmmux", + "filesink location={}".format(filename), + ] + else: + elements.append("autovideosink sync=false") + self._pipeline = Gst.parse_launch(" ! ".join(elements)) + + self._appsrc = self._pipeline.get_by_name("src") + self._appsrc.connect("need-data", self._push_screenshot) + + videocrop = self._pipeline.get_by_name("crop") + if width: + videocrop.set_property("right", -1) # Auto-crop + if height: + videocrop.set_property("bottom", -1) # Auto-crop + + def start(self): + """Start video recording.""" + self._pipeline.set_state(Gst.State.PLAYING) + + def stop(self): + """Stop video recording.""" + self._appsrc.emit("end-of-stream") + self._pipeline.set_state(Gst.State.NULL) + + def _push_screenshot(self, *_): + """Emit a WebDriver screenshot from appsrc.""" + self._appsrc.emit( + "push-buffer", + Gst.Buffer.new_wrapped(self.driver.get_screenshot_as_png()), + ) + + +def VideoViewer(*args, **kwargs): + """Shorthand for `VideoRecorder(filename=None)`. + + Useful with PhantomJS. + """ + kwargs["filename"] = None + return VideoRecorder(*args, **kwargs) + + +def _patch_driver(driver): + """Prevent failures caused by concurrent connections to the remote + WebDriver. + + Another option would be to use a WebDriver proxy. Monkey patching `driver` + is simpler, though more hacky. + """ + lock = threading.Lock() + execute_unsafe = driver.execute + + def execute_safe(driver_command, params=None): + with lock: + return execute_unsafe(driver_command, params) + + driver.execute = execute_safe diff --git a/script/selenium/web_login.py b/script/selenium/web_login.py new file mode 100755 index 0000000..f5d2ba6 --- /dev/null +++ b/script/selenium/web_login.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from selenium import webdriver +from selenium.webdriver.common.by import By + +# Configuration pour lancer Firefox en mode de navigation privée +firefox_options = webdriver.FirefoxOptions() +firefox_options.add_argument("--private") + +# Définissez la préférence pour les notifications de bureau +# 1 signifie "autoriser", 2 signifie "bloquer" +firefox_options.set_preference("permissions.default.desktop-notification", 1) + +# Créez une instance du navigateur Firefox avec les options de navigation privée +driver = webdriver.Firefox(options=firefox_options) + +# Ouvrez la page web +driver.get("http://127.0.0.1:8069/web") + +# Trouvez les éléments du formulaire +courriel_input = driver.find_element(By.NAME, "login") +mot_de_passe_input = driver.find_element(By.NAME, "password") +try: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/div/div/form/div[3]/button" + # '//button[contains(text(), "Log in")]' + ) +except Exception: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/main/div/form/div[3]/button" + # '//button[contains(text(), "Connexion")]' + ) + +# Remplissez le courriel et le mot de passe +courriel_input.send_keys("admin") +mot_de_passe_input.send_keys("admin") + +# Cliquez sur le bouton "Connexion" +connexion_button.click() + +# Fermez le navigateur +# driver.quit() diff --git a/script/selenium/web_login_open_me_devops.py b/script/selenium/web_login_open_me_devops.py new file mode 100755 index 0000000..3c9b6e8 --- /dev/null +++ b/script/selenium/web_login_open_me_devops.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +private_mode = True +record_mode = False +dark_reader_mode = True + + +def click(driver, xpath, time=5): + wait = WebDriverWait(driver, time) + button = wait.until( + EC.element_to_be_clickable( + ( + By.XPATH, + xpath, + ) + ) + ) + button.click() + + +if record_mode: + new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") + ) + sys.path.append(new_path) + from script.selenium.selenium_video import VideoRecorder + +# Configuration pour lancer Firefox en mode de navigation privée +firefox_options = webdriver.FirefoxOptions() +if private_mode: + firefox_options.add_argument("--private") + +# Définissez la préférence pour les notifications de bureau +# 1 signifie "autoriser", 2 signifie "bloquer" +firefox_options.set_preference("permissions.default.desktop-notification", 1) + +# Créez une instance du navigateur Firefox avec les options de navigation privée +driver = webdriver.Firefox(options=firefox_options) + +# Ajout de l'enregistrement +if record_mode: + video_recorder = VideoRecorder(driver) +else: + video_recorder = None + +# Install DarkReader to help my eyes +# TODO do a script to check if it's the last version +if dark_reader_mode: + driver.install_addon( + "./script/selenium/darkreader-firefox.xpi", temporary=True + ) + driver.install_addon( + "./script/selenium/odoo_debug-4.0.xpi", temporary=True + ) + +if private_mode and dark_reader_mode: + driver.get("about:addons") + # Enable Dark Reader into incognito + click(driver, "/html/body/div/div[1]/categories-box/button[2]") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + + # Enable Odoo_debug into incognito + # Back + click(driver, "/html/body/div/div[2]/addon-page-header/div/div[2]/button") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + +# Ouvrez la page web +driver.get("http://127.0.0.1:8069/web") + +# Close tab opening by DarkReader +if not private_mode and dark_reader_mode: + driver.switch_to.window(driver.window_handles[-1]) + driver.close() + driver.switch_to.window(driver.window_handles[0]) + +# Démarrer l'enregistrement +if record_mode: + video_recorder.start() + +# Trouvez les éléments du formulaire +courriel_input = driver.find_element(By.NAME, "login") +mot_de_passe_input = driver.find_element(By.NAME, "password") +try: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/div/div/form/div[3]/button" + # '//button[contains(text(), "Log in")]' + ) +except Exception: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/main/div/form/div[3]/button" + # '//button[contains(text(), "Connexion")]' + ) + +# Remplissez le courriel et le mot de passe +courriel_input.send_keys("admin") +mot_de_passe_input.send_keys("admin") + +# Cliquez sur le bouton "Connexion" +connexion_button.click() + +# Attendez que l'élément soit cliquable +wait = WebDriverWait(driver, 5) +# menu_toggle = wait.until( +# EC.element_to_be_clickable((By.XPATH, "/html/body/header/nav/ul[1]/li/a")) +# ) +# +# # Cliquez sur l'élément +# menu_toggle.click() +# +# # Attendez que le lien soit cliquable +# menu_item = wait.until( +# EC.element_to_be_clickable( +# ( +# By.XPATH, +# "/html/body/header/nav/ul[1]/li/div/a[2]", +# ) +# ) +# ) + +# Open View +click(driver, "/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[2]/a") +click(driver, "/html/body/div[1]/main/div[2]/div/div/table/tbody/tr[1]/td[2]") + +# Arrêter l'enregistrement +if record_mode: + video_recorder.stop() + +# Open conversation chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/header/nav/ul[3]/li[2]/a') +# conversation_button.click() + +# Close bot chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/div[3]/div[1]/span[2]/a[2]') +# conversation_button.click() + +# Fermez le navigateur +# driver.quit() diff --git a/script/selenium/web_login_open_me_devops_auto.py b/script/selenium/web_login_open_me_devops_auto.py new file mode 100755 index 0000000..23bcf6f --- /dev/null +++ b/script/selenium/web_login_open_me_devops_auto.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +private_mode = True +record_mode = False +dark_reader_mode = True + + +def click(driver, xpath, time=5): + wait = WebDriverWait(driver, time) + button = wait.until( + EC.element_to_be_clickable( + ( + By.XPATH, + xpath, + ) + ) + ) + button.click() + + +if record_mode: + new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") + ) + sys.path.append(new_path) + from script.selenium.selenium_video import VideoRecorder + +# Configuration pour lancer Firefox en mode de navigation privée +firefox_options = webdriver.FirefoxOptions() +if private_mode: + firefox_options.add_argument("--private") + +# Définissez la préférence pour les notifications de bureau +# 1 signifie "autoriser", 2 signifie "bloquer" +firefox_options.set_preference("permissions.default.desktop-notification", 1) + +# Créez une instance du navigateur Firefox avec les options de navigation privée +driver = webdriver.Firefox(options=firefox_options) + +# Ajout de l'enregistrement +if record_mode: + video_recorder = VideoRecorder(driver) +else: + video_recorder = None + +# Install DarkReader to help my eyes +# TODO do a script to check if it's the last version +if dark_reader_mode: + driver.install_addon( + "./script/selenium/darkreader-firefox.xpi", temporary=True + ) + driver.install_addon( + "./script/selenium/odoo_debug-4.0.xpi", temporary=True + ) + +if private_mode and dark_reader_mode: + driver.get("about:addons") + click(driver, "/html/body/div/div[1]/categories-box/button[2]") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + + # Enable Odoo_debug into incognito + # Back + click(driver, "/html/body/div/div[2]/addon-page-header/div/div[2]/button") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + +# Ouvrez la page web +driver.get("http://127.0.0.1:8069/web") + +# Close tab opening by DarkReader +if not private_mode and dark_reader_mode: + driver.switch_to.window(driver.window_handles[-1]) + driver.close() + driver.switch_to.window(driver.window_handles[0]) + +# Démarrer l'enregistrement +if record_mode: + video_recorder.start() + +# Trouvez les éléments du formulaire +courriel_input = driver.find_element(By.NAME, "login") +mot_de_passe_input = driver.find_element(By.NAME, "password") +try: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/div/div/form/div[3]/button" + # '//button[contains(text(), "Log in")]' + ) +except Exception: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/main/div/form/div[3]/button" + # '//button[contains(text(), "Connexion")]' + ) + +# Remplissez le courriel et le mot de passe +courriel_input.send_keys("admin") +mot_de_passe_input.send_keys("admin") + +# Cliquez sur le bouton "Connexion" +connexion_button.click() + +# Open View +click(driver, "/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[2]/a") +click(driver, "/html/body/div[1]/main/div[2]/div/div/table/tbody/tr[1]/td[2]") +click(driver, "/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[2]/a") +click(driver, "/html/body/div[1]/main/div[2]/div/div/table/tbody/tr[1]/td[2]") + +# CG self +# Bouton modifier +click(driver, "/html/body/div[1]/main/div[1]/div[2]/div/div/div[1]/button[1]") +# Tab CG +click(driver, "/html/body/div[1]/main/div[2]/div/div/div[7]/ul/li[3]/a") +# Disable «Stop Execution if Env Not Clean +# click( +# driver, +# "/html/body/div[1]/main/div[2]/div/div/div[7]/div/div[3]/div[2]/table[2]/tbody/tr/td[1]/label", +# ) +# Gen +click( + driver, + "/html/body/div[1]/main/div[2]/div/div/div[7]/div/div[3]/div[2]/table[1]/tbody/tr[2]/td[1]/button", +) + +# Check error +click(driver, "/html/body/div[1]/main/div[2]/div/div/div[7]/ul/li[14]/a") +# click(driver, "/html/body/div[1]/main/div[2]/div/div/div[6]/div/div[14]/div/div[2]/table/tbody/tr[1]", time=60*2) + +# Arrêter l'enregistrement +if record_mode: + video_recorder.stop() + +# Open conversation chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/header/nav/ul[3]/li[2]/a') +# conversation_button.click() + +# Close bot chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/div[3]/div[1]/span[2]/a[2]') +# conversation_button.click() + +# Fermez le navigateur +# driver.quit() diff --git a/script/selenium/web_login_open_me_devops_auto_force.py b/script/selenium/web_login_open_me_devops_auto_force.py new file mode 100755 index 0000000..8dbb7cd --- /dev/null +++ b/script/selenium/web_login_open_me_devops_auto_force.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +private_mode = True +record_mode = False +dark_reader_mode = True + + +def click(driver, xpath, time=5): + wait = WebDriverWait(driver, time) + button = wait.until( + EC.element_to_be_clickable( + ( + By.XPATH, + xpath, + ) + ) + ) + button.click() + + +if record_mode: + new_path = os.path.normpath( + os.path.join(os.path.dirname(__file__), "..", "..") + ) + sys.path.append(new_path) + from script.selenium.selenium_video import VideoRecorder + +# Configuration pour lancer Firefox en mode de navigation privée +firefox_options = webdriver.FirefoxOptions() +if private_mode: + firefox_options.add_argument("--private") + +# Définissez la préférence pour les notifications de bureau +# 1 signifie "autoriser", 2 signifie "bloquer" +firefox_options.set_preference("permissions.default.desktop-notification", 1) + +# Créez une instance du navigateur Firefox avec les options de navigation privée +driver = webdriver.Firefox(options=firefox_options) + +# Ajout de l'enregistrement +if record_mode: + video_recorder = VideoRecorder(driver) +else: + video_recorder = None + +# Install DarkReader to help my eyes +# TODO do a script to check if it's the last version +if dark_reader_mode: + driver.install_addon( + "./script/selenium/darkreader-firefox.xpi", temporary=True + ) + driver.install_addon( + "./script/selenium/odoo_debug-4.0.xpi", temporary=True + ) + +if private_mode and dark_reader_mode: + driver.get("about:addons") + click(driver, "/html/body/div/div[1]/categories-box/button[2]") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + + # Enable Odoo_debug into incognito + # Back + click(driver, "/html/body/div/div[2]/addon-page-header/div/div[2]/button") + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/div/div/div/button", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/addon-options/panel-list/panel-item[5]", + ) + click( + driver, + "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", + ) + +# Ouvrez la page web +driver.get("http://127.0.0.1:8069/web") + +# Close tab opening by DarkReader +if not private_mode and dark_reader_mode: + driver.switch_to.window(driver.window_handles[-1]) + driver.close() + driver.switch_to.window(driver.window_handles[0]) + +# Démarrer l'enregistrement +if record_mode: + video_recorder.start() + +# Trouvez les éléments du formulaire +courriel_input = driver.find_element(By.NAME, "login") +mot_de_passe_input = driver.find_element(By.NAME, "password") +try: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/div/div/form/div[3]/button" + # '//button[contains(text(), "Log in")]' + ) +except Exception: + connexion_button = driver.find_element( + By.XPATH, + "/html/body/div/main/div/form/div[3]/button" + # '//button[contains(text(), "Connexion")]' + ) + +# Remplissez le courriel et le mot de passe +courriel_input.send_keys("admin") +mot_de_passe_input.send_keys("admin") + +# Cliquez sur le bouton "Connexion" +connexion_button.click() + +# Open View +click(driver, "/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[2]/a") +click(driver, "/html/body/div[1]/main/div[2]/div/div/table/tbody/tr[1]/td[2]") +click(driver, "/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[2]/a") +click(driver, "/html/body/div[1]/main/div[2]/div/div/table/tbody/tr[1]/td[2]") + +# CG self +# Bouton modifier +click(driver, "/html/body/div[1]/main/div[1]/div[2]/div/div/div[1]/button[1]") +# Tab CG +click(driver, "/html/body/div[1]/main/div[2]/div/div/div[7]/ul/li[3]/a") +# Disable «Stop Execution if Env Not Clean +click( + driver, + "/html/body/div[1]/main/div[2]/div/div/div[7]/div/div[3]/div[2]/table[2]/tbody/tr/td[1]/label", +) +# Gen +click( + driver, + "/html/body/div[1]/main/div[2]/div/div/div[7]/div/div[3]/div[2]/table[1]/tbody/tr[2]/td[1]/button", +) + +# Check error +click(driver, "/html/body/div[1]/main/div[2]/div/div/div[7]/ul/li[14]/a") +# click(driver, "/html/body/div[1]/main/div[2]/div/div/div[6]/div/div[14]/div/div[2]/table/tbody/tr[1]", time=60*2) + +# Arrêter l'enregistrement +if record_mode: + video_recorder.stop() + +# Open conversation chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/header/nav/ul[3]/li[2]/a') +# conversation_button.click() + +# Close bot chat +# conversation_button = driver.find_element(By.XPATH, '/html/body/div[3]/div[1]/span[2]/a[2]') +# conversation_button.click() + +# Fermez le navigateur +# driver.quit()