Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/Module/MailCatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,12 @@ protected function grabUrlsFromEmail(Email $email): array
preg_match_all($regex, $message->getTextContent() ?? '', $textMatches);
preg_match_all($regex, $message->getHtmlContent() ?? '', $htmlMatches);

return array_merge($textMatches[0], $htmlMatches[0]);
$htmlUrls = array_map(
static fn (string $url): string => html_entity_decode($url, ENT_QUOTES | ENT_HTML5),
$htmlMatches[0]
);

return array_merge($textMatches[0], $htmlUrls);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions tests/acceptance/MailcatcherCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ public function test_grab_urls_from_html_email(
$I->assertEquals($url, $urls[0]);
}

public function test_grab_urls_from_html_email_with_multiple_query_parameters(AcceptanceTester $I)
{
$I->sendEmail("user@example.com", 'Html email with urls', "<html><body><a href='https://example.com/confirm?id=1&amp;token=abc'>Confirm</a></body></html>", true);
$urls = $I->grabUrlsFromLastEmail();

$I->assertEquals(['https://example.com/confirm?id=1&token=abc'], $urls);
}

/**
* @param AcceptanceTester $I
* @param \Codeception\Example $example
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/Module/MailCatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,39 @@ public function testSeeInLastEmailRecipientFail()
$mailcatcher->seeInLastEmailRecipient('userB@example.com');
}

public function testGrabUrlsFromSinglePartEmailsRaisesNoDeprecation()
{
$mailcatcher = new MailCatcherTest_TestClass();
$sourceDirectory = dirname(__DIR__, 3) . '/src/';
set_error_handler(static function (int $severity, string $message, string $file, int $line) use ($sourceDirectory): bool {
if (!str_starts_with($file, $sourceDirectory)) {
return false;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
});

try {
$mailcatcher->setLastMessage(new Email(1, [], '', $this->mimeSource('text/plain', 'Visit https://example.com/a')));
$this->assertEquals(['https://example.com/a'], $mailcatcher->grabUrlsFromLastEmail());

$mailcatcher->setLastMessage(new Email(1, [], '', $this->mimeSource('text/html', '<a href="https://example.com/b">B</a>')));
$this->assertEquals(['https://example.com/b'], $mailcatcher->grabUrlsFromLastEmail());
} finally {
restore_error_handler();
}
}

public function testGrabUrlsFromLastEmailDecodesHtmlEntities()
{
$mailcatcher = new MailCatcherTest_TestClass();
$mailcatcher->setLastMessage(new Email(1, [], '', $this->mimeSource(
'text/html',
'<a href="https://example.com/confirm?id=1&amp;token=abc">Confirm</a>'
)));

$this->assertEquals(['https://example.com/confirm?id=1&token=abc'], $mailcatcher->grabUrlsFromLastEmail());
}

private function mailcatcherWithMessages(array $messages, Email ...$emails): MailCatcherTest_TestClass
{
$mailcatcher = new MailCatcherTest_TestClass();
Expand All @@ -506,6 +539,17 @@ private function message(
'recipients' => $recipients,
];
}

private function mimeSource(string $contentType, string $body): string
{
return "From: sender@example.com\r\n"
. "To: user@example.com\r\n"
. "Subject: Test\r\n"
. "MIME-Version: 1.0\r\n"
. "Content-Type: {$contentType}; charset=UTF-8\r\n"
. "\r\n"
. $body;
}
}

class MailCatcherTest_TestClass extends MailCatcher
Expand Down
Loading