Fix for WebTest_Mailing_SpoolTest
[civicrm-core.git] / tests / phpunit / CiviTest / CiviMailUtils.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License along with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
25 */
26
27 /**
28 * Mail utils for use during unit testing to allow retrieval
29 * and examination of 'sent' emails.
30 *
31 * Basic usage:
32 *
33 * $mut = new CiviMailUtils( $this, true ); //true automatically starts spooling
34 * ... do stuff ...
35 * $msg = $mut->getMostRecentEmail( 'raw' ); // or 'ezc' to get an ezc mail object
36 * ... assert stuff about $msg ...
37 * $mut->stop();
38 *
39 *
40 * @package CiviCRM
41 */
42
43 require_once 'ezc/Base/src/ezc_bootstrap.php';
44 require_once 'ezc/autoload/mail_autoload.php';
45
46 /**
47 * Class CiviMailUtils
48 */
49 class CiviMailUtils extends PHPUnit_Framework_TestCase {
50
51 /**
52 * @var mixed current outbound email option
53 */
54 protected $_outBound_option = NULL;
55
56 /**
57 * @var bool is this a webtest
58 */
59 protected $_webtest = FALSE;
60
61 /**
62 * Constructor
63 *
64 * @param $unit_test CiviSeleniumTestCase The currently running test
65 * @param $startImmediately bool Start writing to db now or wait until start() is called
66 */
67 function __construct(&$unit_test, $startImmediately = TRUE) {
68 // Check if running under webtests or not
69 if (is_subclass_of($unit_test, 'CiviSeleniumTestCase')) {
70 $this->_ut = $unit_test;
71 $this->_webtest = TRUE;
72 }
73
74 if ($startImmediately) {
75 $this->start();
76 }
77 }
78
79 /**
80 * Start writing emails to db instead of current option
81 */
82 function start() {
83 if ($this->_webtest) {
84 // Change outbound mail setting
85 $this->_ut->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next");
86
87 // First remember the current setting
88 $this->_outBound_option = $this->getSelectedOutboundOption();
89
90 $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB . '"]');
91 $this->_ut->clickLink("_qf_Smtp_next");
92
93 // Is there supposed to be a status message displayed when outbound email settings are changed?
94 // assert something?
95
96 }
97 else {
98
99 // save current setting for outbound option, then change it
100 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
101 'mailing_backend'
102 );
103
104 $this->_outBound_option = $mailingBackend['outBound_option'];
105 $mailingBackend['outBound_option'] = CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB;
106
107 CRM_Core_BAO_Setting::setItem($mailingBackend,
108 CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
109 'mailing_backend'
110 );
111
112 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
113 'mailing_backend'
114 );
115 }
116 }
117
118 function stop() {
119 if ($this->_webtest) {
120 // Change outbound mail setting
121 $this->_ut->openCiviPage('admin/setting/smtp', "reset=1", "_qf_Smtp_next");
122 $this->_ut->click('xpath=//input[@name="outBound_option" and @value="' . $this->_outBound_option . '"]');
123 $this->_ut->clickLink("_qf_Smtp_next");
124
125 // Is there supposed to be a status message displayed when outbound email settings are changed?
126 // assert something?
127
128 }
129 else {
130
131 $mailingBackend = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
132 'mailing_backend'
133 );
134
135 $mailingBackend['outBound_option'] = $this->_outBound_option;
136
137 CRM_Core_BAO_Setting::setItem($mailingBackend,
138 CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
139 'mailing_backend'
140 );
141 }
142 }
143
144 /**
145 * @param string $type
146 *
147 * @return ezcMail|string
148 */
149 function getMostRecentEmail($type = 'raw') {
150 $msg = '';
151
152 if ($this->_webtest) {
153 // I don't understand but for some reason we have to load the page twice for a recent mailing to appear.
154 $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1');
155 $this->_ut->openCiviPage('mailing/browse/archived', 'reset=1', 'css=td.crm-mailing-name');
156 }
157 // We can't fetch mailing headers from webtest so we'll only try if the format is raw
158 if ($this->_webtest && $type == 'raw') {
159 // 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.
160 $this->_ut->clickLink('xpath=//tr[contains(@id, "crm-mailing_")]//a[text()="Report"]');
161
162 // Also not sure how robust this is, but there isn't a good
163 // identifier for this link either.
164 $this->_ut->waitForElementPresent('xpath=//a[contains(text(), "View complete message")]');
165 $this->_ut->clickAjaxLink('xpath=//a[contains(text(), "View complete message")]', NULL);
166 $msg = $this->_ut->getText('css=.ui-dialog-content.crm-ajax-container');
167 }
168 else {
169 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
170 if ($dao->fetch()) {
171 $msg = $dao->headers . "\n\n" . $dao->body;
172 }
173 $dao->free();
174 }
175
176 switch ($type) {
177 case 'raw':
178 // nothing to do
179 break;
180 case 'ezc':
181 $msg = $this->convertToEzc($msg);
182 break;
183 }
184 return $msg;
185 }
186
187 /**
188 * @param string $type 'raw'|'ezc'
189 *
190 * @throws Exception
191 * @return array(ezcMail)|array(string)
192 */
193 function getAllMessages($type = 'raw') {
194 $msgs = array();
195
196 if ($this->_webtest) {
197 throw new Exception("Not implementated: getAllMessages for WebTest");
198 }
199 else {
200 $dao = CRM_Core_DAO::executeQuery('SELECT headers, body FROM civicrm_mailing_spool ORDER BY id');
201 while ($dao->fetch()) {
202 $msgs[] = $dao->headers . "\n\n" . $dao->body;
203 }
204 $dao->free();
205 }
206
207 switch ($type) {
208 case 'raw':
209 // nothing to do
210 break;
211 case 'ezc':
212 foreach ($msgs as $i => $msg) {
213 $msgs[$i] = $this->convertToEzc($msg);
214 }
215 break;
216 }
217
218 return $msgs;
219 }
220
221 /**
222 * @return int
223 */
224 function getSelectedOutboundOption() {
225 $selectedOption = CRM_Mailing_Config::OUTBOUND_OPTION_MAIL;
226 // Is there a better way to do this? How do you get the currently selected value of a radio button in selenium?
227 for ($i = 0; $i <= 5; $i++) {
228 if ($i != CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) {
229 if ($this->_ut->getValue('xpath=//input[@name="outBound_option" and @value="' . $i . '"]') == "on") {
230 $selectedOption = $i;
231 break;
232 }
233 }
234 }
235 return $selectedOption;
236 }
237
238 /*
239 * Utility functions (previously part of CiviUnitTestCase)
240 * Included for backward compatibility with existing tests.
241 */
242
243 /**
244 * Check contents of mail log
245 *
246 * @param array $strings strings that should be included
247 * @param array $absentStrings strings that should not be included
248 * @param string $prefix
249 *
250 * @return \ezcMail|string
251 */
252 function checkMailLog($strings, $absentStrings = array(), $prefix = '') {
253 $mail = $this->getMostRecentEmail('raw');
254 foreach ($strings as $string) {
255 $this->_ut->assertContains($string, $mail, "$string . not found in $mail $prefix");
256 }
257 foreach ($absentStrings as $string) {
258 $this->_ut->assertEmpty(strstr($mail, $string), "$string incorrectly found in $mail $prefix");
259 ;
260 }
261 return $mail;
262 }
263
264 /**
265 * Check that mail log is empty
266 */
267 function assertMailLogEmpty($prefix = '') {
268 $mail = $this->getMostRecentEmail('raw');
269 $this->_ut->assertEmpty($mail, 'mail sent when it should not have been ' . $prefix);
270 }
271
272 /**
273 * Assert that $expectedRecipients (and no else) have received emails
274 *
275 * @param array $expectedRecipients array($msgPos => array($recipPos => $emailAddr))
276 */
277 function assertRecipients($expectedRecipients) {
278 $recipients = array();
279 foreach ($this->getAllMessages('ezc') as $message) {
280 $recipients[] = CRM_Utils_Array::collect('email', $message->to);
281 }
282 sort($recipients);
283 sort($expectedRecipients);
284 $this->_ut->assertEquals(
285 $expectedRecipients,
286 $recipients,
287 "Incorrect recipients: " . print_r(array('expected' => $expectedRecipients, 'actual' => $recipients), TRUE)
288 );
289 }
290
291 /**
292 * Remove any sent messages from the log
293 */
294 function clearMessages() {
295 if ($this->_webtest) {
296 throw new Exception("Not implementated: clearMessages for WebTest");
297 }
298 else {
299 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_mailing_spool ORDER BY id DESC LIMIT 1');
300 }
301 }
302
303 /**
304 * @param string $msg email header and body
305 * @return ezcMail
306 */
307 private function convertToEzc($msg) {
308 $set = new ezcMailVariableSet($msg);
309 $parser = new ezcMailParser();
310 $mail = $parser->parseMail($set);
311 $this->_ut->assertNotEmpty($mail, 'Cannot parse mail');
312 return $mail[0];
313 }
314 }