├── .github
├── dependabot.yml
├── release-drafter.yml
└── workflows
│ ├── release-drafter.yml
│ └── tests.yml
├── LICENSE
├── README.md
├── composer.json
└── src
├── Module
└── MailCatcher.php
└── Util
└── Email.php
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # https://help.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file
2 | version: 2
3 | updates:
4 | - package-ecosystem: composer
5 | directory: /
6 | schedule:
7 | interval: daily
8 | commit-message:
9 | prefix: composer
10 | include: scope
11 | - package-ecosystem: github-actions
12 | directory: /
13 | schedule:
14 | interval: daily
15 | commit-message:
16 | prefix: github-actions
17 | include: scope
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | template: |
2 | ## What’s Changed
3 |
4 | $CHANGES
5 |
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | # Drafts your next Release notes as Pull Requests are merged into "master"
2 | name: Release Drafter
3 |
4 | on:
5 | push:
6 | branches:
7 | - master
8 |
9 | permissions:
10 | contents: read
11 |
12 | jobs:
13 | update_release_draft:
14 | permissions:
15 | contents: write
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: release-drafter/release-drafter@v6
19 | env:
20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | concurrency:
12 | group: ${{ github.sha }}-tests
13 | cancel-in-progress: true
14 |
15 | jobs:
16 | test:
17 | runs-on: ubuntu-latest
18 | strategy:
19 | matrix:
20 | php: ['7.4', '8.0', '8.1', '8.2', '8.3']
21 | codeception: ['^4.0', '^5.0']
22 | exclude:
23 | # Codeception 5 requires PHP ≥ 8
24 | - php: '7.4'
25 | codeception: '^5.0'
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 | - name: Setup Ruby
30 | uses: ruby/setup-ruby@v1
31 | with:
32 | ruby-version: 3.1.1
33 | - run: gem install mime-types --version "< 3"
34 | - run: gem install --conservative mailcatcher
35 | - run: mailcatcher
36 | # Temporary workaround for https://github.com/sj26/mailcatcher/issues/182
37 | - run: mailcatcher -f > out.txt&
38 | - name: Setup PHP
39 | uses: shivammathur/setup-php@v2
40 | with:
41 | coverage: xdebug
42 | ini-values: "sendmail_path = /usr/bin/env catchmail -f some@from.address"
43 | php-version: ${{ matrix.php }}
44 | - uses: ramsey/composer-install@v3
45 | with:
46 | dependency-versions: highest
47 | composer-options: "--with=codeception/codeception:${{ matrix.codeception }}"
48 | - run: php vendor/bin/codecept build
49 | - name: Acceptance tests
50 | run: php vendor/bin/codecept run acceptance -d
51 | - name: Unit tests
52 | env:
53 | XDEBUG_MODE: coverage
54 | run: php vendor/bin/codecept run unit --coverage-xml -d
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Jordan Eldredge and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codeception MailCatcher Module
2 |
3 | [](https://travis-ci.org/captbaritone/codeception-mailcatcher-module)
4 |
5 | This module will let you test emails that are sent during your Codeception
6 | acceptance tests. It depends upon you having
7 | [MailCatcher](http://mailcatcher.me/) installed on your development server.
8 |
9 | It was inspired by the Codeception blog post: [Testing Email in
10 | PHP](http://codeception.com/12-15-2013/testing-emails-in-php). It is currently
11 | very simple. Send a pull request or file an issue if you have ideas for more
12 | features.
13 |
14 | ## Installation
15 |
16 | 1. Add the package to your `composer.json`:
17 |
18 | `composer require --dev captbaritone/mailcatcher-codeception-module`
19 |
20 | 2. Configure your project to actually send emails through `smtp://127.0.0.1:1025` in the test environment
21 |
22 | 3. Enable the module in your `acceptance.suite.yml`:
23 | ```yaml
24 | modules:
25 | enabled:
26 | - MailCatcher
27 | config:
28 | MailCatcher:
29 | url: 'http://127.0.0.1'
30 | port: '1080'
31 | ```
32 |
33 | ## Optional Configuration
34 |
35 | If you need to specify some special options (e.g. SSL verification or authentication
36 | headers), you can set all of the allowed [Guzzle request options](https://guzzle.readthedocs.org/en/5.3/clients.html#request-options):
37 |
38 | class_name: WebGuy
39 | modules:
40 | enabled:
41 | - MailCatcher
42 | config:
43 | MailCatcher:
44 | url: 'http://127.0.0.1'
45 | port: '1080'
46 | guzzleRequestOptions:
47 | verify: false
48 | debug: true
49 | version: 1.0
50 |
51 | ## Example Usage
52 | ```php
53 | wantTo('Get a password reset email');
56 |
57 | // Clear old emails from MailCatcher
58 | $I->resetEmails();
59 |
60 | // Reset password
61 | $I->amOnPage('forgotPassword.php');
62 | $I->fillField("input[name='email']", 'user@example.com');
63 | $I->click('Submit');
64 | $I->see('Please check your inbox');
65 |
66 | $I->seeInLastEmail('Please click this link to reset your password');
67 | ```
68 |
69 | ## Actions
70 |
71 | ### resetEmails
72 |
73 | Clears the emails in MailCatcher's list. This prevents seeing emails sent
74 | during a previous test. You probably want to do this before you trigger any
75 | emails to be sent
76 |
77 | Example:
78 |
79 | resetEmails();
82 | ?>
83 |
84 | ### seeEmailAttachmentCount
85 |
86 | Checks expected count of attachments in last email.
87 |
88 | Example:
89 |
90 | seeEmailAttachmentCount(1);
92 | ?>
93 |
94 | * Param $expectCount
95 |
96 | ### seeAttachmentInLastEmail
97 |
98 | Checks that last email contains an attachment with filename.
99 |
100 | Example:
101 |
102 | seeAttachmentInLastEmail('image.jpg');
104 | ?>
105 |
106 | * Param $filename
107 |
108 | ### seeInLastEmail
109 |
110 | Checks that an email contains a value. It searches the full raw text of the
111 | email: headers, subject line, and body.
112 |
113 | Example:
114 |
115 | seeInLastEmail('Thanks for signing up!');
117 | ?>
118 |
119 | * Param $text
120 |
121 | ### seeInLastEmailTo
122 |
123 | Checks that the last email sent to an address contains a value. It searches the
124 | full raw text of the email: headers, subject line, and body.
125 |
126 | This is useful if, for example a page triggers both an email to the new user,
127 | and to the administrator.
128 |
129 | Example:
130 |
131 | seeInLastEmailTo('user@example.com', 'Thanks for signing up!');
133 | $I->seeInLastEmailTo('admin@example.com', 'A new user has signed up!');
134 | ?>
135 |
136 | * Param $email
137 | * Param $text
138 |
139 | ### dontSeeInLastEmail
140 |
141 | Checks that an email does NOT contain a value. It searches the full raw text of the
142 | email: headers, subject line, and body.
143 |
144 | Example:
145 |
146 | dontSeeInLastEmail('Hit me with those laser beams');
148 | ?>
149 |
150 | * Param $text
151 |
152 | ### dontSeeInLastEmailTo
153 |
154 | Checks that the last email sent to an address does NOT contain a value. It searches the
155 | full raw text of the email: headers, subject line, and body.
156 |
157 | Example:
158 |
159 | dontSeeInLastEmailTo('admin@example.com', 'But shoot it in the right direction');
161 | ?>
162 |
163 | * Param $email
164 | * Param $text
165 |
166 | ### grabAttachmentsFromLastEmail
167 |
168 | Grab Attachments From Email
169 |
170 | Returns array with the format [ [filename1 => bytes1], [filename2 => bytes2], ...]
171 |
172 | Example:
173 |
174 | grabAttachmentsFromLastEmail();
176 | ?>
177 |
178 | ### grabMatchesFromLastEmail
179 |
180 | Extracts an array of matches and sub-matches from the last email based on
181 | a regular expression. It searches the full raw text of the email: headers,
182 | subject line, and body. The return value is an array like that returned by
183 | `preg_match()`.
184 |
185 | Example:
186 |
187 | grabMatchesFromLastEmail('@(.*)@');
189 | ?>
190 |
191 | * Param $regex
192 |
193 | ### grabFromLastEmail
194 |
195 | Extracts a string from the last email based on a regular expression.
196 | It searches the full raw text of the email: headers, subject line, and body.
197 |
198 | Example:
199 |
200 | grabFromLastEmail('@(.*)@');
202 | ?>
203 |
204 | * Param $regex
205 |
206 | ### grabUrlsFromLastEmail
207 |
208 | Extracts an array of urls from the last email.
209 | It searches the full raw body of the email.
210 | The return value is an array of strings.
211 |
212 | Example:
213 |
214 | grabUrlsFromLastEmail();
216 | ?>
217 |
218 | ### lastMessageFrom
219 |
220 | Grab the full email object sent to an address.
221 |
222 | Example:
223 |
224 | lastMessageFrom('example@example.com');
226 | $I->assertNotEmpty($email['attachments']);
227 | ?>
228 |
229 | ### lastMessage
230 |
231 | Grab the full email object from the last email.
232 |
233 | Example:
234 |
235 | grabLastEmail();
237 | $I->assertNotEmpty($email['attachments']);
238 | ?>
239 |
240 | ### grabMatchesFromLastEmailTo
241 |
242 | Extracts an array of matches and sub-matches from the last email to a given
243 | address based on a regular expression. It searches the full raw text of the
244 | email: headers, subject line, and body. The return value is an array like that
245 | returned by `preg_match()`.
246 |
247 | Example:
248 |
249 | grabMatchesFromLastEmailTo('user@example.com', '@(.*)@');
251 | ?>
252 |
253 | * Param $email
254 | * Param $regex
255 |
256 | ### grabFromLastEmailTo
257 |
258 | Extracts a string from the last email to a given address based on a regular
259 | expression. It searches the full raw text of the email: headers, subject
260 | line, and body.
261 |
262 | Example:
263 |
264 | grabFromLastEmailTo('user@example.com', '@(.*)@');
266 | ?>
267 |
268 | * Param $email
269 | * Param $regex
270 |
271 | ### seeEmailCount
272 |
273 | Asserts that a certain number of emails have been sent since the last time
274 | `resetEmails()` was called.
275 |
276 | Example:
277 |
278 | seeEmailCount(2);
280 | ?>
281 |
282 | * Param $count
283 |
284 | # License
285 |
286 | Released under the same license as Codeception: [MIT](https://github.com/captbaritone/codeception-mailcatcher-module/blob/master/LICENSE)
287 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "captbaritone/mailcatcher-codeception-module",
3 | "description": "Test emails in your Codeception acceptance tests",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "Jordan Eldredge",
8 | "email": "jordan@jordaneldredge.com"
9 | },
10 | {
11 | "name": "James King",
12 | "email": "james@jamesking.dev"
13 | }
14 | ],
15 | "require": {
16 | "php": "^7.4 || ^8.0",
17 | "ext-json": "*",
18 | "guzzlehttp/guzzle": "^6.0 || ^7.0",
19 | "codeception/codeception": "^4.0 || ^5.0",
20 | "zbateson/mail-mime-parser": "^1.2"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Codeception\\": "src"
25 | }
26 | },
27 | "require-dev": {
28 | "codeception/module-asserts": "^1.1 || ^2",
29 | "phpmailer/phpmailer": "^6.1.6"
30 | },
31 | "suggest": {
32 | "codeception/module-asserts": "Required if using Codeception >= 4.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Module/MailCatcher.php:
--------------------------------------------------------------------------------
1 | config['url'], '/') . ':' . $this->config['port'];
56 |
57 | $guzzleConfig = [
58 | 'base_uri' => $base_uri
59 | ];
60 | if (isset($this->config['guzzleRequestOptions'])) {
61 | $guzzleConfig = array_merge($guzzleConfig, $this->config['guzzleRequestOptions']);
62 | }
63 |
64 | $this->mailcatcher = new Client($guzzleConfig);
65 | }
66 |
67 |
68 | /**
69 | * Reset emails
70 | *
71 | * Clear all emails from mailcatcher. You probably want to do this before
72 | * you do the thing that will send emails
73 | *
74 | * @author Jordan Eldredge
75 | **/
76 | public function resetEmails(): void
77 | {
78 | $this->mailcatcher->delete('/messages');
79 | }
80 |
81 |
82 | /**
83 | * See In Last Email
84 | *
85 | * Look for a string in the most recent email
86 | *
87 | * @author Jordan Eldredge
88 | **/
89 | public function seeInLastEmail(string $expected): void
90 | {
91 | $email = $this->lastMessage();
92 | $this->seeInEmail($email, $expected);
93 | }
94 |
95 | /**
96 | * See In Last Email subject
97 | *
98 | * Look for a string in the most recent email subject
99 | *
100 | * @author Antoine Augusti
101 | **/
102 | public function seeInLastEmailSubject(string $expected): void
103 | {
104 | $email = $this->lastMessage();
105 | $this->seeInEmailSubject($email, $expected);
106 | }
107 |
108 | /**
109 | * Don't See In Last Email subject
110 | *
111 | * Look for the absence of a string in the most recent email subject
112 | **/
113 | public function dontSeeInLastEmailSubject(string $expected): void
114 | {
115 | $email = $this->lastMessage();
116 | $this->dontSeeInEmailSubject($email, $expected);
117 | }
118 |
119 | /**
120 | * Don't See In Last Email
121 | *
122 | * Look for the absence of a string in the most recent email
123 | **/
124 | public function dontSeeInLastEmail(string $unexpected): void
125 | {
126 | $email = $this->lastMessage();
127 | $this->dontSeeInEmail($email, $unexpected);
128 | }
129 |
130 | /**
131 | * See In Last Email To
132 | *
133 | * Look for a string in the most recent email sent to $address
134 | *
135 | * @author Jordan Eldredge
136 | **/
137 | public function seeInLastEmailTo(string $address, string $expected): void
138 | {
139 | $email = $this->lastMessageTo($address);
140 | $this->seeInEmail($email, $expected);
141 | }
142 |
143 | /**
144 | * Don't See In Last Email To
145 | *
146 | * Look for the absence of a string in the most recent email sent to $address
147 | **/
148 | public function dontSeeInLastEmailTo(string $address, string $unexpected): void
149 | {
150 | $email = $this->lastMessageTo($address);
151 | $this->dontSeeInEmail($email, $unexpected);
152 | }
153 |
154 | /**
155 | * See In Last Email Subject To
156 | *
157 | * Look for a string in the most recent email subject sent to $address
158 | *
159 | * @author Antoine Augusti
160 | **/
161 | public function seeInLastEmailSubjectTo(string $address, string $expected): void
162 | {
163 | $email = $this->lastMessageTo($address);
164 | $this->seeInEmailSubject($email, $expected);
165 | }
166 |
167 | /**
168 | * Don't See In Last Email Subject To
169 | *
170 | * Look for the absence of a string in the most recent email subject sent to $address
171 | **/
172 | public function dontSeeInLastEmailSubjectTo(string $address, string $unexpected): void
173 | {
174 | $email = $this->lastMessageTo($address);
175 | $this->dontSeeInEmailSubject($email, $unexpected);
176 | }
177 |
178 | public function lastMessage(): \Codeception\Util\Email
179 | {
180 | $messages = $this->messages();
181 | if (empty($messages)) {
182 | $this->fail("No messages received");
183 | }
184 |
185 | $last = array_shift($messages);
186 |
187 | return $this->emailFromId($last['id']);
188 | }
189 |
190 | public function lastMessageTo(string $address): \Codeception\Util\Email
191 | {
192 | $ids = [];
193 | $messages = $this->messages();
194 | if (empty($messages)) {
195 | $this->fail("No messages received");
196 | }
197 |
198 | foreach ($messages as $message) {
199 | foreach ($message['recipients'] as $recipient) {
200 | if (strpos($recipient, $address) !== false) {
201 | $ids[] = $message['id'];
202 | }
203 | }
204 | }
205 |
206 | if (count($ids) === 0) {
207 | $this->fail("No messages sent to {$address}");
208 | }
209 |
210 | return $this->emailFromId(max($ids));
211 | }
212 |
213 | public function lastMessageFrom(string $address): \Codeception\Util\Email
214 | {
215 | $ids = [];
216 | $messages = $this->messages();
217 | if (empty($messages)) {
218 | $this->fail("No messages received");
219 | }
220 |
221 | foreach ($messages as $message) {
222 | if (strpos($message['sender'], $address) !== false) {
223 | $ids[] = $message['id'];
224 | }
225 |
226 | // @todo deprecated, remove
227 | foreach ($message['recipients'] as $recipient) {
228 | if (strpos($recipient, $address) !== false) {
229 | trigger_error('`lastMessageFrom` no longer accepts a recipient email.', E_USER_DEPRECATED);
230 | $ids[] = $message['id'];
231 | }
232 | }
233 | }
234 |
235 | if (count($ids) === 0) {
236 | $this->fail("No messages sent from {$address}");
237 | }
238 |
239 | return $this->emailFromId(max($ids));
240 | }
241 |
242 | /**
243 | * Grab Matches From Last Email
244 | *
245 | * Look for a regex in the email source and return it's matches
246 | *
247 | * @author Stephan Hochhaus
248 | * @return mixed[]
249 | **/
250 | public function grabMatchesFromLastEmail(string $regex): array
251 | {
252 | $email = $this->lastMessage();
253 | return $this->grabMatchesFromEmail($email, $regex);
254 | }
255 |
256 | /**
257 | * Grab From Last Email
258 | *
259 | * Look for a regex in the email source and return it
260 | *
261 | * @author Stephan Hochhaus
262 | **/
263 | public function grabFromLastEmail(string $regex): string
264 | {
265 | $matches = $this->grabMatchesFromLastEmail($regex);
266 | return $matches[0];
267 | }
268 |
269 | /**
270 | * Grab Matches From Last Email To
271 | *
272 | * Look for a regex in most recent email sent to $addres email source and
273 | * return it's matches
274 | *
275 | * @author Stephan Hochhaus
276 | * @return mixed[]
277 | **/
278 | public function grabMatchesFromLastEmailTo(string $address, string $regex): array
279 | {
280 | $email = $this->lastMessageTo($address);
281 | return $this->grabMatchesFromEmail($email, $regex);
282 | }
283 |
284 | /**
285 | * Grab From Last Email To
286 | *
287 | * Look for a regex in most recent email sent to $addres email source and
288 | * return it
289 | *
290 | * @author Stephan Hochhaus
291 | **/
292 | public function grabFromLastEmailTo(string $address, string $regex): string
293 | {
294 | $matches = $this->grabMatchesFromLastEmailTo($address, $regex);
295 | return $matches[0];
296 | }
297 |
298 | /**
299 | * Grab Urls From Email
300 | *
301 | * Return the urls the email contains
302 | *
303 | * @author Marcelo Briones
304 | * @return mixed[]
305 | */
306 | public function grabUrlsFromLastEmail(): array
307 | {
308 | $regex = '#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#';
309 | $email = $this->lastMessage();
310 |
311 | $message = Message::from($email->getSource());
312 |
313 | $text = $message->getTextContent();
314 | preg_match_all($regex, $text, $text_matches);
315 |
316 | $html = $message->getHtmlContent();
317 | preg_match_all($regex, $html, $html_matches);
318 |
319 | return array_merge($text_matches[0], $html_matches[0]);
320 | }
321 |
322 | /**
323 | * Grab Attachments From Email
324 | *
325 | * Returns array with the format [ [filename1 => bytes1], [filename2 => bytes2], ...]
326 | *
327 | * @return array
328 | * @author Marcelo Briones
329 | */
330 | public function grabAttachmentsFromLastEmail(): array
331 | {
332 | $email = $this->lastMessage();
333 |
334 | $message = Message::from($email->getSource());
335 |
336 | $attachments = [];
337 |
338 | foreach ($message->getAllAttachmentParts() as $attachmentPart) {
339 | $filename = $attachmentPart->getFilename();
340 | $content = $attachmentPart->getContent();
341 | $attachments[$filename] = $content;
342 | }
343 |
344 | return $attachments;
345 | }
346 |
347 | /**
348 | * See Attachment In Last Email
349 | *
350 | * Look for a attachement with certain filename in the most recent email
351 | *
352 | * @author Marcelo Briones
353 | **/
354 | public function seeAttachmentInLastEmail(string $expectedFilename): void
355 | {
356 | $email = $this->lastMessage();
357 | $message = Message::from($email->getSource());
358 |
359 | foreach ($message->getAllAttachmentParts() as $attachmentPart) {
360 | if ($attachmentPart->getFilename() === $expectedFilename) {
361 | return;
362 | }
363 | }
364 | $this->fail("Filename not found in attachments.");
365 | }
366 |
367 | /**
368 | * Test email count equals expected value
369 | *
370 | * @author Mike Crowe
371 | **/
372 | public function seeEmailCount(int $expected): void
373 | {
374 | $messages = $this->messages();
375 | $count = count($messages);
376 | $this->assertEquals($expected, $count);
377 | }
378 |
379 | /**
380 | * Checks expected count of attachment in last email.
381 | *
382 | * @author Marcelo Briones
383 | **/
384 | public function seeEmailAttachmentCount(int $expectedCount): void
385 | {
386 | $email = $this->lastMessage();
387 | $message = Message::from($email->getSource());
388 | $this->assertEquals($expectedCount, $message->getAttachmentCount());
389 | }
390 |
391 | // ----------- HELPER METHODS BELOW HERE -----------------------//
392 | /**
393 | * Messages
394 | *
395 | * Get an array of all the message objects
396 | *
397 | * @author Jordan Eldredge
398 | **/
399 | protected function messages(): array
400 | {
401 | $response = $this->mailcatcher->get('/messages');
402 | $messages = json_decode($response->getBody(), true);
403 | // Ensure messages are shown in the order they were recieved
404 | // https://github.com/sj26/mailcatcher/pull/184
405 | usort($messages, function ($messageA, $messageB): int {
406 | $sortKeyA = $messageA['created_at'] . $messageA['id'];
407 | $sortKeyB = $messageB['created_at'] . $messageB['id'];
408 | return ($sortKeyA > $sortKeyB) ? -1 : 1;
409 | });
410 | return $messages;
411 | }
412 |
413 | /**
414 | * @param int|string $id
415 | */
416 | protected function emailFromId($id): \Codeception\Util\Email
417 | {
418 | $response = $this->mailcatcher->get("/messages/{$id}.json");
419 | $plainMessage = $this->mailcatcher->get("/messages/{$id}.source");
420 | $messageData = json_decode($response->getBody(), true);
421 | $messageData['source'] = $plainMessage->getBody()->getContents();
422 |
423 | return Email::createFromMailcatcherData($messageData);
424 | }
425 |
426 | protected function seeInEmailSubject(Email $email, string $expected): void
427 | {
428 | if(method_exists($this, 'assertStringContainsString')){
429 | $this->assertStringContainsString($expected, $email->getSubject(), "Email Subject Contains");
430 | }else{
431 | $this->assertContains($expected, $email->getSubject(), "Email Subject Contains");
432 | }
433 | }
434 |
435 | protected function dontSeeInEmailSubject(Email $email, string $unexpected): void
436 | {
437 | if(method_exists($this, 'assertStringContainsString')){
438 | $this->assertStringNotContainsString($unexpected, $email->getSubject(), "Email Subject Does Not Contain");
439 | }else{
440 | $this->assertNotContains($unexpected, $email->getSubject(), "Email Subject Does Not Contain");
441 | }
442 | }
443 |
444 | protected function seeInEmail(Email $email, string $expected): void
445 | {
446 | if(method_exists($this, 'assertStringContainsString')){
447 | $this->assertStringContainsString($expected, $email->getSourceQuotedPrintableDecoded(), "Email Contains");
448 | }else{
449 | $this->assertContains($expected, $email->getSourceQuotedPrintableDecoded(), "Email Contains");
450 | }
451 | }
452 |
453 | protected function dontSeeInEmail(Email $email, string $unexpected): void
454 | {
455 | if(method_exists($this, 'assertStringContainsString')){
456 | $this->assertStringNotContainsString($unexpected, $email->getSourceQuotedPrintableDecoded(), "Email Does Not Contain");
457 | }else{
458 | $this->assertNotContains($unexpected, $email->getSourceQuotedPrintableDecoded(), "Email Does Not Contain");
459 | }
460 | }
461 |
462 | protected function grabMatchesFromEmail(Email $email, string $regex): array
463 | {
464 | preg_match($regex, $email->getSourceQuotedPrintableDecoded(), $matches);
465 | $this->assertNotEmpty($matches, "No matches found for $regex");
466 | return $matches;
467 | }
468 | }
469 |
--------------------------------------------------------------------------------
/src/Util/Email.php:
--------------------------------------------------------------------------------
1 | id = $id;
33 | $this->recipients = $recipients;
34 | $this->subject = $subject;
35 | $this->source = $source;
36 | }
37 |
38 | public function getId(): int
39 | {
40 | return $this->id;
41 | }
42 |
43 | /**
44 | * @return string[]
45 | */
46 | public function getRecipients(): array
47 | {
48 | return $this->recipients;
49 | }
50 |
51 | public function getSubject(): string
52 | {
53 | return $this->subject;
54 | }
55 |
56 | public function getSource(): string
57 | {
58 | return $this->source;
59 | }
60 |
61 | public function getSourceQuotedPrintableDecoded(): string
62 | {
63 | return quoted_printable_decode($this->source);
64 | }
65 |
66 | public static function createFromMailcatcherData(array $data): \Codeception\Util\Email
67 | {
68 | return new self($data['id'], $data['recipients'], $data['subject'], $data['source']);
69 | }
70 | }
--------------------------------------------------------------------------------