# Selenium WebDriver: Guia Completo para Automação Web

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

---

## Introdução ao Selenium WebDriver

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

### Arquitetura

```
┌─────────────┐    JSON Wire Protocol    ┌─────────────┐
│    Tests    │ ←──────────────────────→ │   Browser   │
│  (Python)   │                          │  (Chrome)   │
└─────────────┘                          └─────────────┘
       ↑                                        ↑
       │                                        │
       └────────── WebDriver API ───────────────┘
```

---

## Setup

### Instalação

```bash
# Python
pip install selenium webdriver-manager

# Java
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.15.0</version>
</dependency>

# JavaScript
npm install selenium-webdriver
```

### Setup Rápido

```python
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Setup automático
driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install())
)

driver.get("https://example.com")
print(driver.title)
driver.quit()
```

---

## Locators

### Priority de Locators

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

### Exemplos de Locators

```python
from selenium.webdriver.common.by import By

# By ID (mais rápido)
driver.find_element(By.ID, "username")
driver.find_element(By.ID, "password")
driver.find_element(By.ID, "login-button")

# By Name
driver.find_element(By.NAME, "email")
driver.find_element(By.NAME, "search")

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

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

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

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

# Link Text
driver.find_element(By.LINK_TEXT, "Forgot Password?")
driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")

# Data Test ID (recomendado para testes)
driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']")
```

###Locator Strategies

```python
# 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),
]

def find_element(driver, locator_type, locator_value):
    """Encontra elemento usando estratégia otimizada"""
    by, value = LOCATOR_STRATEGY[locator_type](locator_value)
    return driver.find_element(by, value)
```

---

## Interações Básicas

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

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

# Click
driver.find_element(By.ID, "submit-button").click()

# Dropdowns
from selenium.webdriver.support.ui import Select

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

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

# Radio buttons
radio = driver.find_element(By.CSS_SELECTOR, "input[name='plan'][value='premium']")
if not radio.is_selected():
    radio.click()
```

---

## Waits

### Wait Implícito vs Explícito

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

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

# Espera elemento ficar clicável
button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "submit"))
)
button.click()
```

### Expected Conditions

```python
from selenium.webdriver.support import expected_conditions as EC

# Elemento presente no DOM
EC.presence_of_element_located((By.ID, "username"))

# Elemento visível
EC.visibility_of_element_located((By.ID, "welcome-message"))

# Elemento clicável
EC.element_to_be_clickable((By.ID, "submit-button"))

# Texto presente
EC.text_to_be_present_in_element((By.CLASS_NAME, "message"), "Success")

# URL contém
EC.url_contains("dashboard")

# Título contém
EC.title_contains("Dashboard")

# Nova janela abre
EC.new_window_is_opened(original_handles)

# Alerta presente
EC.alert_is_present()

# Loading spinners disappear
EC.invisibility_of_element_located((By.CLASS_NAME, "spinner"))
```

### Wait Customizado

```python
# 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)
    )

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

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

---

## Page Object Pattern

```python
# pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)
    
    def _find(self, locator):
        return self.wait.until(
            EC.presence_of_element_located(locator)
        )
    
    def _click(self, locator):
        self.wait.until(
            EC.element_to_be_clickable(locator)
        ).click()
    
    def _input(self, locator, text):
        element = self.wait.until(
            EC.presence_of_element_located(locator)
        )
        element.clear()
        element.send_keys(text)
    
    def _get_text(self, locator):
        return self.wait.until(
            EC.visibility_of_element_located(locator)
        ).text
    
    def is_displayed(self, locator):
        try:
            return self.wait.until(
                EC.visibility_of_element_located(locator)
            ).is_displayed()
        except:
            return False

# pages/login_page.py
from selenium.webdriver.common.by import By
from .base_page import BasePage

class LoginPage(BasePage):
    URL = "https://example.com/login"
    
    # Locators
    EMAIL_INPUT = (By.ID, "email")
    PASSWORD_INPUT = (By.ID, "password")
    LOGIN_BUTTON = (By.ID, "login-button")
    ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
    FORGOT_PASSWORD_LINK = (By.LINK_TEXT, "Forgot Password?")
    
    def load(self):
        self.driver.get(self.URL)
        return self
    
    def login(self, email, password):
        self._input(self.EMAIL_INPUT, email)
        self._input(self.PASSWORD_INPUT, password)
        self._click(self.LOGIN_BUTTON)
        from pages.dashboard_page import DashboardPage
        return DashboardPage(self.driver)
    
    def get_error_message(self):
        return self._get_text(self.ERROR_MESSAGE)
    
    def click_forgot_password(self):
        self._click(self.FORGOT_PASSWORD_LINK)
        from pages.forgot_password_page import ForgotPasswordPage
        return ForgotPasswordPage(self.driver)

# pages/dashboard_page.py
from .base_page import BasePage
from selenium.webdriver.common.by import By

class DashboardPage(BasePage):
    WELCOME_MESSAGE = (By.CLASS_NAME, "welcome-message")
    USER_NAME = (By.CSS_SELECTOR, "[data-testid='user-name']")
    LOGOUT_BUTTON = (By.ID, "logout-button")
    
    def get_welcome_message(self):
        return self._get_text(self.WELCOME_MESSAGE)
    
    def get_user_name(self):
        return self._get_text(self.USER_NAME)
    
    def logout(self):
        self._click(self.LOGOUT_BUTTON)
        from pages.login_page import LoginPage
        return LoginPage(self.driver)
```

---

## Testando Exemplos

```python
# tests/test_login.py
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

class TestLogin:
    
    @pytest.fixture(autouse=True)
    def setup(self, driver):
        self.driver = driver
        self.login_page = LoginPage(driver)
    
    def test_login_with_valid_credentials(self):
        """Deve fazer login e ir para dashboard"""
        dashboard = self.login_page.load().login(
            email="user@example.com",
            password="password123"
        )
        
        assert dashboard.get_welcome_message() == "Welcome!"
        assert dashboard.get_user_name() == "John Doe"
    
    def test_login_with_invalid_credentials(self):
        """Deve exibir mensagem de erro"""
        self.login_page.load()
        self.login_page.login(
            email="invalid@example.com",
            password="wrongpassword"
        )
        
        assert "Invalid credentials" in self.login_page.get_error_message()
    
    def test_forgot_password_flow(self):
        """Deve navegar para recuperação de senha"""
        forgot_page = self.login_page.load().click_forgot_password()
        
        assert forgot_page.is_displayed()
        assert "Reset Password" in forgot_page.get_title()
```

---

## Headless Mode

```python
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")

driver = webdriver.Chrome(options=options)
```

---

## Conclusão

Selenium WebDriver é poderoso para automação web. As chaves são:

1. **Use locators estáveis** - data-testid > ID > XPath
2. **Waits explícitos** - Sempre prefira sobre implícitos
3. **Page Objects** - Separe locators e ações
4. **Evite sleeps** - Use expected conditions
5. **Debug com screenshots** - Capture estado em falhas
