Skip to content

julienmerconsulting/KatalonPlaywrightEdition

Repository files navigation

Java 11+ Selenium 4 License

🚀 WebOperationsManager

Le framework Selenium qui rivalise avec Playwright
API unifiée, robuste et sans dépendances framework

FeaturesInstallationQuick StartAPI ReferenceExamplesvs Playwright


📋 Description

WebOperationsManager est un gestionnaire d'opérations Web haute performance basé sur Selenium 4. Conçu dans l'esprit "Clean QA", il offre une API simple, lisible et sans héritage superflu.

Philosophie

  • Pas d'abstraction inutile — Chaque méthode fait exactement ce qu'elle dit
  • Pas de couche framework — Fonctionne partout : Java pur, Katalon, TestNG, JUnit, Cucumber
  • Auto-logging intégré — Chaque action logge son résultat
  • Résilience native — Retry, waits explicites, fallbacks JavaScript automatiques
  • Compatible React/SPA — Injection d'events natifs pour les frameworks JS modernes

⭐ Features

Catégorie Fonctionnalités
Navigation navigateTo, refreshPage, navigateBack, navigateForward
Clicks clickOn (avec fallback JS), clickJS, clickWithRetry, doubleClickOn, rightClickOn, hoverOn
Formulaires typeText, typeTextForReact, clearField, uploadFile, pasteText
Waits waitForElementVisible, waitForTextInElement, waitForAjaxComplete, waitForAttributeContains
Extraction getText, getAttribute, getAllTexts, getElementCount, isElementDisplayed
Dropdowns selectByVisibleText, selectByValue, selectByIndex, getAllSelectOptions
Tables clickTableCellByRowTextAndColumnHeader, extractTableData, findRowByText
Iframes switchToIframe, switchToDefaultContent, switchToParentFrame
Shadow DOM getShadowElement, clickShadowElement
Fenêtres switchToWindowByTitle, openNewTab, closeOtherWindows
Alerts acceptAlert, dismissAlert, getAlertText, typeInAlert
Scroll scrollToElement, scrollToTop, scrollToBottom, scrollUntilElementVisible
Drag & Drop dragAndDrop, dragAndDropByOffset, dragAndDropJS
Screenshots captureScreenshot, captureElementScreenshot, highlightElement
Cookies addCookie, getCookie, deleteAllCookies
Storage getLocalStorage, setLocalStorage, clearSessionStorage
Performance getPageLoadTime, getPerformanceMetrics, measureElementRenderTime
JavaScript executeJS, executeAsyncJS, setAttributeJS, injectCSS
CDP/DevTools executeCdpCommand, blockUrls, getConsoleLogs, hasJavaScriptErrors
Retry executeWithRetry, retryClick, retryUntilElementVisible

📦 Installation

Prérequis

  • Java 11+
  • Selenium 4.x
  • ChromeDriver (ou autre WebDriver)

Maven

<dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.27.0</version>
    </dependency>
</dependencies>

Gradle

implementation 'org.seleniumhq.selenium:selenium-java:4.27.0'

Installation manuelle

  1. Copier WebOperationsManager.java dans votre projet
  2. Adapter le package si nécessaire
  3. C'est prêt ! 🎉

🚀 Quick Start

Exemple minimal

import com.ars.global.utils.WebOperationsManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

// Créer un driver Chrome optimisé
WebDriver driver = WebOperationsManager.createOptimizedChromeDriver(false); // false = visible
WebOperationsManager web = new WebOperationsManager(driver);

try {
    // Navigation
    web.navigateTo("https://example.com");
    
    // Formulaire
    web.typeText(By.id("username"), "julien");
    web.typeText(By.id("password"), "secret");
    web.clickOn(By.id("login"));
    
    // Vérification
    web.waitForTextInElement(By.cssSelector(".welcome"), "Bienvenue", 10);
    
} finally {
    web.driverquit();
}

Avec Katalon Studio

import com.kms.katalon.core.webui.driver.DriverFactory
import com.ars.global.utils.WebOperationsManager
import org.openqa.selenium.By

// Utiliser le driver Katalon existant
def web = new WebOperationsManager(DriverFactory.getWebDriver())

web.clickOn(By.id("loginBtn"))
web.typeText(By.name("username"), "julien")
web.waitForElementVisible(By.cssSelector(".dashboard"))

📚 API Reference

🔧 Construction & Initialisation

// Constructeur avec driver existant
WebOperationsManager web = new WebOperationsManager(driver);

// Factory method pour Chrome optimisé
WebDriver driver = WebOperationsManager.createOptimizedChromeDriver(false); // headless = true/false

Configuration automatique :

  • Timeouts : 10s (default), 30s (long)
  • Polling : 500ms
  • ImplicitWait désactivé (tout est explicite)

🌐 Navigation

Méthode Description
navigateTo(String url) Navigue vers une URL avec attente du chargement
refreshPage() Recharge la page
navigateBack() Retour arrière
navigateForward() Avance
getCurrentUrl() Retourne l'URL actuelle
getPageTitle() Retourne le titre de la page
web.navigateTo("https://www.saucedemo.com/");
String url = web.getCurrentUrl();
web.refreshPage();

🖱️ Clicks

Méthode Description
clickOn(By locator) Click standard avec fallback JS automatique
clickJS(By locator) Click JavaScript (contourne overlays)
clickWithRetry(By locator, int maxAttempts) Click avec retry automatique
clickWithOffset(By locator, int x, int y) Click avec offset depuis le centre
doubleClickOn(By locator) Double click
rightClickOn(By locator) Click droit (context menu)
clickIfExists(By locator, int timeout) Click si l'élément existe (ne plante pas)
hoverOn(By locator) Survol d'un élément
// Click intelligent avec fallback automatique
web.clickOn(By.id("submit"));

// Pour les overlays ou éléments cachés
web.clickJS(By.cssSelector(".hidden-button"));

// Click avec retry pour DOM dynamique
web.clickWithRetry(By.id("dynamic-btn"), 3);

💡 Le clickOn() est intelligent :

  1. Essaie un click standard
  2. Si échec → fallback JavaScript
  3. Pour les <a> et <button> → vérifie si navigation a eu lieu
  4. Pour les type="submit" → force le submit si nécessaire

⌨️ Saisie de texte

Méthode Description
typeText(By locator, String text) Saisie avec injection JS (marche partout)
typeTextForReact(By locator, String text) Saisie optimisée React/Vue/Angular
typeTextWithEnter(By locator, String text) Saisie + touche ENTER
pasteText(By locator, String text) Injection directe (rapide pour longs textes)
clearField(By locator) Vide un champ (avec fallback JS)
uploadFile(By locator, String filePath) Upload de fichier
uploadMultipleFiles(By locator, String... paths) Upload multiple
// Fonctionne sur tous les frameworks JS
web.typeText(By.id("email"), "julien@example.com");

// Pour les apps React avec state management
web.typeTextForReact(By.id("password"), "secret");

// Upload
web.uploadFile(By.id("fileInput"), "/path/to/file.pdf");

💡 typeText() utilise l'injection native :

// Contourne les problèmes React/Vue/Angular
nativeInputValueSetter.call(element, value);
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));

⏳ Waits & Synchronisation

Méthode Description
waitForElementPresent(By locator) Attend présence dans DOM
waitForElementVisible(By locator) Attend visibilité
waitForElementClickable(By locator) Attend que l'élément soit cliquable
waitForPageLoad() Attend document.readyState === 'complete'
waitForTextInElement(By locator, String text, int timeout) Attend un texte spécifique
waitForAttributeContains(By locator, String attr, String value, int timeout) Attend valeur d'attribut
waitForTextChange(By locator, String oldText, int timeout) Attend changement de texte
waitForElementToDisappear(By locator, int timeout) Attend disparition
waitForNumberOfElements(By locator, int count, int timeout) Attend nombre exact d'éléments
waitForAjaxComplete() Attend fin des appels AJAX (jQuery)
waitForStaleness(WebElement element, int timeout) Attend que l'élément devienne stale
waitForOverlayToDisappear(By locator, int timeout) Attend disparition d'un overlay
waitFor(int seconds) Attente fixe (Thread.sleep loggé)
// Attente classique
web.waitForElementVisible(By.id("dashboard"));

// Attente de texte
web.waitForTextInElement(By.cssSelector(".status"), "Completed", 15);

// Attente de changement de classe
web.waitForAttributeContains(By.id("btn"), "class", "active", 10);

// Attente AJAX
web.clickOn(By.id("loadData"));
web.waitForAjaxComplete();

📤 Extraction de données

Méthode Description
getText(By locator) Récupère le texte visible
getAttribute(By locator, String attr) Récupère un attribut HTML
getCssValue(By locator, String property) Récupère une propriété CSS
isElementDisplayed(By locator) Vérifie visibilité (ne plante pas)
isElementEnabled(By locator) Vérifie si enabled
isElementSelected(By locator) Vérifie si sélectionné (checkbox/radio)
getElementCount(By locator) Compte les éléments matchant
getAllTexts(By locator) Récupère tous les textes d'une liste
String title = web.getText(By.cssSelector(".title"));
String href = web.getAttribute(By.id("link"), "href");
int itemCount = web.getElementCount(By.cssSelector(".item"));
List<String> prices = web.getAllTexts(By.cssSelector(".price"));

📋 Dropdowns / Select

Méthode Description
selectByVisibleText(By locator, String text) Sélectionne par texte visible
selectByValue(By locator, String value) Sélectionne par valeur
selectByIndex(By locator, int index) Sélectionne par index
getAllSelectOptions(By locator) Liste toutes les options
getSelectedOption(By locator) Option actuellement sélectionnée
isOptionAvailable(By locator, String text) Vérifie si une option existe
web.selectByVisibleText(By.id("country"), "France");
web.selectByValue(By.id("sort"), "price-asc");
List<String> options = web.getAllSelectOptions(By.id("category"));

📊 Tables dynamiques

Méthode Description
getTableRows(By tableLocator) Récupère toutes les lignes
getTableCell(By tableLocator, int row, int col) Récupère une cellule
clickTableCell(By tableLocator, int row, int col) Clique sur une cellule
findRowByText(By tableLocator, String text) Trouve l'index d'une ligne par son texte
clickTableCellByRowTextAndColumnHeader(By table, String rowText, String colHeader) ⭐ Click intelligent par croisement
extractTableData(By tableLocator) Extrait toutes les données en List<Map>
// Click intelligent : trouve la ligne "John Doe" et clique sur la colonne "Actions"
web.clickTableCellByRowTextAndColumnHeader(
    By.id("usersTable"), 
    "John Doe", 
    "Actions"
);

// Extraction complète
List<Map<String, String>> data = web.extractTableData(By.id("resultsTable"));
for (Map<String, String> row : data) {
    System.out.println(row.get("Name") + " - " + row.get("Price"));
}

🖼️ Iframes & Shadow DOM

Méthode Description
switchToIframe(By locator) Bascule vers un iframe
switchToIframeByIndex(int index) Bascule par index
switchToIframeByName(String nameOrId) Bascule par name/id
switchToDefaultContent() Retour au contexte principal
switchToParentFrame() Retour au parent frame
getShadowElement(By host, String selector) Accède à un élément Shadow DOM
clickShadowElement(By host, String selector) Click dans Shadow DOM
// Iframe
web.switchToIframe(By.id("paymentFrame"));
web.typeText(By.id("cardNumber"), "4242424242424242");
web.switchToDefaultContent();

// Shadow DOM
WebElement shadowBtn = web.getShadowElement(
    By.cssSelector("custom-element"), 
    "button.inner"
);
web.clickShadowElement(By.cssSelector("my-component"), ".shadow-button");

🪟 Fenêtres & Onglets

Méthode Description
getAllWindowHandles() Récupère tous les handles
switchToWindowByTitle(String title) Bascule par titre
switchToWindowByIndex(int index) Bascule par index
switchToLatestWindow() Bascule vers la dernière fenêtre
openNewTab(String url) Ouvre un nouvel onglet
closeCurrentWindowAndSwitchToMain() Ferme et revient à la principale
closeOtherWindows() Ferme toutes sauf la principale
web.openNewTab("https://docs.example.com");
// ... actions dans le nouvel onglet ...
web.closeCurrentWindowAndSwitchToMain();

🔔 Alerts & Popups

Méthode Description
acceptAlert() Accepte une alerte
dismissAlert() Rejette une alerte
getAlertText() Récupère le texte
typeInAlert(String text) Saisie dans un prompt
isAlertPresent() Vérifie présence
closeModalByEscape() Ferme modale par ESC
web.clickOn(By.id("deleteBtn"));
String message = web.getAlertText();
web.acceptAlert();

📜 Scroll intelligent

Méthode Description
scrollToElement(By locator) Scroll jusqu'à un élément (smooth, center)
scrollToCoordinates(int x, int y) Scroll vers coordonnées
scrollToTop() Haut de page
scrollToBottom() Bas de page
scrollByOffset(int x, int y) Scroll relatif
scrollUntilElementVisible(By locator, int maxScrolls) Scroll progressif jusqu'à trouver
web.scrollToElement(By.id("footer"));
web.scrollUntilElementVisible(By.cssSelector(".lazy-item"), 10);

🎯 Drag & Drop

Méthode Description
dragAndDrop(By source, By target) Drag & Drop standard
dragAndDropByOffset(By locator, int x, int y) Par offset
dragAndDropJS(By source, By target) Via JavaScript (quand Selenium plante)
web.dragAndDrop(By.id("draggable"), By.id("droppable"));

// Si le D&D standard ne fonctionne pas
web.dragAndDropJS(By.id("source"), By.id("target"));

📸 Screenshots & Debug

Méthode Description
captureScreenshot(String path) Screenshot page complète
captureElementScreenshot(By locator, String path) Screenshot d'un élément
highlightElement(By locator, int durationMs) Surbrillance temporaire
isElementInViewport(By locator) Vérifie si visible dans viewport
printElementProperties(By locator) Affiche toutes les propriétés
getComputedStyle(By locator, String property) Style CSS computed
logCurrentUrl() Log l'URL actuelle
logPageSource() Log le HTML source
web.highlightElement(By.id("errorField"), 2000);
web.captureScreenshot("screenshots/error_" + System.currentTimeMillis() + ".png");

🍪 Cookies & Storage

Méthode Description
addCookie(String name, String value) Ajoute un cookie
getCookie(String name) Récupère un cookie
deleteCookie(String name) Supprime un cookie
deleteAllCookies() Supprime tous les cookies
getAllCookies() Liste tous les cookies
getLocalStorage(String key) Récupère du localStorage
setLocalStorage(String key, String value) Définit dans localStorage
removeLocalStorage(String key) Supprime du localStorage
clearLocalStorage() Vide le localStorage
getSessionStorage(String key) Récupère du sessionStorage
setSessionStorage(String key, String value) Définit dans sessionStorage
clearSessionStorage() Vide le sessionStorage
web.addCookie("session_id", "abc123");
String token = web.getLocalStorage("auth_token");
web.clearSessionStorage();

⚡ Performance

Méthode Description
getPageLoadTime() Temps de chargement (ms)
measureElementRenderTime(By locator) Temps de rendu d'un élément
getPerformanceMetrics() Métriques détaillées (DNS, TCP, DOM...)
long loadTime = web.getPageLoadTime();
assert loadTime < 3000 : "Page trop lente!";

Map<String, Object> metrics = web.getPerformanceMetrics();
System.out.println("DNS: " + metrics.get("dns") + "ms");
System.out.println("Total: " + metrics.get("total") + "ms");

🔧 JavaScript & CDP

Méthode Description
executeJS(String script, Object... args) Exécute du JS synchrone
executeAsyncJS(String script, Object... args) Exécute du JS asynchrone
setAttributeJS(By locator, String attr, String value) Modifie un attribut
removeAttributeJS(By locator, String attr) Supprime un attribut
injectCSS(String css) Injecte du CSS
getJSVariable(String name) Récupère une variable globale
setJSVariable(String name, Object value) Définit une variable globale
executeCdpCommand(String cmd, Map params) Commande Chrome DevTools
blockUrls(List<String> patterns) Bloque des URLs (CDP)
getConsoleLogs() Récupère les logs console
hasJavaScriptErrors() Vérifie erreurs JS
// Modifier un attribut
web.setAttributeJS(By.id("input"), "readonly", "true");

// Bloquer des ressources (analytics, ads...)
web.blockUrls(Arrays.asList("*google-analytics*", "*doubleclick*"));

// Vérifier erreurs JS
if (web.hasJavaScriptErrors()) {
    web.captureScreenshot("logs/js_error.png");
}

🔄 Retry & Résilience

Méthode Description
executeWithRetry(Runnable action, int maxRetries, int delayMs) Exécute avec retry
retryUntilElementVisible(By locator, int attempts, int delayMs) Retry jusqu'à visibilité
retryClick(By locator, int maxAttempts) Click avec gestion StaleElement
executeIgnoringExceptions(Runnable action) Exécute en ignorant les erreurs
// Retry générique
web.executeWithRetry(() -> {
    web.clickOn(By.id("flaky-button"));
    web.waitForElementVisible(By.id("result"));
}, 3, 1000);

// Retry click avec gestion StaleElement
web.retryClick(By.id("dynamic-element"), 5);

🖥️ Browser & Utilitaires

Méthode Description
maximizeWindow() Maximise la fenêtre
fullscreenWindow() Mode plein écran
setWindowSize(int w, int h) Définit la taille
getWindowSize() Récupère la taille
setBrowserZoom(int percentage) Change le zoom
clearBrowserCache() Vide le cache
disableImages() Désactive les images
openDevTools() Ouvre DevTools
driverquit() Ferme proprement le driver
web.maximizeWindow();
web.setBrowserZoom(80);
web.driverquit();

📖 Examples

Test E2E complet : SauceDemo

public class SauceDemoTest {
    private WebOperationsManager web;
    
    // Locators
    private static final By INPUT_USERNAME = By.id("user-name");
    private static final By INPUT_PASSWORD = By.id("password");
    private static final By BTN_LOGIN = By.id("login-button");
    private static final By TITLE_PRODUCTS = By.cssSelector(".title");
    private static final By BTN_ADD_BACKPACK = By.id("add-to-cart-sauce-labs-backpack");
    private static final By CART_BADGE = By.cssSelector(".shopping_cart_badge");
    private static final By CART_LINK = By.cssSelector(".shopping_cart_link");
    private static final By BTN_CHECKOUT = By.id("checkout");
    private static final By INPUT_FIRST_NAME = By.id("first-name");
    private static final By INPUT_LAST_NAME = By.id("last-name");
    private static final By INPUT_POSTAL_CODE = By.id("postal-code");
    private static final By BTN_CONTINUE = By.id("continue");
    private static final By BTN_FINISH = By.id("finish");
    private static final By CHECKOUT_COMPLETE = By.cssSelector(".complete-header");
    
    public void setup() {
        WebDriver driver = WebOperationsManager.createOptimizedChromeDriver(false);
        web = new WebOperationsManager(driver);
    }
    
    public void testFullCheckout() {
        // 1. Login
        web.navigateTo("https://www.saucedemo.com/");
        web.typeText(INPUT_USERNAME, "standard_user");
        web.typeText(INPUT_PASSWORD, "secret_sauce");
        web.clickOn(BTN_LOGIN);
        web.waitForTextInElement(TITLE_PRODUCTS, "Products", 5);
        
        // 2. Add to cart
        web.clickOn(BTN_ADD_BACKPACK);
        assert web.getText(CART_BADGE).equals("1");
        
        // 3. Checkout
        web.clickJS(CART_LINK);
        web.clickJS(BTN_CHECKOUT);
        
        // 4. Fill form (React-compatible)
        web.typeTextForReact(INPUT_FIRST_NAME, "Julien");
        web.typeTextForReact(INPUT_LAST_NAME, "Mer");
        web.typeTextForReact(INPUT_POSTAL_CODE, "69000");
        web.clickOn(BTN_CONTINUE);
        
        // 5. Finish
        web.clickJS(BTN_FINISH);
        
        // 6. Verify
        web.waitForElementVisible(CHECKOUT_COMPLETE);
        assert web.getText(CHECKOUT_COMPLETE).contains("Thank you");
        
        web.logInfo("✅ TEST PASSED: Full checkout");
    }
    
    public void teardown() {
        web.driverquit();
    }
}

⚔️ vs Playwright

Aspect WebOperationsManager Playwright
Setup 1 fichier Java npm install + config
Dépendances Selenium seul Node.js + Playwright
Auto-wait Explicite (contrôle total) Implicite (magic)
React/SPA typeTextForReact() ✅ Natif
Shadow DOM getShadowElement() ✅ Natif
Retry clickWithRetry() ✅ Auto
CDP executeCdpCommand() ✅ Natif
Performance getPerformanceMetrics() ✅ Tracing
Vitesse 🚀 Plus rapide (pas d'overhead Node) 🏃 Rapide
Debugging highlightElement() + screenshots Trace viewer
Intégration Katalon ✅ Direct ❌ Impossible
Courbe d'apprentissage Faible (Selenium classique) Moyenne

Pourquoi choisir WebOperationsManager ?

  1. Vous utilisez déjà Selenium/Katalon → Pas de migration
  2. Vous voulez du contrôle → Pas de magie, tout est explicite
  3. Vous avez besoin de Java → Intégration native
  4. Vous aimez la simplicité → 1 fichier, 0 config

🎓 Bonnes pratiques

1. Toujours utiliser des waits explicites

// ❌ Mauvais
Thread.sleep(3000);
web.clickOn(By.id("btn"));

// ✅ Bon
web.waitForElementClickable(By.id("btn"));
web.clickOn(By.id("btn"));

2. Utiliser clickJS() pour les overlays

// ❌ Peut planter sur overlay
web.clickOn(By.id("hiddenBtn"));

// ✅ Contourne les overlays
web.clickJS(By.id("hiddenBtn"));

3. Utiliser typeTextForReact() pour les SPA

// ❌ Peut ne pas déclencher le state React
web.typeText(By.id("input"), "text");

// ✅ Déclenche correctement les events React
web.typeTextForReact(By.id("input"), "text");

4. Toujours fermer proprement

try {
    // ... tests ...
} finally {
    web.driverquit(); // Ferme driver ET process Chrome
}

5. Centraliser les locators

// ❌ Mauvais : locators en dur
web.clickOn(By.id("login-button"));

// ✅ Bon : constantes réutilisables
private static final By BTN_LOGIN = By.id("login-button");
web.clickOn(BTN_LOGIN);

📄 License

MIT License - Libre d'utilisation, modification et distribution.


👤 Auteur

Julien Mer
QA Architect & Consultant | 20+ ans d'expérience
LinkedInNewsletter QA


⭐ Si ce projet vous aide, n'hésitez pas à lui donner une étoile !

About

Project which demonstrate the speed of Selenium with good practices

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors