Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
7d61e75f | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
7d61e75f TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * Mail utils for use during unit testing to allow retrieval | |
14 | * and examination of 'sent' emails. | |
15 | * | |
16 | * Basic usage: | |
17 | * | |
18 | * $mut = new CiviMailUtils( $this, true ); //true automatically starts spooling | |
19 | * ... do stuff ... | |
20 | * $msg = $mut->getMostRecentEmail( 'raw' ); // or 'ezc' to get an ezc mail object | |
21 | * ... assert stuff about $msg ... | |
22 | * $mut->stop(); | |
23 | * | |
24 | * | |
25 | * @package CiviCRM | |
26 | */ | |
27 | ||
4cbe18b8 EM |
28 | /** |
29 | * Class CiviMailUtils | |
30 | */ | |
a6439b6a | 31 | class CiviMailUtils extends PHPUnit\Framework\TestCase { |
6a488035 TO |
32 | |
33 | /** | |
1d3260ea SL |
34 | * Current outbound email option |
35 | * @var mixed | |
6a488035 TO |
36 | */ |
37 | protected $_outBound_option = NULL; | |
38 | ||
39 | /** | |
1d3260ea SL |
40 | * Is this a webtest |
41 | * @var bool | |
6a488035 TO |
42 | */ |
43 | protected $_webtest = FALSE; | |
44 | ||
45 | /** | |
eceb18cc | 46 | * Constructor. |
6a488035 | 47 | * |
858d0bf8 | 48 | * @param CiviSeleniumTestCase|CiviUnitTestCase $unit_test The currently running test |
e16033b4 TO |
49 | * @param bool $startImmediately |
50 | * Start writing to db now or wait until start() is called. | |
6a488035 | 51 | */ |
00be9182 | 52 | public function __construct(&$unit_test, $startImmediately = TRUE) { |
70520a0b CW |
53 | $this->_ut = $unit_test; |
54 | ||
6a488035 TO |
55 | // Check if running under webtests or not |
56 | if (is_subclass_of($unit_test, 'CiviSeleniumTestCase')) { | |
57 | $this->_webtest = TRUE; | |
58 | } | |
59 | ||
60 | if ($startImmediately) { | |
61 | $this->start(); | |
62 | } | |
63 | } | |
64 | ||
65 | /** | |
eceb18cc | 66 | * Start writing emails to db instead of current option. |
6a488035 | 67 | */ |
00be9182 | 68 | public function start() { |
6a488035 TO |
69 | if ($this->_webtest) { |
70 | // Change outbound mail setting | |
14d3f751 | 71 | $this->_ut->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next"); |
6a488035 TO |
72 | |
73 | // First remember the current setting | |
74 | $this->_outBound_option = $this->getSelectedOutboundOption(); | |
75 | ||
76 | $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB . '"]'); | |
14d3f751 | 77 | $this->_ut->clickLink("_qf_Smtp_next"); |
6a488035 TO |
78 | |
79 | // Is there supposed to be a status message displayed when outbound email settings are changed? | |
80 | // assert something? | |
81 | ||
82 | } | |
83 | else { | |
84 | ||
85 | // save current setting for outbound option, then change it | |
86 | $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, | |
87 | 'mailing_backend' | |
88 | ); | |
89 | ||
90 | $this->_outBound_option = $mailingBackend['outBound_option']; | |
91 | $mailingBackend['outBound_option'] = CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB; | |
92 | ||
08ef4ddd | 93 | Civi::settings()->set('mailing_backend', $mailingBackend); |
6a488035 TO |
94 | |
95 | $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, | |
96 | 'mailing_backend' | |
97 | ); | |
98 | } | |
99 | } | |
100 | ||
00be9182 | 101 | public function stop() { |
6a488035 | 102 | if ($this->_webtest) { |
054c3522 CW |
103 | if ($this->_outBound_option != CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB) { |
104 | // Change outbound mail setting | |
105 | $this->_ut->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next"); | |
106 | $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . $this->_outBound_option . '"]'); | |
3a35908d | 107 | // There will be a warning when switching from test to live mode |
054c3522 | 108 | if ($this->_outBound_option != CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED) { |
499508f0 | 109 | $this->_ut->getAlert(); |
054c3522 CW |
110 | } |
111 | $this->_ut->clickLink("_qf_Smtp_next"); | |
112 | } | |
6a488035 TO |
113 | } |
114 | else { | |
115 | ||
116 | $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, | |
117 | 'mailing_backend' | |
118 | ); | |
119 | ||
120 | $mailingBackend['outBound_option'] = $this->_outBound_option; | |
121 | ||
08ef4ddd | 122 | Civi::settings()->set('mailing_backend', $mailingBackend); |
6a488035 TO |
123 | } |
124 | } | |
125 | ||
4cbe18b8 EM |
126 | /** |
127 | * @param string $type | |
128 | * | |
129 | * @return ezcMail|string | |
130 | */ | |
00be9182 | 131 | public function getMostRecentEmail($type = 'raw') { |
6a488035 TO |
132 | $msg = ''; |
133 | ||
6a488035 | 134 | if ($this->_webtest) { |
6a488035 | 135 | // I don't understand but for some reason we have to load the page twice for a recent mailing to appear. |
14d3f751 CW |
136 | $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1'); |
137 | $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1', 'css=td.crm-mailing-name'); | |
138 | } | |
139 | // We can't fetch mailing headers from webtest so we'll only try if the format is raw | |
140 | if ($this->_webtest && $type == 'raw') { | |
6a488035 | 141 | // This should select the first "Report" link in the table, which is sorted by Completion Date descending, so in theory is the most recent email. Not sure of a more robust way at the moment. |
b3d40287 | 142 | $this->_ut->clickLink('xpath=//tr[contains(@id, "crm-mailing_")]//a[text()="Report"]'); |
6a488035 TO |
143 | |
144 | // Also not sure how robust this is, but there isn't a good | |
145 | // identifier for this link either. | |
146 | $this->_ut->waitForElementPresent('xpath=//a[contains(text(), "View complete message")]'); | |
a5d61f09 | 147 | $this->_ut->clickAjaxLink('xpath=//a[contains(text(), "View complete message")]'); |
14d3f751 | 148 | $msg = $this->_ut->getText('css=.ui-dialog-content.crm-ajax-container'); |
6a488035 TO |
149 | } |
150 | else { | |
151 | $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1'); | |
152 | if ($dao->fetch()) { | |
153 | $msg = $dao->headers . "\n\n" . $dao->body; | |
154 | } | |
6a488035 TO |
155 | } |
156 | ||
157 | switch ($type) { | |
158 | case 'raw': | |
159 | // nothing to do | |
160 | break; | |
6c6e6187 | 161 | |
6a488035 TO |
162 | case 'ezc': |
163 | $msg = $this->convertToEzc($msg); | |
164 | break; | |
165 | } | |
166 | return $msg; | |
167 | } | |
168 | ||
169 | /** | |
e16033b4 TO |
170 | * @param string $type |
171 | * 'raw'|'ezc'. | |
cbdcc634 | 172 | * |
e88f4fee | 173 | * @throws CRM_Core_Exception |
174 | * | |
6a488035 TO |
175 | * @return array(ezcMail)|array(string) |
176 | */ | |
00be9182 | 177 | public function getAllMessages($type = 'raw') { |
e88f4fee | 178 | $msgs = []; |
6a488035 TO |
179 | |
180 | if ($this->_webtest) { | |
e88f4fee | 181 | throw new CRM_Core_Exception('Not implemented: getAllMessages for WebTest'); |
6a488035 | 182 | } |
e88f4fee | 183 | $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id'); |
184 | while ($dao->fetch()) { | |
185 | $msgs[] = $dao->headers . "\n\n" . $dao->body; | |
6a488035 TO |
186 | } |
187 | ||
188 | switch ($type) { | |
189 | case 'raw': | |
190 | // nothing to do | |
191 | break; | |
6c6e6187 | 192 | |
6a488035 TO |
193 | case 'ezc': |
194 | foreach ($msgs as $i => $msg) { | |
195 | $msgs[$i] = $this->convertToEzc($msg); | |
196 | } | |
197 | break; | |
198 | } | |
199 | ||
200 | return $msgs; | |
201 | } | |
202 | ||
4cbe18b8 EM |
203 | /** |
204 | * @return int | |
205 | */ | |
00be9182 | 206 | public function getSelectedOutboundOption() { |
6a488035 TO |
207 | $selectedOption = CRM_Mailing_Config::OUTBOUND_OPTION_MAIL; |
208 | // Is there a better way to do this? How do you get the currently selected value of a radio button in selenium? | |
209 | for ($i = 0; $i <= 5; $i++) { | |
210 | if ($i != CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) { | |
211 | if ($this->_ut->getValue('xpath=//input[@name="outBound_option" and @value="' . $i . '"]') == "on") { | |
212 | $selectedOption = $i; | |
213 | break; | |
214 | } | |
215 | } | |
216 | } | |
217 | return $selectedOption; | |
218 | } | |
219 | ||
220 | /* | |
221 | * Utility functions (previously part of CiviUnitTestCase) | |
222 | * Included for backward compatibility with existing tests. | |
223 | */ | |
224 | ||
225 | /** | |
eceb18cc | 226 | * Check contents of mail log. |
77b97be7 | 227 | * |
e16033b4 TO |
228 | * @param array $strings |
229 | * Strings that should be included. | |
230 | * @param array $absentStrings | |
231 | * Strings that should not be included. | |
77b97be7 | 232 | * @param string $prefix |
6a488035 | 233 | * |
77b97be7 | 234 | * @return \ezcMail|string |
6a488035 | 235 | */ |
affcc9d2 | 236 | public function checkMailLog($strings, $absentStrings = [], $prefix = '') { |
6a488035 | 237 | $mail = $this->getMostRecentEmail('raw'); |
481312d9 | 238 | return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail); |
239 | } | |
240 | ||
241 | /** | |
242 | * Check contents of mail log. | |
243 | * | |
244 | * @param array $strings | |
245 | * Strings that should be included. | |
246 | * @param array $absentStrings | |
247 | * Strings that should not be included. | |
248 | * @param string $prefix | |
249 | * | |
250 | * @return \ezcMail|string | |
251 | */ | |
affcc9d2 | 252 | public function checkAllMailLog($strings, $absentStrings = [], $prefix = '') { |
481312d9 | 253 | $mails = $this->getAllMessages('raw'); |
254 | $mail = implode(',', $mails); | |
255 | return $this->checkMailForStrings($strings, $absentStrings, $prefix, $mail); | |
6a488035 TO |
256 | } |
257 | ||
258 | /** | |
eceb18cc | 259 | * Check that mail log is empty. |
1e1fdcf6 | 260 | * @param string $prefix |
6a488035 | 261 | */ |
00be9182 | 262 | public function assertMailLogEmpty($prefix = '') { |
6a488035 TO |
263 | $mail = $this->getMostRecentEmail('raw'); |
264 | $this->_ut->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix); | |
265 | } | |
266 | ||
267 | /** | |
268 | * Assert that $expectedRecipients (and no else) have received emails | |
269 | * | |
e16033b4 TO |
270 | * @param array $expectedRecipients |
271 | * Array($msgPos => array($recipPos => $emailAddr)). | |
6a488035 | 272 | */ |
00be9182 | 273 | public function assertRecipients($expectedRecipients) { |
affcc9d2 | 274 | $recipients = []; |
6a488035 TO |
275 | foreach ($this->getAllMessages('ezc') as $message) { |
276 | $recipients[] = CRM_Utils_Array::collect('email', $message->to); | |
277 | } | |
11f535e4 TO |
278 | $cmp = function($a, $b) { |
279 | if ($a[0] == $b[0]) { | |
280 | return 0; | |
281 | } | |
282 | return ($a[0] < $b[0]) ? 1 : -1; | |
283 | }; | |
284 | usort($recipients, $cmp); | |
285 | usort($expectedRecipients, $cmp); | |
6a488035 TO |
286 | $this->_ut->assertEquals( |
287 | $expectedRecipients, | |
288 | $recipients, | |
289 | "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE) | |
290 | ); | |
291 | } | |
292 | ||
f8c3594b TO |
293 | /** |
294 | * Assert that $expectedSubjects (and no other subjects) were sent. | |
295 | * | |
296 | * @param array $expectedSubjects | |
297 | * Array(string $subj). | |
298 | */ | |
299 | public function assertSubjects($expectedSubjects) { | |
affcc9d2 | 300 | $subjects = []; |
f8c3594b TO |
301 | foreach ($this->getAllMessages('ezc') as $message) { |
302 | /** @var ezcMail $message */ | |
303 | $subjects[] = $message->subject; | |
304 | } | |
305 | sort($subjects); | |
306 | sort($expectedSubjects); | |
307 | $this->_ut->assertEquals( | |
308 | $expectedSubjects, | |
309 | $subjects, | |
310 | "Incorrect subjects: " . print_r(array('expected' => $expectedSubjects, 'actual' => $subjects), TRUE) | |
311 | ); | |
312 | } | |
313 | ||
6a488035 | 314 | /** |
eceb18cc | 315 | * Remove any sent messages from the log. |
aa1febb5 | 316 | * |
317 | * @param int $limit | |
b13d4a61 | 318 | * How many recent messages to remove, defaults to 0 (all). |
aa1febb5 | 319 | * |
1e0f58c7 | 320 | * @throws \CRM_Core_Exception |
6a488035 | 321 | */ |
b13d4a61 | 322 | public function clearMessages($limit = 0) { |
6a488035 | 323 | if ($this->_webtest) { |
1e0f58c7 | 324 | throw new \CRM_Core_Exception("Not implemented: clearMessages for WebTest"); |
6a488035 TO |
325 | } |
326 | else { | |
b13d4a61 SL |
327 | $sql = 'DELETE FROM civicrm_mailing_spool ORDER BY id DESC'; |
328 | if ($limit) { | |
329 | $sql .= ' LIMIT ' . $limit; | |
330 | } | |
331 | CRM_Core_DAO::executeQuery($sql); | |
6a488035 TO |
332 | } |
333 | } | |
334 | ||
335 | /** | |
e16033b4 TO |
336 | * @param string $msg |
337 | * Email header and body. | |
6a488035 TO |
338 | * @return ezcMail |
339 | */ | |
340 | private function convertToEzc($msg) { | |
14d3f751 | 341 | $set = new ezcMailVariableSet($msg); |
6a488035 TO |
342 | $parser = new ezcMailParser(); |
343 | $mail = $parser->parseMail($set); | |
344 | $this->_ut->assertNotEmpty($mail, 'Cannot parse mail'); | |
345 | return $mail[0]; | |
346 | } | |
96025800 | 347 | |
481312d9 | 348 | /** |
349 | * @param $strings | |
350 | * @param $absentStrings | |
351 | * @param $prefix | |
352 | * @param $mail | |
353 | * @return mixed | |
354 | */ | |
355 | public function checkMailForStrings($strings, $absentStrings, $prefix, $mail) { | |
356 | foreach ($strings as $string) { | |
357 | $this->_ut->assertContains($string, $mail, "$string . not found in $mail $prefix"); | |
358 | } | |
359 | foreach ($absentStrings as $string) { | |
fe7f4414 | 360 | $this->_ut->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix"); |
481312d9 | 361 | } |
362 | return $mail; | |
363 | } | |
364 | ||
6a488035 | 365 | } |