<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dell's Dev Notes]]></title><description><![CDATA[Dell's Dev Notes]]></description><link>https://dell1sec.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Dell&apos;s Dev Notes</title><link>https://dell1sec.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 09:47:15 GMT</lastBuildDate><atom:link href="https://dell1sec.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Temporary Email for Selenium Automation: Zero-Setup Guide (2026)]]></title><description><![CDATA[Temporary Email for Selenium Automation: Zero-Setup Guide (2026)
If you automate signup-flow tests with Selenium, you've hit this wall: the test needs a real inbox to receive the verification email, b]]></description><link>https://dell1sec.hashnode.dev/temporary-email-for-selenium-automation-zero-setup-guide-2026</link><guid isPermaLink="true">https://dell1sec.hashnode.dev/temporary-email-for-selenium-automation-zero-setup-guide-2026</guid><category><![CDATA[selenium]]></category><category><![CDATA[Testing]]></category><category><![CDATA[automation]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Dell]]></dc:creator><pubDate>Wed, 30 Sep 2026 03:57:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc7d1017caa732f780f081/5da3405d-46de-41bc-8d06-559c06557214.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>Temporary Email for Selenium Automation: Zero-Setup Guide (2026)</h1>
<p>If you automate signup-flow tests with Selenium, you've hit this wall: the test needs a <strong>real inbox</strong> to receive the verification email, but you can't use your real email (spam, rate limits, shared state) and spinning up mail servers is overkill.</p>
<p>The clean solution: a free temp-mail API you can poll from your test. No signup, no API key, no cost. Here's the complete pattern with copy-paste code.</p>
<h2>The Problem</h2>
<p>A typical signup E2E test does this:</p>
<ol>
<li>Fill the registration form with a test email</li>
<li><strong>Wait for the verification email</strong></li>
<li>Extract the code/link</li>
<li>Complete verification</li>
<li>Assert the account is active</li>
</ol>
<p>Step 2 is where tests get flaky. Common bad solutions:</p>
<ul>
<li><strong>Real Gmail + IMAP polling</strong> — works until Google rate-limits you, flags access, or a teammate's test run races yours on the same inbox.</li>
<li><strong>MailHog/MailSlurp-style local servers</strong> — great, but requires infrastructure your CI also needs.</li>
<li><strong>Hardcoded test inboxes on your own domain</strong> — now you're running mail infrastructure to test a signup form.</li>
</ul>
<p>A disposable-email API gives you <strong>a fresh, isolated inbox per test run</strong> with zero infrastructure.</p>
<h2>The Pattern (4 Steps)</h2>
<ol>
<li>GET a random temp address from the API</li>
<li>Use it in your Selenium signup flow</li>
<li>Poll the API inbox until the verification email arrives</li>
<li>Parse the code/link, finish the flow</li>
</ol>
<p>Each test run gets its own address — no cross-test contamination, parallel-safe.</p>
<h2>Code: Selenium + 1sec.email API (Python)</h2>
<p><a href="https://1sec.email/">1sec.email</a> exposes a free, no-key API: <code>GET https://1sec.email/api/emails?address=&lt;addr&gt;@1sec.email</code> returns the inbox as JSON. Here's a complete working example:</p>
<pre><code class="language-python">import random, re, string, time, requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

API = "https://1sec.email/api/emails"

def random_address():
    name = "".join(random.choices(string.ascii_lowercase + string.digits, k=10))
    return f"{name}@1sec.email"

def wait_for_email(address, timeout=60):
    """Poll the temp inbox until an email arrives. Returns the latest message."""
    start = time.time()
    while time.time() - start &lt; timeout:
        r = requests.get(API, params={"address": address}, timeout=10)
        r.raise_for_status()
        emails = r.json()
        if emails:
            return emails[0]  # newest first
        time.sleep(3)
    raise TimeoutError(f"No email arrived for {address} within {timeout}s")

def extract_code(email):
    """Pull a 6-digit verification code from subject or body."""
    text = f"{email.get('subject','')} {email.get('body','')}"
    m = re.search(r"\b(\d{6})\b", text)
    if not m:
        raise ValueError("No verification code found in email")
    return m.group(1)

# ---- the test ----
driver = webdriver.Chrome()
try:
    address = random_address()
    print("Using temp address:", address)

    driver.get("https://your-app.example.com/signup")
    driver.find_element(By.NAME, "email").send_keys(address)
    driver.find_element(By.NAME, "password").send_keys("TestPass123!")
    driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

    # wait for the "check your email" state, then poll the inbox
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.NAME, "verification_code"))
    )
    email = wait_for_email(address)
    code = extract_code(email)

    driver.find_element(By.NAME, "verification_code").send_keys(code)
    driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

    WebDriverWait(driver, 10).until(EC.url_contains("/dashboard"))
    print("Signup verified OK")
finally:
    driver.quit()
</code></pre>
<p><strong>Key details that make this reliable:</strong></p>
<ul>
<li><strong>Unique address per run</strong> — parallel CI jobs never collide.</li>
<li><strong>Poll, don't sleep</strong> — polling every 3s up to 60s beats a fixed <code>time.sleep(30)</code>; fixed sleeps are the #1 cause of flaky email tests.</li>
<li><strong>Parse defensively</strong> — regex for the code pattern your app actually sends; fall back to link extraction for magic-link flows.</li>
</ul>
<h2>JavaScript / Node.js Version</h2>
<p>Same pattern with <code>fetch</code> (Node 18+):</p>
<pre><code class="language-javascript">const API = 'https://1sec.email/api/emails';
const rand = () =&gt; Math.random().toString(36).slice(2, 12);

async function waitForEmail(address, timeoutMs = 60000) {
  const start = Date.now();
  while (Date.now() - start &lt; timeoutMs) {
    const res = await fetch(`${API}?address=${encodeURIComponent(address)}`);
    const emails = await res.json();
    if (emails.length) return emails[0];
    await new Promise(r =&gt; setTimeout(r, 3000));
  }
  throw new Error(`No email for ${address} in ${timeoutMs}ms`);
}

// in your Selenium (selenium-webdriver) test:
const address = `${rand()}@1sec.email`;
// ... fill form, submit ...
const email = await waitForEmail(address);
const code = (email.subject + ' ' + email.body).match(/\b(\d{6})\b/)[1];
// ... enter code, assert ...
</code></pre>
<h2>CI/CD Notes</h2>
<ul>
<li><strong>No secrets needed.</strong> The API needs no key, so there are no credentials to rotate in CI.</li>
<li><strong>Rate limits:</strong> be polite — one inbox poll per 3 seconds per test is plenty. Stagger starts for hundreds of parallel tests.</li>
<li><strong>Cleanup:</strong> inboxes auto-expire. Nothing to tear down, no test data leaking between runs.</li>
<li><strong>Debugging failures:</strong> log the temp address on failure and re-query the inbox URL manually within the expiry window to see exactly what arrived.</li>
</ul>
<h2>When NOT to Use This</h2>
<ul>
<li><strong>Load testing</strong> — don't hammer a free API with 10k concurrent inboxes; that's what MailHog is for.</li>
<li><strong>Testing your own product's temp-mail blocking</strong> — use a known-blocked domain instead.</li>
<li><strong>Long-lived test accounts</strong> — inboxes expire; if the test needs the account tomorrow, use a real test mailbox.</li>
</ul>
<h2>Why 1sec.email for This</h2>
<p>Honest comparison for the automation use case: Guerrilla Mail's API is free but its docs are frozen since 2011; temp-mail.io's API is paywalled; tmailor publishes no public API at all. 1sec.email's endpoint is free, keyless, returns clean JSON, and address generation is client-side (no round-trip to create an inbox). For CI test inboxes, that's the whole checklist.</p>
<hr />
<p><em>Related: <a href="https://dell1sec.hashnode.dev/testing-signup-flows-with-playwright-and-a-free-temp-mail-api">Testing Signup Flows with Playwright and a Free Temp Mail API</a> · <a href="https://dell1sec.hashnode.dev/temp-mail-not-receiving-emails-9-fixes-that-actually-work">Temp Mail Not Receiving Emails? 9 Fixes</a> · <a href="https://1sec.email/">1sec.email</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Testing Signup Flows with Playwright and a Free Temp Mail API]]></title><description><![CDATA[Every end-to-end test suite eventually hits the same wall: your signup flow sends a verification email, and your test needs to read it. Spinning up real mailboxes is slow and flaky. Paid inbox APIs (M]]></description><link>https://dell1sec.hashnode.dev/testing-signup-flows-with-playwright-and-a-free-temp-mail-api</link><guid isPermaLink="true">https://dell1sec.hashnode.dev/testing-signup-flows-with-playwright-and-a-free-temp-mail-api</guid><category><![CDATA[Testing]]></category><category><![CDATA[playwright]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Dell]]></dc:creator><pubDate>Wed, 30 Sep 2026 03:43:34 GMT</pubDate><content:encoded><![CDATA[<p>Every end-to-end test suite eventually hits the same wall: your signup flow sends a verification email, and your test needs to read it. Spinning up real mailboxes is slow and flaky. Paid inbox APIs (Mailtrap, Mailslurp) work but cost money per seat. For most signup/OTP test scenarios, there's a simpler option — a <strong>free disposable email API</strong> that gives you a real inbox you can poll from your test code.</p>
<p>In this tutorial I'll show you a complete, copy-paste Playwright setup that:</p>
<ol>
<li>Generates a disposable inbox via API (no signup, no key)</li>
<li>Runs your signup form with that address</li>
<li>Polls the inbox, extracts the OTP/verification link</li>
<li>Completes the flow and asserts success</li>
</ol>
<p>I'll use the <a href="https://1sec.email/">1sec.email API</a> because it's free, needs no API key, and the endpoints are dead simple. The patterns transfer to any temp-mail API (mail.tm, Guerrilla Mail, etc.).</p>
<h2>Why Temp Mail APIs Beat Real Mailboxes in E2E Tests</h2>
<table>
<thead>
<tr>
<th>Approach</th>
<th>Setup</th>
<th>Speed</th>
<th>Cost</th>
<th>Flakiness</th>
</tr>
</thead>
<tbody><tr>
<td>Real Gmail + IMAP</td>
<td>OAuth/app passwords, 2FA headaches</td>
<td>Slow (poll intervals)</td>
<td>Free but fragile</td>
<td>High</td>
</tr>
<tr>
<td>Mailtrap / Mailslurp</td>
<td>API key, dashboard config</td>
<td>Fast</td>
<td>Paid per seat/message</td>
<td>Low</td>
</tr>
<tr>
<td><strong>Disposable email API</strong></td>
<td><strong>One HTTP call</strong></td>
<td><strong>Fast</strong></td>
<td><strong>Free</strong></td>
<td><strong>Low</strong></td>
</tr>
</tbody></table>
<p>The trade-off: disposable APIs give you receive-only inboxes with short lifetimes. That's exactly what signup/OTP tests need — and nothing they don't.</p>
<h2>The 1sec.email API: Two Endpoints</h2>
<p>The whole API surface you need:</p>
<pre><code class="language-bash"># 1. Check the inbox for an address (creates it implicitly on first use)
GET https://1sec.email/api/emails?address=mytest123@1sec.email

# Response: JSON array of message summaries
[
  {
    "id": 892131,
    "from": "noreply@example-app.com",
    "subject": "Verify your email",
    "date": "2026-09-30 09:12:44",
    "bodyPreview": "Your code is 482913..."
  }
]
</code></pre>
<pre><code class="language-bash"># 2. Fetch a full message by ID
GET https://1sec.email/api/emails/892131

# Response: full message with HTML/text body
</code></pre>
<p>That's it. Any address on the service's domains works — there's no registration step, so your test can generate a unique address per run (e.g. <code>e2e-${Date.now()}@1sec.email</code>) and never worry about collisions.</p>
<h2>The Full Playwright Test</h2>
<p>Here's a complete working test. It signs up a user on a fictional app, waits for the OTP email, extracts the 6-digit code, and completes verification:</p>
<pre><code class="language-typescript">// tests/signup.spec.ts
import { test, expect } from '@playwright/test';

const MAIL_API = 'https://1sec.email/api';

interface MailSummary {
  id: number;
  from: string;
  subject: string;
  date: string;
  bodyPreview: string;
}

/** Generate a unique disposable address for this test run */
function freshAddress(): string {
  const stamp = Date.now().toString(36);
  const rand = Math.random().toString(36).slice(2, 8);
  return `e2e-${stamp}-${rand}@1sec.email`;
}

/** Poll the inbox until a message arrives (or time out) */
async function waitForEmail(
  address: string,
  opts: { timeoutMs?: number; intervalMs?: number } = {}
): Promise&lt;MailSummary&gt; {
  const timeoutMs = opts.timeoutMs ?? 90_000;
  const intervalMs = opts.intervalMs ?? 3_000;
  const deadline = Date.now() + timeoutMs;

  while (Date.now() &lt; deadline) {
    const res = await fetch(
      `${MAIL_API}/emails?address=${encodeURIComponent(address)}`
    );
    if (!res.ok) throw new Error(`Mail API error: ${res.status}`);
    const messages: MailSummary[] = await res.json();

    if (messages.length &gt; 0) {
      // Newest first — take the latest message
      return messages.sort((a, b) =&gt; b.id - a.id)[0];
    }
    await new Promise((r) =&gt; setTimeout(r, intervalMs));
  }
  throw new Error(`Timed out waiting for email to ${address}`);
}

/** Fetch the full body of a message */
async function getMessageBody(id: number): Promise&lt;string&gt; {
  const res = await fetch(`${MAIL_API}/emails/${id}`);
  if (!res.ok) throw new Error(`Mail API error: ${res.status}`);
  const msg = await res.json();
  return msg.body ?? msg.textBody ?? '';
}

test('user can sign up and verify email via OTP', async ({ page }) =&gt; {
  const email = freshAddress();
  const password = 'TestPass123!';

  // 1. Fill the signup form with the disposable address
  await page.goto('https://your-app.example.com/signup');
  await page.getByLabel('Email').fill(email);
  await page.getByLabel('Password').fill(password);
  await page.getByRole('button', { name: /sign up/i }).click();

  // 2. App should now show "check your inbox"
  await expect(page.getByText(/check your inbox/i)).toBeVisible();

  // 3. Poll the disposable inbox for the OTP email
  const message = await waitForEmail(email);

  // 4. Extract the 6-digit code from the full body
  const body = await getMessageBody(message.id);
  const match = body.match(/\b(\d{6})\b/);
  expect(match, 'OTP code not found in email body').not.toBeNull();
  const otp = match![1];

  // 5. Enter the code and complete verification
  await page.getByLabel(/verification code/i).fill(otp);
  await page.getByRole('button', { name: /verify/i }).click();

  // 6. Assert we're in
  await expect(page.getByText(/welcome/i)).toBeVisible({ timeout: 15_000 });
});
</code></pre>
<h3>Why This Polling Pattern Doesn't Flake</h3>
<p>Three details make the difference between a reliable test and a flaky one:</p>
<ol>
<li><strong>Unique address per run.</strong> Reusing one inbox across tests means leftover messages from previous runs get picked up. <code>Date.now()</code> + random suffix makes collisions effectively impossible.</li>
<li><strong>Sort by ID descending.</strong> Take the <em>newest</em> message, not the first. If your app sends a welcome email <em>and</em> an OTP, you want the OTP.</li>
<li><strong>Regex on the full body, not the preview.</strong> <code>bodyPreview</code> is truncated — the code might be cut off. Always fetch the full message before extracting.</li>
</ol>
<h2>Extracting Verification Links Instead of Codes</h2>
<p>If your app sends a magic link instead of an OTP, swap the extraction step:</p>
<pre><code class="language-typescript">const body = await getMessageBody(message.id);
const linkMatch = body.match(/https?:\/\/[^\s"'&lt;&gt;]+verify[^\s"'&lt;&gt;]*/i);
expect(linkMatch, 'Verification link not found').not.toBeNull();

// Visit the link directly — no need to click through the UI
await page.goto(linkMatch![0]);
await expect(page.getByText(/verified/i)).toBeVisible({ timeout: 15_000 });
</code></pre>
<p>Visiting the link directly with <code>page.goto</code> is faster and less flaky than trying to click a link inside a rendered email.</p>
<h2>Cypress Variant</h2>
<p>Same idea, Cypress style — put the polling in a custom command:</p>
<pre><code class="language-javascript">// cypress/support/commands.js
Cypress.Commands.add('getOtpEmail', (address, timeout = 90000) =&gt; {
  const started = Date.now();
  const poll = () =&gt; {
    return cy
      .request(`https://1sec.email/api/emails?address=${address}`)
      .then((res) =&gt; {
        if (res.body.length &gt; 0) return res.body[0];
        if (Date.now() - started &gt; timeout) {
          throw new Error(`No email arrived for ${address}`);
        }
        return cy.wait(3000).then(poll);
      });
  };
  return poll();
});

// cypress/e2e/signup.cy.js
it('signs up with OTP verification', () =&gt; {
  const email = `cypress-${Date.now()}@1sec.email`;
  cy.visit('/signup');
  cy.get('[data-cy=email]').type(email);
  cy.get('[data-cy=password]').type('TestPass123!');
  cy.get('[data-cy=submit]').click();

  cy.getOtpEmail(email).then((msg) =&gt;
    cy.request(`https://1sec.email/api/emails/${msg.id}`)
  ).then((res) =&gt; {
    const code = res.body.body.match(/\b(\d{6})\b/)[1];
    cy.get('[data-cy=otp]').type(code);
    cy.get('[data-cy=verify]').click();
  });

  cy.contains(/welcome/i).should('be.visible');
});
</code></pre>
<h2>Python Version (for Selenium / API-Level Tests)</h2>
<pre><code class="language-python">import re, time, requests
from datetime import datetime

MAIL_API = "https://1sec.email/api"

def fresh_address() -&gt; str:
    stamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
    return f"pytest-{stamp}@1sec.email"

def wait_for_email(address: str, timeout: int = 90) -&gt; dict:
    deadline = time.time() + timeout
    while time.time() &lt; deadline:
        r = requests.get(f"{MAIL_API}/emails", params={"address": address})
        r.raise_for_status()
        msgs = r.json()
        if msgs:
            return max(msgs, key=lambda m: m["id"])
        time.sleep(3)
    raise TimeoutError(f"No email arrived for {address}")

def get_otp(address: str) -&gt; str:
    msg = wait_for_email(address)
    full = requests.get(f"{MAIL_API}/emails/{msg['id']}").json()
    body = full.get("body") or full.get("textBody") or ""
    m = re.search(r"\b(\d{6})\b", body)
    assert m, "OTP not found in email body"
    return m.group(1)
</code></pre>
<h2>CI Considerations</h2>
<p>A few things to get right when this runs in CI:</p>
<ul>
<li><strong>Parallel runs:</strong> unique addresses per test (shown above) mean parallel workers never collide. Don't share one inbox across workers.</li>
<li><strong>Rate limits:</strong> keep the poll interval at 3+ seconds. Polling every 500ms from 10 parallel workers looks like abuse and can get your CI IPs throttled.</li>
<li><strong>Timeouts:</strong> 90 seconds is generous; most OTPs arrive in 5–20 seconds. In CI, allow more headroom than locally — mail queues are slower under load.</li>
<li><strong>Cleanup:</strong> disposable inboxes auto-expire, so there's nothing to delete. One less teardown step than Mailtrap-style inboxes.</li>
<li><strong>Don't test the email provider.</strong> Your test asserts <em>your app's</em> behavior. If the temp-mail API is down, that's an infrastructure blip, not an app bug — consider a retry or a fallback provider for the mail step so provider outages don't red your builds.</li>
</ul>
<h2>When NOT to Use This</h2>
<ul>
<li><strong>Testing actual email deliverability</strong> (SPF/DKIM, spam scores, inbox placement) — use a real seed-list service.</li>
<li><strong>Load testing your mail pipeline</strong> — hammering a free API is rude and will get you blocked. Use a local fake SMTP (MailHog) for volume.</li>
<li><strong>Long-lived test accounts</strong> — disposable inboxes expire. For accounts your suite revisits over days, use a real mailbox or a persistent test-mail service.</li>
</ul>
<p>For the 90% case — "my E2E test needs to read one OTP email" — a free disposable email API is the fastest path from zero to green. Grab an address from <a href="https://1sec.email/">1sec.email</a>, poll the API, extract the code, and move on to testing the things that actually matter.</p>
<hr />
<p><em>Related reading: <a href="https://dell1sec.hashnode.dev/temp-mail-not-receiving-emails-9-fixes-that-actually-work">Temp Mail Not Receiving Emails? 9 Fixes That Actually Work</a> · <a href="https://telegra.ph/Why-Do-Websites-Block-Temp-Mail-How-Disposable-Email-Detection-Really-Works-09-30-2">Why Do Websites Block Temp Mail?</a></em></p>
]]></content:encoded></item><item><title><![CDATA[Testing Email Flows Without Burning Your Real Inbox: A Look at the 1sec.email API]]></title><description><![CDATA[Every team has the same problem: testing signup, password-reset, and notification emails requires real inboxes, and nobody wants to sacrifice their personal address to QA. 1sec.email is a free tempora]]></description><link>https://dell1sec.hashnode.dev/testing-email-flows-without-burning-your-real-inbox-a-look-at-the-1sec-email-api</link><guid isPermaLink="true">https://dell1sec.hashnode.dev/testing-email-flows-without-burning-your-real-inbox-a-look-at-the-1sec-email-api</guid><category><![CDATA[Testing]]></category><category><![CDATA[api]]></category><category><![CDATA[Developer Tools]]></category><dc:creator><![CDATA[Dell]]></dc:creator><pubDate>Wed, 30 Sep 2026 03:13:48 GMT</pubDate><content:encoded><![CDATA[<p>Every team has the same problem: testing signup, password-reset, and notification emails requires real inboxes, and nobody wants to sacrifice their personal address to QA. <a href="https://1sec.email/">1sec.email</a> is a free temporary email service with a public JSON API built for exactly this.</p>
<p>How it works: invent any address like <a href="mailto:qa-test-42@1sec.email">qa-test-42@1sec.email</a>, point your app's signup form at it, then poll GET <a href="https://1sec.email/api/emails?address=qa-test-42@1sec.email">https://1sec.email/api/emails?address=qa-test-42@1sec.email</a> — the inbox exists the instant you query it. No registration, no API keys, no "create mailbox" step.</p>
<p>A minimal Node.js polling loop:</p>
<pre><code class="language-js">async function waitForMail(address, tries = 30) {
  for (let i = 0; i &lt; tries; i++) {
    const res = await fetch(`https://1sec.email/api/emails?address=${address}`).then(r =&gt; r.json());
    if (res.emails &amp;&amp; res.emails.length) return res.emails[0];
    await new Promise(r =&gt; setTimeout(r, 2000));
  }
  throw new Error("mail never arrived");
}
</code></pre>
<p>Why I like it for test suites: unique addresses per run eliminate cross-talk between parallel CI jobs; you're exercising the real SMTP path, not a mock; and the zero-disk design means test mail simply vanishes instead of piling up somewhere.</p>
<p>Try it in your browser first: <a href="https://1sec.email/">https://1sec.email/</a> — open the page and the inbox is already there.</p>
]]></content:encoded></item></channel></rss>