# selenium-nanowait

**Adaptive, state-based waiting utilities for Selenium — built on NanoWait**

## 🚀 What is `selenium-nanowait`?

`selenium-nanowait` is a lightweight companion library for Selenium that **eliminates fragile, time-based waits** by synchronizing browser actions with real page states, not arbitrary timeouts.

Instead of guessing how long to wait (`time.sleep(5)` or `WebDriverWait(10)`), `selenium-nanowait` waits for what actually matters:

*   Element visibility
*   Layout stability
*   DOM readiness

It is **not a Selenium replacement**. It is a drop-in enhancement that works alongside existing Selenium code. In short: you keep using Selenium exactly as you always have — `selenium-nanowait` simply makes waiting deterministic, adaptive, and reliable.

## 🧠 Design Philosophy

`selenium-nanowait` follows three strict rules:

1.  **Complement, never replace Selenium**
2.  **Wait for states, not time**
3.  **Stay explicit and opt-in**

There is **no monkey-patching**, **no hidden globals**, and **no custom drivers**.

## 🛠️ Installation

```bash
pip install selenium-nanowait
```

### Requirements

*   Python ≥ 3.8
*   Selenium ≥ 4.x
*   NanoWait ≥ 4.0.0 (core adaptive engine)

## 💡 Quick Start

### Before (Fragile, Time-Based)

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

# You are guessing how long the element will take to appear
time.sleep(3) 
driver.find_element(By.ID, "submit").click()
```

### After (State-Aware, Deterministic)

```python
from selenium_nanowait import wait_for

# The click only happens when the element is ready
wait_for(driver, "#submit").click()
```

The `click()` in the "After" example only happens when the element:
*   exists
*   is visible
*   is layout-stable
*   and the DOM is ready

## ⚙️ Core API

### `wait_for()`

```python
wait_for(
    driver,
    selector: str,
    *,
    timeout: float | None = None
)
```

This function returns an `AdaptiveElement`, a thin helper that wraps Selenium’s behavior without replacing it.

## 🧩 `AdaptiveElement` API

The `AdaptiveElement` provides state-aware methods that automatically wait for the element to be ready before performing the action.

| Method | Description | Example |
| :--- | :--- | :--- |
| `.click()` | Safely waits and clicks when the element is ready. | `wait_for(driver, "#login").click()` |
| `.type(text, clear=True)` | Waits for readiness, then types text. | `wait_for(driver, "#email").type("user@email.com")` |
| `.raw()` | Returns the native Selenium `WebElement`, untouched. Guarantees full Selenium compatibility. | `el = wait_for(driver, "#submit").raw()` |

### Fine-Grained Control with `.until(...)`

Use the `.until()` method for fine-grained control over the waiting conditions:

```python
wait_for(driver, "#pay").until(
    visible=True,
    stable=True,
    dom_idle=True
)
```

| Condition | Meaning |
| :--- | :--- |
| `visible` | Element is displayed and interactable. |
| `stable` | Element’s bounding box stopped changing (layout stability). |
| `dom_idle` | `document.readyState === "complete"`. |

## 🧠 What Makes `selenium-nanowait` Different?

| Feature | ❌ Traditional Selenium Waits | ✅ `selenium-nanowait` |
| :--- | :--- | :--- |
| **Basis** | Time-based | **State-based** |
| **Scope** | Global | **Element-scoped** |
| **Waiting** | Guess-driven | **Adaptive backoff** |
| **Flakiness** | Fragile under load | **Layout-aware** |
| **Debugging** | Hard to debug | **Deterministic failure messages** |

### ⏱️ Adaptive Waiting (via NanoWait)

Internally, `selenium-nanowait` delegates timing decisions to **NanoWait**, which:
*   adapts polling frequency
*   prevents busy-waiting
*   applies a safe execution floor (50 ms)
*   remains deterministic across runs

This ensures: faster tests on fast machines and safer waits on slow or overloaded systems.

### 🔬 Failure Diagnostics

Instead of generic errors like:
```
TimeoutException after 10 seconds
```

`selenium-nanowait` raises descriptive errors such as:
```
Element '#submit' was found but never became stable.
Observed multiple layout shifts before timeout.
```
This dramatically reduces debugging time.

## 🧪 Design Guarantees

*   Deterministic execution
*   No replacement of Selenium APIs
*   No global side effects
*   No forced DSL
*   Explicit, opt-in usage
*   Safe defaults

## ❌ What This Library Is NOT

*   Not a Selenium fork
*   Not a testing framework
*   Not a Playwright competitor
*   Not a browser controller
*   Not a magic abstraction layer

`selenium-nanowait` does one thing well: **it fixes waiting — without changing Selenium.**

## 🧑‍💻 Example: Side-by-Side with Selenium

The library is designed to coexist peacefully with your existing Selenium code:

```python
# selenium-nanowait (for state-aware actions)
wait_for(driver, "#email").type("test@email.com")

# plain Selenium (still valid for non-interactive elements or quick checks)
driver.find_element(By.ID, "email").send_keys("test@email.com")
```

## 📦 Project Metadata

*   **License:** MIT
*   **Author:** Luiz Filipe Seabra de Marco
*   **Python:** 3.8+
*   **OS:** Independent
*   **Status:** Production-ready (v0.1)

## 🤝 Contribution & Philosophy

`selenium-nanowait` is open-source and intentionally small. Pull requests are welcome if they: improve determinism, reduce flakiness, and preserve Selenium’s mental model. If a feature tries to replace Selenium — it does not belong here.

---

**One-Line Summary:** `selenium-nanowait` makes Selenium wait for reality, not time.
