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
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,36 @@ For `<a href="http://second-link.com">Second link</a>`, this returns

* Param $label

### grabUrlForLinkFromEmail

Returns the `href` of the first link whose text exactly matches the label in
the given email's HTML body. Matching is case-sensitive. Nested tags and HTML
entities are decoded. Whitespace in the label is preserved.

Fails if the email has no HTML body or no matching link with an `href`.

Example:

```php
$email = $I->lastMessageTo('user@example.com');
$url = $I->grabUrlForLinkFromEmail($email, 'Second link');
```

To use the last email:

```php
$url = $I->grabUrlForLinkFromEmail($I->lastMessage(), 'Second link');
```

For `<a href="http://second-link.com">Second link</a>`, this returns
`http://second-link.com`.

Use an `Email` object from `lastMessage()`, `lastMessageTo()`, or
`lastMessageFrom()`, or one you created directly.

* Param $email
* Param $label

### lastMessageFrom

Grab the full email object sent to an address.
Expand Down
11 changes: 10 additions & 1 deletion src/Module/MailCatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,16 @@ public function grabUrlsFromNthEmail(int $nth): array
*/
public function grabUrlForLinkFromLastEmail(string $label): string
{
$message = Message::from($this->lastMessage()->getSource(), false);
return $this->grabUrlForLinkFromEmail($this->lastMessage(), $label);
}

/**
* Return the URL of the first link with an exact, case-sensitive label
* in the given email's HTML body. Fail if no matching link has an href.
*/
public function grabUrlForLinkFromEmail(Email $email, string $label): string
{
$message = Message::from($email->getSource(), false);
$html = $message->getHtmlContent();

if ($html !== null && $html !== '') {
Expand Down
47 changes: 45 additions & 2 deletions tests/unit/Module/MailCatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,19 +222,48 @@ public function testGrabUrlForLinkFromLastEmail()
$this->assertSame('http://second-link.com', $mailcatcher->grabUrlForLinkFromLastEmail('Second link'));
}

public function testGrabUrlForLinkFromEmail()
{
$mailcatcher = new MailCatcherTest_TestClass();
$email = new Email(1, [], '',
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
. '<a href="http://first-link.com">First link</a>'
. '<a href="http://second-link.com">Second link</a>'
);

$this->assertSame('http://second-link.com', $mailcatcher->grabUrlForLinkFromEmail($email, 'Second link'));
}

public function testGrabUrlForLinkFromGivenEmail()
{
$mailcatcher = new MailCatcherTest_TestClass();
$mailcatcher->setLastMessage(new Email(2, [], '',
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
. '<a href="https://example.com/latest">Reset password</a>'
));
$email = new Email(1, ['user@example.com'], '',
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
. '<a href="https://example.com/selected">Reset password</a>'
);

$this->assertSame('https://example.com/selected', $mailcatcher->grabUrlForLinkFromEmail($email, 'Reset password'));
}

public function testGrabUrlForLinkFromEncodedHtmlEmail()
{
$html = '<a href="https://example.com/first?one=1&amp;two=2"><strong>Café &amp; "tea"</strong></a>'
. '<a href="https://example.com/second">Café &amp; "tea"</a>';
$mailcatcher = new MailCatcherTest_TestClass();
$mailcatcher->setLastMessage(new Email(1, [], '',
$email = new Email(1, [], '',
"MIME-Version: 1.0\r\nContent-Type: multipart/alternative; boundary=parts\r\n\r\n"
. "--parts\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nPlain text\r\n"
. "--parts\r\nContent-Type: text/html; charset=UTF-8\r\n"
. "Content-Transfer-Encoding: quoted-printable\r\n\r\n"
. quoted_printable_encode($html) . "\r\n--parts--\r\n"
));
);

$this->assertSame('https://example.com/first?one=1&two=2', $mailcatcher->grabUrlForLinkFromEmail($email, 'Café & "tea"'));
$mailcatcher->setLastMessage($email);
$this->assertSame('https://example.com/first?one=1&two=2', $mailcatcher->grabUrlForLinkFromLastEmail('Café & "tea"'));
}

Expand All @@ -252,6 +281,20 @@ public function testGrabUrlForLinkFromLastEmailFailsWhenNoLinkMatches(string $so
$mailcatcher->grabUrlForLinkFromLastEmail('Second link');
}

/**
* @dataProvider missingLinkEmails
*/
public function testGrabUrlForLinkFromEmailFailsWhenNoLinkMatches(string $source)
{
$mailcatcher = new MailCatcherTest_TestClass();
$email = new Email(1, [], '', $source);

$this->expectException(AssertionFailedError::class);
$this->expectExceptionMessage('No link found with label: Second link');

$mailcatcher->grabUrlForLinkFromEmail($email, 'Second link');
}

public static function missingLinkEmails(): array
{
return [
Expand Down
Loading