Le framework Selenium qui rivalise avec Playwright
API unifiée, robuste et sans dépendances framework
Features •
Installation •
Quick Start •
API Reference •
Examples •
vs Playwright
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.
✅ 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
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
Java 11+
Selenium 4.x
ChromeDriver (ou autre WebDriver)
<dependencies >
<dependency >
<groupId >org.seleniumhq.selenium</groupId >
<artifactId >selenium-java</artifactId >
<version >4.27.0</version >
</dependency >
</dependencies >
implementation ' org.seleniumhq.selenium:selenium-java:4.27.0'
Copier WebOperationsManager.java dans votre projet
Adapter le package si nécessaire
C'est prêt ! 🎉
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 ();
}
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" ))
🔧 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)
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 ();
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 :
Essaie un click standard
Si échec → fallback JavaScript
Pour les <a> et <button> → vérifie si navigation a eu lieu
Pour les type="submit" → force le submit si nécessaire
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" ));
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" ));
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" ));
}
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" );
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 ();
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 );
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" ));
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" );
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 ();
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" );
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" );
}
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 );
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 ();
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 ();
}
}
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 ?
Vous utilisez déjà Selenium/Katalon → Pas de migration
Vous voulez du contrôle → Pas de magie, tout est explicite
Vous avez besoin de Java → Intégration native
Vous aimez la simplicité → 1 fichier, 0 config
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 );
MIT License - Libre d'utilisation, modification et distribution.
Julien Mer
QA Architect & Consultant | 20+ ans d'expérience
LinkedIn • Newsletter QA
⭐ Si ce projet vous aide, n'hésitez pas à lui donner une étoile !