Metadata-Version: 2.4
Name: mailfixture
Version: 0.2.0
Summary: Email fixtures for your test suite — programmatic inboxes, long-polling, first-class OTP and link extraction.
Project-URL: Homepage, https://mailfixture.com
Project-URL: Documentation, https://mailfixture.com/docs
Author-email: MailFixture <support@mailfixture.com>
License-Expression: MIT
License-File: LICENSE
Keywords: e2e,email,fixtures,otp,playwright,pytest,testing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# mailfixture

Email fixtures for your test suite. Create a programmatic inbox in one call,
point your app's signup / OTP / password-reset flow at it, then wait for the
email — parsed, with the OTP and links already extracted.

Receive-only, built for CI: long-polling instead of retry loops, zero
dependencies outside the Python standard library, Python 3.9+.

```bash
pip install mailfixture
```

## Quickstart

Create a key in [Dashboard → API keys](https://mailfixture.com/app/keys) and
export it as `MAILFIXTURE_API_KEY`.

```python
from mailfixture import MailFixture

mfx = MailFixture()  # reads MAILFIXTURE_API_KEY

def test_signup_sends_otp(page):
    inbox = mfx.create_inbox(ttl_seconds=900)

    page.goto("/signup")
    page.fill("#email", inbox.email_address)
    page.click("text=Send code")

    otp = inbox.wait_for_otp(timeout=30)   # long-polls; no sleep()
    page.fill("#otp", otp)
```

Waiting for a magic link instead:

```python
link = inbox.wait_for_link(kind="verify", timeout=30)
page.goto(link.url)
```

Everything on the wait helpers is server-side long-polling: the request is
held open until a matching message arrives (or the timeout passes, raising
`mailfixture.MailFixtureTimeout`).

## API surface

| Method | Purpose |
| --- | --- |
| `create_inbox(local_part=, domain=, ttl_seconds=)` | new inbox; returns an `Inbox` handle |
| `inbox.wait_for_message(match=, timeout=)` | first matching message, full body + extraction |
| `inbox.wait_for_otp(match=, timeout=)` | best extracted OTP as a string |
| `inbox.wait_for_link(kind=, match=, timeout=)` | first link, optionally `verify\|reset\|unsubscribe` |
| `inbox.messages(since=, match=, wait=)` / `clear()` / `delete()` | raw listing & lifecycle |
| `get_message(id)`, `get_otp(id)`, `get_links(id)` | direct message access |
| `download_attachment(message_id, index)` | decoded attachment bytes |
| `create_domain(fqdn)`, `verify_domain(id)`, `list_domains()`, `delete_domain(id)` | custom domains |
| `create_key(label=)`, `list_keys()`, `revoke_key(id)` | API keys |

`match` filters with `subject:`, `from:`, or `to:` prefixes; a bare term
matches the subject.

Errors are raised as `mailfixture.MailFixtureError` carrying the API's
RFC 7807 `status` / `title` / `detail` fields.

## Development

```bash
PYTHONPATH=src python3 -m unittest discover -s tests
```

Docs: https://mailfixture.com/docs
