<p><strong>Meta Description:</strong> Domine Selenium WebDriver: setup, locators, Page Objects, waits, debugging e melhores práticas para automação de testes web.</p>

<hr>
<h2>Introdução ao Selenium WebDriver</h2>

<p>Selenium WebDriver é a ferramenta mais popular para automação de testes web. Permite controlar browsers programaticamente para executar ações e verificar resultados.</p>

<h3>Arquitetura</h3>

<pre><code>┌─────────────┐    JSON Wire Protocol    ┌─────────────┐
│    Tests    │ ←──────────────────────→ │   Browser   │
│  (Python)   │                          │  (Chrome)   │
└─────────────┘                          └─────────────┘
       ↑                                        ↑
       │                                        │
       └────────── WebDriver API ───────────────┘
</code></pre>

<hr>
<h2>Setup</h2>

<h3>Instalação</h3>

<pre><code># Python
pip install selenium webdriver-manager

<p># Java<br><br>    org.seleniumhq.selenium<br>    selenium-java<br>    4.15.0<br></p>

<p># JavaScript<br>npm install selenium-webdriver<br></code></pre></p>

<h3>Setup Rápido</h3>

<pre><code>from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

<p># Setup automático<br>driver = webdriver.Chrome(<br>    service=Service(ChromeDriverManager().install())<br>)</p>

<p>driver.get("https://example.com")<br>print(driver.title)<br>driver.quit()<br></code></pre></p>

<hr>
<h2>Locators</h2>

<h3>Priority de Locators</h3>

<figure class="wp-block-table"><table>
<tr><th>Prioridade</th><th>Locator</th><th>Exemplo</th><th>Confiabilidade</th></tr>
<tr><td>1</td><td>ID</td><td><code>By.ID, "username"</code></td><td>Mais confiável</td></tr>
<tr><td>2</td><td>Name</td><td><code>By.NAME, "email"</code></td><td>Bom</td></tr>
<tr><td>3</td><td>Data Test ID</td><td><code>By.CSS, "[data-testid='btn']"</code></td><td>Excelente</td></tr>
<tr><td>4</td><td>Class</td><td><code>By.CLASS_NAME, "btn-primary"</code></td><td>Médio</td></tr>
<tr><td>5</td><td>XPath</td><td><code>By.XPATH, "//button[@id='submit']"</code></td><td>Flexível</td></tr>
<tr><td>6</td><td>CSS</td><td><code>By.CSS_SELECTOR, "#submit"</code></td><td>Rápido</td></tr>
<tr><td>7</td><td>Link Text</td><td><code>By.LINK_TEXT, "Click here"</code></td><td>Texto âncora</td></tr>
<tr><td>8</td><td>Partial Link</td><td><code>By.PARTIAL_LINK_TEXT, "here"</code></td><td>Texto parcial</td></tr>
</table></figure>

<h3>Exemplos de Locators</h3>

<pre><code>from selenium.webdriver.common.by import By

<p># By ID (mais rápido)<br>driver.find_element(By.ID, "username")<br>driver.find_element(By.ID, "password")<br>driver.find_element(By.ID, "login-button")</p>

<p># By Name<br>driver.find_element(By.NAME, "email")<br>driver.find_element(By.NAME, "search")</p>

<p># By XPath (absoluto - evitar)<br>driver.find_element(By.XPATH, "/html/body/div[2]/form/input[1]")</p>

<p># XPath relativo (preferir)<br>driver.find_element(By.XPATH, "//input[@id='username']")<br>driver.find_element(By.XPATH, "//button[contains(text(), 'Login')]")<br>driver.find_element(By.XPATH, "//div[@class='form']//input[@name='email']")</p>

<p># XPath com múltiplas condições<br>driver.find_element(By.XPATH, <br>    "//button[@type='submit' and contains(@class, 'btn-primary')]")</p>

<p># CSS Selector<br>driver.find_element(By.CSS_SELECTOR, "#username")<br>driver.find_element(By.CSS_SELECTOR, ".form-group .form-control")<br>driver.find_element(By.CSS_SELECTOR, "input[name='email']")<br>driver.find_element(By.CSS_SELECTOR, "button[type='submit']")</p>

<p># Link Text<br>driver.find_element(By.LINK_TEXT, "Forgot Password?")<br>driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")</p>

<p># Data Test ID (recomendado para testes)<br>driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']")<br></code></pre></p>

<p>###Locator Strategies</p>

<pre><code># Hierarquia de preferências
LOCATOR_STRATEGY = [
    ("data-testid", lambda x: By.CSS_SELECTOR, f"[data-testid='{x}']"),
    ("id", lambda x: By.ID, x),
    ("name", lambda x: By.NAME, x),
    ("aria-label", lambda x: By.CSS_SELECTOR, f"[aria-label='{x}']"),
    ("text", lambda x: By.XPATH, f"//*[contains(text(), '{x}')]"),
    ("css", lambda x: By.CSS_SELECTOR, x),
]

<p>def find_element(driver, locator_type, locator_value):<br>    """Encontra elemento usando estratégia otimizada"""<br>    by, value = LOCATOR_STRATEGY<a href="locator_value">locator_type</a><br>    return driver.find_element(by, value)<br></code></pre></p>

<hr>
<h2>Interações Básicas</h2>

<pre><code># Navegação
driver.get("https://example.com")
driver.back()
driver.forward()
driver.refresh()
driver.title  # Page title
driver.current_url  # Current URL

<p># Localizar e interagir<br>element = driver.find_element(By.ID, "username")<br>element.send_keys("user@example.com")<br>element.clear()</p>

<p># Click<br>driver.find_element(By.ID, "submit-button").click()</p>

<p># Dropdowns<br>from selenium.webdriver.support.ui import Select</p>

<p>dropdown = Select(driver.find_element(By.ID, "country"))<br>dropdown.select_by_visible_text("Brazil")<br>dropdown.select_by_value("br")<br>dropdown.select_by_index(1)</p>

<p># Checkboxes<br>checkbox = driver.find_element(By.ID, "accept-terms")<br>if not checkbox.is_selected():<br>    checkbox.click()</p>

<p># Radio buttons<br>radio = driver.find_element(By.CSS_SELECTOR, "input[name='plan'][value='premium']")<br>if not radio.is_selected():<br>    radio.click()<br></code></pre></p>

<hr>
<h2>Waits</h2>

<h3>Wait Implícito vs Explícito</h3>

<pre><code># ❌ NÃO USE: Wait implícito (global, aplicado a todos)
driver.implicitly_wait(10)  # Espera 10s para cada elemento

<p># ✅ USE: Wait explícito (específico por situação)<br>from selenium.webdriver.support.ui import WebDriverWait<br>from selenium.webdriver.support import expected_conditions as EC</p>

<p># Espera elemento ficar clicável<br>button = WebDriverWait(driver, 10).until(<br>    EC.element_to_be_clickable((By.ID, "submit"))<br>)<br>button.click()<br></code></pre></p>

<h3>Expected Conditions</h3>

<pre><code>from selenium.webdriver.support import expected_conditions as EC

<p># Elemento presente no DOM<br>EC.presence_of_element_located((By.ID, "username"))</p>

<p># Elemento visível<br>EC.visibility_of_element_located((By.ID, "welcome-message"))</p>

<p># Elemento clicável<br>EC.element_to_be_clickable((By.ID, "submit-button"))</p>

<p># Texto presente<br>EC.text_to_be_present_in_element((By.CLASS_NAME, "message"), "Success")</p>

<p># URL contém<br>EC.url_contains("dashboard")</p>

<p># Título contém<br>EC.title_contains("Dashboard")</p>

<p># Nova janela abre<br>EC.new_window_is_opened(original_handles)</p>

<p># Alerta presente<br>EC.alert_is_present()</p>

<p># Loading spinners disappear<br>EC.invisibility_of_element_located((By.CLASS_NAME, "spinner"))<br></code></pre></p>

<h3>Wait Customizado</h3>

<pre><code># Wait para elemento com texto específico
def wait_for_text(driver, locator, text, timeout=10):
    """Espera até elemento conter texto específico"""
    return WebDriverWait(driver, timeout).until(
        EC.text_to_be_present_in_element(locator, text)
    )

<p># Wait para página carregar completamente<br>def wait_for_page_load(driver, timeout=30):<br>    """Espera documento.readyState ser 'complete'"""<br>    def document_ready(driver):<br>        return driver.execute_script("return document.readyState") == "complete"<br>    WebDriverWait(driver, timeout).until(document_ready)</p>

<p># Wait para requisição AJAX<br>def wait_for_ajax(driver, timeout=10):<br>    """Espera requisições AJAX terminarem"""<br>    def ajax_complete(driver):<br>        return driver.execute_script(<br>            "return jQuery.active == 0"<br>        )<br>    WebDriverWait(driver, timeout).until(ajax_complete)<br></code></pre></p>

<hr>
<h2>Page Object Pattern</h2>

<pre><code># pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

<p>class BasePage:<br>    def __init__(self, driver):<br>        self.driver = driver<br>        self.wait = WebDriverWait(driver, 10)</p>

<p>def _find(self, locator):<br>        return self.wait.until(<br>            EC.presence_of_element_located(locator)<br>        )</p>

<p>def _click(self, locator):<br>        self.wait.until(<br>            EC.element_to_be_clickable(locator)<br>        ).click()</p>

<p>def _input(self, locator, text):<br>        element = self.wait.until(<br>            EC.presence_of_element_located(locator)<br>        )<br>        element.clear()<br>        element.send_keys(text)</p>

<p>def _get_text(self, locator):<br>        return self.wait.until(<br>            EC.visibility_of_element_located(locator)<br>        ).text</p>

<p>def is_displayed(self, locator):<br>        try:<br>            return self.wait.until(<br>                EC.visibility_of_element_located(locator)<br>            ).is_displayed()<br>        except:<br>            return False</p>

<p># pages/login_page.py<br>from selenium.webdriver.common.by import By<br>from .base_page import BasePage</p>

<p>class LoginPage(BasePage):<br>    URL = "https://example.com/login"</p>

<p># Locators<br>    EMAIL_INPUT = (By.ID, "email")<br>    PASSWORD_INPUT = (By.ID, "password")<br>    LOGIN_BUTTON = (By.ID, "login-button")<br>    ERROR_MESSAGE = (By.CLASS_NAME, "error-message")<br>    FORGOT_PASSWORD_LINK = (By.LINK_TEXT, "Forgot Password?")</p>

<p>def load(self):<br>        self.driver.get(self.URL)<br>        return self</p>

<p>def login(self, email, password):<br>        self._input(self.EMAIL_INPUT, email)<br>        self._input(self.PASSWORD_INPUT, password)<br>        self._click(self.LOGIN_BUTTON)<br>        from pages.dashboard_page import DashboardPage<br>        return DashboardPage(self.driver)</p>

<p>def get_error_message(self):<br>        return self._get_text(self.ERROR_MESSAGE)</p>

<p>def click_forgot_password(self):<br>        self._click(self.FORGOT_PASSWORD_LINK)<br>        from pages.forgot_password_page import ForgotPasswordPage<br>        return ForgotPasswordPage(self.driver)</p>

<p># pages/dashboard_page.py<br>from .base_page import BasePage<br>from selenium.webdriver.common.by import By</p>

<p>class DashboardPage(BasePage):<br>    WELCOME_MESSAGE = (By.CLASS_NAME, "welcome-message")<br>    USER_NAME = (By.CSS_SELECTOR, "[data-testid='user-name']")<br>    LOGOUT_BUTTON = (By.ID, "logout-button")</p>

<p>def get_welcome_message(self):<br>        return self._get_text(self.WELCOME_MESSAGE)</p>

<p>def get_user_name(self):<br>        return self._get_text(self.USER_NAME)</p>

<p>def logout(self):<br>        self._click(self.LOGOUT_BUTTON)<br>        from pages.login_page import LoginPage<br>        return LoginPage(self.driver)<br></code></pre></p>

<hr>
<h2>Testando Exemplos</h2>

<pre><code># tests/test_login.py
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

<p>class TestLogin:</p>

<p>@pytest.fixture(autouse=True)<br>    def setup(self, driver):<br>        self.driver = driver<br>        self.login_page = LoginPage(driver)</p>

<p>def test_login_with_valid_credentials(self):<br>        """Deve fazer login e ir para dashboard"""<br>        dashboard = self.login_page.load().login(<br>            email="user@example.com",<br>            password="password123"<br>        )</p>

<p>assert dashboard.get_welcome_message() == "Welcome!"<br>        assert dashboard.get_user_name() == "John Doe"</p>

<p>def test_login_with_invalid_credentials(self):<br>        """Deve exibir mensagem de erro"""<br>        self.login_page.load()<br>        self.login_page.login(<br>            email="invalid@example.com",<br>            password="wrongpassword"<br>        )</p>

<p>assert "Invalid credentials" in self.login_page.get_error_message()</p>

<p>def test_forgot_password_flow(self):<br>        """Deve navegar para recuperação de senha"""<br>        forgot_page = self.login_page.load().click_forgot_password()</p>

<p>assert forgot_page.is_displayed()<br>        assert "Reset Password" in forgot_page.get_title()<br></code></pre></p>

<hr>
<h2>Headless Mode</h2>

<pre><code>from selenium.webdriver.chrome.options import Options

<p>options = Options()<br>options.add_argument("--headless")<br>options.add_argument("--no-sandbox")<br>options.add_argument("--disable-dev-shm-usage")<br>options.add_argument("--disable-gpu")</p>

<p>driver = webdriver.Chrome(options=options)<br></code></pre></p>

<hr>
<h2>Conclusão</h2>

<p>Selenium WebDriver é poderoso para automação web. As chaves são:<br><ol><li><strong>Use locators estáveis</strong> - data-testid &gt; ID &gt; XPath</li><br><li><strong>Waits explícitos</strong> - Sempre prefira sobre implícitos</li><br><li><strong>Page Objects</strong> - Separe locators e ações</li><br><li><strong>Evite sleeps</strong> - Use expected conditions</li><br><li><strong>Debug com screenshots</strong> - Capture estado em falhas</li></ol></p>