mail-intercept icon indicating copy to clipboard operation
mail-intercept copied to clipboard

assert attachments

Open jamesj2 opened this issue 3 years ago • 5 comments

Is there a way to assert attachments? I tried accessing them via intercepted mail collection but it's values are private.

jamesj2 avatar Aug 08 '22 19:08 jamesj2

That is a great question and sorry I missed this question earlier. I'll look into that. That would be a good addition.

brandonferens avatar Sep 22 '22 15:09 brandonferens

Any news on this ?

rabol avatar Oct 03 '23 06:10 rabol

No news. I haven't been able to make time to make this addition. A PR is welcome, however ;)

brandonferens avatar Oct 24 '23 16:10 brandonferens

Roughly 4 years ago, I had to implement my own mail interception as I didn't come across this library. I wanted to transition to this now but it lacks attachments api that I had already written. Here is the attachment part from my implementation which can give some direction if someone wants to implement it in this library:

public function assertMailHasAttachment($name = null, $mimeType = null)
{
    $email = $this->lastEmail();

    $attachments = collect($email->getAttachments())->filter(function (DataPart $dataPart) use ($mimeType, $name) {
        $attachmentFilename = $dataPart->getPreparedHeaders()->getHeaderParameter('Content-Disposition', 'filename');
        $attachmentMimeType = $dataPart->getPreparedHeaders()->getHeaderBody('Content-Type');
        $valid = true;

        if ($name) {
            $valid = $valid && $attachmentFilename === $name;
        }

        if ($mimeType) {
            $valid = $valid && $attachmentMimeType === $mimeType;
        }

        return $valid;
    });

    $helpMessage = "Could not find any attachment";

    if ($name || $mimeType) {
        $helpMessage .= " with " . collect(['name' => $name, 'mime type' => $mimeType])
                ->filter()
                ->map(fn($v, $key) => "$key \"$v\"")
                ->join(' and ');
    }

    Assert::assertNotEmpty($attachments, $helpMessage);

    return $this;
}

Here is my full implementation:

Click to expand...
<?php

namespace App\Traits;

use Illuminate\Testing\Assert;
use Illuminate\Testing\Constraints\SeeInOrder;
use Illuminate\Support\Collection;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;

trait InteractsWithMail
{

    /**
     * @return Collection<int, SentMessage>
     */
    public function getMessages()
    {
        return app('mailer')->getSymfonyTransport()->messages();
    }

    /** @before */
    public function setUpInteractsWithMail()
    {
        $this->afterApplicationCreated(function () {
            config()->set('mail.driver', 'array');
        });
    }

    public function lastEmail(): Email
    {
        return $this->getMessages()->last()->getOriginalMessage();
    }

    /// Assertions
    public function assertMailSent()
    {
        $this->assertTrue($this->getMessages()->isNotEmpty(), 'No mail has been sent');

        return $this;
    }

    public function assertMailSentTo($mail, $name = null)
    {
        $email = $this->lastEmail();
        $to = collect($email->getTo())
            ->map(fn(Address $address) => ['name' => $address->getName(), 'address' => $address->getAddress()])
            ->pluck('name', 'address');
        $helpMessage = function ($key) use ($to) {
            $v = json_encode($to);
            return "Could not find the given $key in $v";
        };

        $this->assertArrayHasKey($mail, $to, $helpMessage($mail));

        if ($name) {
            $this->assertEquals($name, $to[$mail] ?? null, $helpMessage($name));
        }

        return $this;
    }

    public function assertMailSentFrom($mail, $name = null)
    {
        $email = $this->lastEmail();
        $from = collect($email->getFrom())
            ->map(fn(Address $address) => ['name' => $address->getName(), 'address' => $address->getAddress()])
            ->pluck('name', 'address');

        $helpMessage = function ($key) use ($from) {
            $v = json_encode($from);
            return "Could not find the given $key in $v";
        };

        $this->assertArrayHasKey($mail, $from, $helpMessage($mail));

        if ($name) {
            $this->assertEquals($name, $from[$mail] ?? null, $helpMessage($name));
        }

        return $this;
    }

    public function assertMailSubject($value)
    {
        $email = $this->lastEmail();

        $this->assertEquals($value, $email->getSubject());

        return $this;
    }

    public function assertMailContainsText($value)
    {
        $email = $this->lastEmail();

        Assert::assertStringContainsString((string)$value, strip_tags($email->getHtmlBody()));

        return $this;
    }

    public function assertMailContainsTextInOrder(array $values)
    {
        $email = $this->lastEmail();

        Assert::assertThat($values, new SeeInOrder(strip_tags($email->getHtmlBody())));

        return $this;
    }

    public function assertMailContainsRaw($value)
    {
        $email = $this->lastEmail();

        Assert::assertStringContainsString((string)$value, $email->getHtmlBody());

        return $this;
    }

    public function assertMailHasAttachment($name = null, $mimeType = null)
    {
        $email = $this->lastEmail();

        $attachments = collect($email->getAttachments())->filter(function (DataPart $dataPart) use ($mimeType, $name) {
            $attachmentFilename = $dataPart->getPreparedHeaders()->getHeaderParameter('Content-Disposition', 'filename');
            $attachmentMimeType = $dataPart->getPreparedHeaders()->getHeaderBody('Content-Type');
            $valid = true;

            if ($name) {
                $valid = $valid && $attachmentFilename === $name;
            }

            if ($mimeType) {
                $valid = $valid && $attachmentMimeType === $mimeType;
            }

            return $valid;
        });

        $helpMessage = "Could not find any attachment";

        if ($name || $mimeType) {
            $helpMessage .= " with " . collect(['name' => $name, 'mime type' => $mimeType])
                    ->filter()
                    ->map(fn($v, $key) => "$key \"$v\"")
                    ->join(' and ');
        }

        Assert::assertNotEmpty($attachments, $helpMessage);

        return $this;
    }
}

OrkhanAlikhanov avatar Jul 14 '24 20:07 OrkhanAlikhanov

Thank you for this! Hopefully I can get some extra time in the very near future to implement this.

brandonferens avatar Jul 30 '24 13:07 brandonferens