Guide: Testing Password Resets

Automate the full password reset flow: trigger the email, wait for it, extract the reset link, and verify the reset completes successfully.

Overview

Password reset testing follows this pattern:

  1. Generate a unique test email address for this test run.
  2. Trigger your application's "forgot password" flow using that address.
  3. Wait for the reset email to arrive using waitFor().
  4. Extract the reset link from the email's parsed links.
  5. Visit the reset link and complete the password change.
  6. Assert the reset was successful.

TypeScript Example

TypeScript
import { ReceiverClient } from "@receiverdotemail/sdk";

const receiver = new ReceiverClient({
  apiKey: process.env.RECEIVER_API_KEY,
});

describe("Password Reset", () => {
  it("sends a reset email and allows password change", async () => {
    // 1. Generate a unique email address
    const testEmail = `reset-${Date.now()}[email protected]`;

    // 2. Trigger password reset in your app
    await fetch("https://your-app.com/api/forgot-password", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: testEmail }),
    });

    // 3. Wait for the email (long-polls for up to 30s)
    const email = await receiver.emails.waitFor({
      projectId: "proj_abc123",
      recipient: testEmail,
      timeout: 30,
    });

    // 4. Assert email was received
    expect(email).not.toBeNull();
    expect(email.subject.toLowerCase()).toContain("reset");

    // 5. Extract the reset link
    const resetLink = email.links.find((link) =>
      link.includes("/reset-password") || link.includes("/reset")
    );
    expect(resetLink).toBeDefined();

    // 6. Complete the reset (visit the link with new password)
    const response = await fetch(resetLink!, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ password: "newSecurePassword123!" }),
    });
    expect(response.ok).toBe(true);

    // 7. Verify login with new password works
    const loginResponse = await fetch("https://your-app.com/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: testEmail,
        password: "newSecurePassword123!",
      }),
    });
    expect(loginResponse.ok).toBe(true);
  });
});

Python Example

Python
import pytest
import time
import httpx
from receiverdotemail import ReceiverClient

@pytest.fixture
async def receiver():
    async with ReceiverClient(
        api_key=os.environ["RECEIVER_API_KEY"]
    ) as client:
        yield client

@pytest.mark.asyncio
async def test_password_reset_flow(receiver):
    # 1. Generate a unique email address
    test_email = f"reset-{int(time.time())}[email protected]"

    # 2. Trigger password reset
    async with httpx.AsyncClient() as http:
        await http.post(
            "https://your-app.com/api/forgot-password",
            json={"email": test_email},
        )

    # 3. Wait for the email
    email = await receiver.emails.wait_for(
        project_id="proj_abc123",
        recipient=test_email,
        timeout=30,
    )

    # 4. Assert email was received
    assert email is not None
    assert "reset" in email.subject.lower()

    # 5. Extract the reset link
    reset_link = next(
        (link for link in email.links if "/reset" in link), None
    )
    assert reset_link is not None

    # 6. Complete the reset
    async with httpx.AsyncClient() as http:
        response = await http.post(
            reset_link,
            json={"password": "newSecurePassword123!"},
        )
        assert response.status_code == 200

Tips

  • Unique addresses: Always generate a unique email per test run using timestamps or UUIDs. This prevents interference between parallel tests.
  • Link filtering: Reset emails often contain multiple links (unsubscribe, privacy policy, etc.). Filter by URL path to find the right one.
  • Timeout: Set the waitFor timeout higher than your app's email delivery time. 30 seconds is usually sufficient.
  • Test framework timeout: Make sure your test framework's timeout is higher than the waitFor timeout (e.g. Jest default is 5s, so increase it).

Next Steps