June 24, 2025

Email attachments with Playwright

While email attachments are infrequently tested, they should still be covered if anything is being done programmatically.

In this post we'll be going over how to test email attachments functionality using Playwright with Mailisk. If you want to follow along or view the final result, you can check out the repository.

Let's summarize attachments. Email attachments are files that are sent along with an email. They can be used to send things like invoices, receipts, or other documents. They’re sent as separate MIME parts (often base64-encoded) that most clients show as downloadable files.

Note: Emails that include attachments are larger and usually take a little longer to finish processing than plain-text messages. If you run into issues with the emails not being received, try increasing the timeout.

Playwright

We'll be using Playwright. If this is your first time using Playwright take a look at the guide. The provided repository in this post already has Playwright setup.

Mailisk

This is what we'll use to receive our emails programmatically and get the attachments.

This guide assumes you have some familiarity with Mailisk and the namespaces. If you're not familiar with Mailisk take a look at the Email Verification with Playwright post.

Getting started

We'll have a simple form with a file input, a recipient email input, and a submit button.

Form

Let's write a email-attachments.spec.ts file.

Our goal will be to test the following:

  • Upload a file
  • Enter recipient email
  • Send the email
  • Check if the attachment is received

The following is a snippet which already has the Mailisk client setup, and receiving the email. We still need to fill out uploading the file and then checking if the attachment is received.

import { test, expect } from "@playwright/test";
import { MailiskClient } from "mailisk";

const namespace = process.env.MAILISK_NAMESPACE;

const mailisk = new MailiskClient({ apiKey: process.env.MAILISK_API_KEY });

test.describe("Email attachments", () => {
  test("Should send uploaded file to recipient", async ({ page }) => {
    const recipientEmailAddress = `test.${Date.now()}@${namespace}.mailisk.net`;

    await page.goto("http://localhost:3000/");
    await page.fill("#recipient-email", recipientEmailAddress);

    // upload file
    await page.setInputFiles('input[type="file"]', "./example.txt");

    await page.click('form button[type="submit"]');

    // we wait for the email to arrive, we filter by it's prefix
    const { data: emails } = await mailisk.searchInbox(namespace, {
      to_addr_prefix: recipientEmailAddress,
    });
    const email = emails[0];

    // TODO: check if the attachment matches the uploaded file
  });
});

Uploading the file

In our project we have a example.txt file in the root directory. We'll be using this file to upload to the website. With Playwright we can use the page.setInputFiles method to upload the file in the input field.

// upload file
await page.setInputFiles('input[type="file"]', "./example.txt");

Checking if the attachment is received

We've used the mailisk.searchInbox method to search for the email. The attachments are included in the attachments property of the email like so:

{
  "id": "1659368409795-42UcuQtMy",
  "from": {
    "address": "test@example.com",
    "name": ""
  },
  ...
  "attachments": [
    {
      "id": "123",
      "filename": "attachment.txt",
      "content_type": "text/plain",
      "size": 100,
    }
  ]
}

We can use the mailisk.getAttachment method to get the attachment information including it's URL. But since we want the file blob we can use the utility function mailisk.downloadAttachment to download the attachment directly.

// download the attachment
const attachment = email.attachments[0];
expect(attachment).toBeDefined();
const blob = await mailisk.downloadAttachment(attachment.id);

Using the buffer we can compare the uploaded file with the downloaded attachment.

// check if the attachment matches the uploaded file
const originalFile = await fs.readFile("./example.txt");
expect(blob).toEqual(originalFile);

And that's it! With this we've covered the email attachment flow for this example. Our final email-attachments.spec.ts file looks like this:

import { test, expect } from "@playwright/test";
import { MailiskClient } from "mailisk";
import fs from "fs/promises";

const namespace = process.env.MAILISK_NAMESPACE;

const mailisk = new MailiskClient({ apiKey: process.env.MAILISK_API_KEY });

test.describe("Email attachments", () => {
  test("Should send uploaded file to recipient", async ({ page }) => {
    const recipientEmailAddress = `test.${Date.now()}@${namespace}.mailisk.net`;

    await page.goto("http://localhost:5173/");
    await page.getByTestId("email").fill(recipientEmailAddress);

    // upload file
    await page.setInputFiles('input[type="file"]', "./example.txt");

    await page.click('form button[type="submit"]');

    // we wait for the email to arrive, we filter by it's prefix
    const { data: emails } = await mailisk.searchInbox(namespace, {
      to_addr_prefix: recipientEmailAddress,
    });
    const email = emails[0];

    // download the attachment
    const attachment = email.attachments[0];
    expect(attachment).toBeDefined();
    const blob = await mailisk.downloadAttachment(attachment.id);

    // check if the attachment matches the uploaded file
    const originalFile = await fs.readFile("./example.txt");
    expect(blob).toEqual(originalFile);
  });
});

Ready to start testing emails?
Create a free account.

Get started