MailerTest.php 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @since 3.1.0
  12. * @license https://opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Test\TestCase\Mailer;
  15. use BadMethodCallException;
  16. use Cake\Core\Configure;
  17. use Cake\Core\Exception\CakeException;
  18. use Cake\Log\Log;
  19. use Cake\Mailer\AbstractTransport;
  20. use Cake\Mailer\Exception\MissingActionException;
  21. use Cake\Mailer\Mailer;
  22. use Cake\Mailer\Message;
  23. use Cake\Mailer\Transport\DebugTransport;
  24. use Cake\Mailer\TransportFactory;
  25. use Cake\TestSuite\TestCase;
  26. use Cake\View\Exception\MissingTemplateException;
  27. use DateTime;
  28. use InvalidArgumentException;
  29. use RuntimeException;
  30. use stdClass;
  31. use TestApp\Mailer\TestMailer;
  32. use function Cake\Core\env;
  33. class MailerTest extends TestCase
  34. {
  35. /**
  36. * @var array
  37. */
  38. protected $transports = [];
  39. /**
  40. * @var \Cake\Mailer\Mailer
  41. */
  42. protected $mailer;
  43. /**
  44. * setUp
  45. */
  46. public function setUp(): void
  47. {
  48. parent::setUp();
  49. $this->transports = [
  50. 'debug' => [
  51. 'className' => 'Debug',
  52. ],
  53. 'badClassName' => [
  54. 'className' => 'TestFalse',
  55. ],
  56. ];
  57. TransportFactory::setConfig($this->transports);
  58. $this->mailer = new TestMailer();
  59. }
  60. /**
  61. * tearDown method
  62. */
  63. public function tearDown(): void
  64. {
  65. parent::tearDown();
  66. TransportFactory::drop('debug');
  67. TransportFactory::drop('badClassName');
  68. Mailer::drop('test');
  69. Mailer::drop('default');
  70. Log::drop('email');
  71. }
  72. /**
  73. * testTransport method
  74. */
  75. public function testTransport(): void
  76. {
  77. $result = $this->mailer->setTransport('debug');
  78. $this->assertSame($this->mailer, $result);
  79. $result = $this->mailer->getTransport();
  80. $this->assertInstanceOf(DebugTransport::class, $result);
  81. $instance = $this->getMockBuilder(DebugTransport::class)->getMock();
  82. $this->mailer->setTransport($instance);
  83. $this->assertSame($instance, $this->mailer->getTransport());
  84. }
  85. /**
  86. * Test that using unknown transports fails.
  87. */
  88. public function testTransportInvalid(): void
  89. {
  90. $this->expectException(InvalidArgumentException::class);
  91. $this->expectExceptionMessage('The "Invalid" transport configuration does not exist');
  92. $this->mailer->setTransport('Invalid');
  93. }
  94. /**
  95. * Test that using classes with no send method fails.
  96. */
  97. public function testTransportInstanceInvalid(): void
  98. {
  99. $this->expectException(CakeException::class);
  100. $this->mailer->setTransport(new stdClass());
  101. }
  102. /**
  103. * Test that using unknown transports fails.
  104. */
  105. public function testTransportTypeInvalid(): void
  106. {
  107. $this->expectException(InvalidArgumentException::class);
  108. $this->expectExceptionMessage('The value passed for the "$name" argument must be either a string, or an object, integer given.');
  109. $this->mailer->setTransport(123);
  110. }
  111. /**
  112. * testMessage function
  113. */
  114. public function testMessage(): void
  115. {
  116. $message = $this->mailer->getMessage();
  117. $this->assertInstanceOf(Message::class, $message);
  118. $newMessage = new Message();
  119. $this->mailer->setMessage($newMessage);
  120. $this->assertSame($newMessage, $this->mailer->getMessage());
  121. $this->assertNotSame($message, $newMessage);
  122. }
  123. /**
  124. * Test reading/writing configuration profiles.
  125. */
  126. public function testConfig(): void
  127. {
  128. $settings = [
  129. 'to' => 'mark@example.com',
  130. 'from' => 'noreply@example.com',
  131. ];
  132. Mailer::setConfig('test', $settings);
  133. $this->assertEquals($settings, Mailer::getConfig('test'), 'Should be the same.');
  134. $mailer = new Mailer('test');
  135. $this->assertContains($settings['to'], $mailer->getTo());
  136. }
  137. /**
  138. * Test that exceptions are raised on duplicate config set.
  139. */
  140. public function testConfigErrorOnDuplicate(): void
  141. {
  142. $this->expectException(BadMethodCallException::class);
  143. $settings = [
  144. 'to' => 'mark@example.com',
  145. 'from' => 'noreply@example.com',
  146. ];
  147. Mailer::setConfig('test', $settings);
  148. Mailer::setConfig('test', $settings);
  149. }
  150. /**
  151. * testConstructWithConfigArray method
  152. */
  153. public function testConstructWithConfigArray(): void
  154. {
  155. $configs = [
  156. 'from' => ['some@example.com' => 'My website'],
  157. 'to' => 'test@example.com',
  158. 'subject' => 'Test mail subject',
  159. 'transport' => 'debug',
  160. ];
  161. $this->mailer = new Mailer($configs);
  162. $result = $this->mailer->getTo();
  163. $this->assertEquals([$configs['to'] => $configs['to']], $result);
  164. $result = $this->mailer->getFrom();
  165. $this->assertEquals($configs['from'], $result);
  166. $result = $this->mailer->getSubject();
  167. $this->assertSame($configs['subject'], $result);
  168. $result = $this->mailer->getTransport();
  169. $this->assertInstanceOf(DebugTransport::class, $result);
  170. $result = $this->mailer->deliver('This is the message');
  171. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  172. $this->assertStringContainsString('To: ', $result['headers']);
  173. }
  174. /**
  175. * testConfigArrayWithLayoutWithoutTemplate method
  176. */
  177. public function testConfigArrayWithLayoutWithoutTemplate(): void
  178. {
  179. $configs = [
  180. 'from' => ['some@example.com' => 'My website'],
  181. 'to' => 'test@example.com',
  182. 'subject' => 'Test mail subject',
  183. 'transport' => 'debug',
  184. 'layout' => 'custom',
  185. ];
  186. $this->mailer = new Mailer($configs);
  187. $template = $this->mailer->viewBuilder()->getTemplate();
  188. $layout = $this->mailer->viewBuilder()->getLayout();
  189. $this->assertNull($template);
  190. $this->assertSame($configs['layout'], $layout);
  191. }
  192. /**
  193. * testConstructWithConfigString method
  194. */
  195. public function testConstructWithConfigString(): void
  196. {
  197. $configs = [
  198. 'from' => ['some@example.com' => 'My website'],
  199. 'to' => 'test@example.com',
  200. 'subject' => 'Test mail subject',
  201. 'transport' => 'debug',
  202. ];
  203. Mailer::setConfig('test', $configs);
  204. $this->mailer = new Mailer('test');
  205. $result = $this->mailer->getTo();
  206. $this->assertEquals([$configs['to'] => $configs['to']], $result);
  207. $result = $this->mailer->getFrom();
  208. $this->assertEquals($configs['from'], $result);
  209. $result = $this->mailer->getSubject();
  210. $this->assertSame($configs['subject'], $result);
  211. $result = $this->mailer->getTransport();
  212. $this->assertInstanceOf('Cake\Mailer\Transport\DebugTransport', $result);
  213. $result = $this->mailer->deliver('This is the message');
  214. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  215. $this->assertStringContainsString('To: ', $result['headers']);
  216. }
  217. /**
  218. * test profile method
  219. */
  220. public function testSetProfile(): void
  221. {
  222. $config = ['to' => 'foo@bar.com'];
  223. $this->mailer->setProfile($config);
  224. $this->assertSame(['foo@bar.com' => 'foo@bar.com'], $this->mailer->getTo());
  225. }
  226. /**
  227. * test that default profile is used by constructor if available.
  228. */
  229. public function testDefaultProfile(): void
  230. {
  231. $config = ['to' => 'foo@bar.com', 'from' => 'from@bar.com'];
  232. Configure::write('Mailer.default', $config);
  233. Mailer::setConfig(Configure::consume('Mailer'));
  234. $mailer = new Mailer();
  235. $this->assertSame(['foo@bar.com' => 'foo@bar.com'], $mailer->getTo());
  236. $this->assertSame(['from@bar.com' => 'from@bar.com'], $mailer->getFrom());
  237. Configure::delete('Mailer');
  238. Mailer::drop('default');
  239. }
  240. /**
  241. * Test that using an invalid profile fails.
  242. */
  243. public function testProfileInvalid(): void
  244. {
  245. $this->expectException(InvalidArgumentException::class);
  246. $this->expectExceptionMessage('Unknown email configuration "derp".');
  247. $mailer = new Mailer();
  248. $mailer->setProfile('derp');
  249. }
  250. /**
  251. * testConfigString method
  252. */
  253. public function testUseConfigString(): void
  254. {
  255. $config = [
  256. 'from' => ['some@example.com' => 'My website'],
  257. 'to' => ['test@example.com' => 'Testname'],
  258. 'subject' => 'Test mail subject',
  259. 'transport' => 'debug',
  260. 'theme' => 'TestTheme',
  261. 'helpers' => ['Html', 'Form'],
  262. 'autoLayout' => false,
  263. ];
  264. Mailer::setConfig('test', $config);
  265. $this->mailer->setProfile('test');
  266. $result = $this->mailer->getTo();
  267. $this->assertEquals($config['to'], $result);
  268. $result = $this->mailer->getFrom();
  269. $this->assertEquals($config['from'], $result);
  270. $result = $this->mailer->getSubject();
  271. $this->assertSame($config['subject'], $result);
  272. $result = $this->mailer->viewBuilder()->getTheme();
  273. $this->assertSame($config['theme'], $result);
  274. $result = $this->mailer->getTransport();
  275. $this->assertInstanceOf(DebugTransport::class, $result);
  276. $result = $this->mailer->viewBuilder()->getHelpers();
  277. $this->assertEquals($config['helpers'], $result);
  278. $this->assertFalse($this->mailer->viewBuilder()->isAutoLayoutEnabled());
  279. Mailer::drop('test');
  280. }
  281. /**
  282. * CakeEmailTest::testMockTransport()
  283. */
  284. public function testMockTransport(): void
  285. {
  286. TransportFactory::drop('default');
  287. $mock = $this->getMockBuilder(AbstractTransport::class)->getMock();
  288. $config = ['from' => 'tester@example.org', 'transport' => 'default'];
  289. Mailer::setConfig('default', $config);
  290. TransportFactory::setConfig('default', $mock);
  291. $em = new Mailer('default');
  292. $this->assertSame($mock, $em->getTransport());
  293. TransportFactory::drop('default');
  294. }
  295. public function testProxies(): void
  296. {
  297. $result = (new Mailer())->setHeaders(['X-Something' => 'nice']);
  298. $this->assertInstanceOf(Mailer::class, $result);
  299. $header = $result->getMessage()->getHeaders();
  300. $this->assertSame('nice', $header['X-Something']);
  301. $result = (new Mailer())->setAttachments([
  302. ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain'],
  303. ]);
  304. $this->assertInstanceOf(Mailer::class, $result);
  305. $this->assertSame(
  306. ['basics.php' => ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain']],
  307. $result->getMessage()->getAttachments()
  308. );
  309. }
  310. /**
  311. * Test that get/set methods can be proxied.
  312. */
  313. public function testGetSetProxies(): void
  314. {
  315. $mailer = new Mailer();
  316. $result = $mailer
  317. ->setTo('test@example.com')
  318. ->setCc('cc@example.com');
  319. $this->assertSame($result, $mailer);
  320. $this->assertSame(['test@example.com' => 'test@example.com'], $result->getTo());
  321. $this->assertSame(['cc@example.com' => 'cc@example.com'], $result->getCc());
  322. }
  323. public function testSet(): void
  324. {
  325. $result = (new Mailer())->setViewVars('key', 'value');
  326. $this->assertInstanceOf(Mailer::class, $result);
  327. $this->assertSame(['key' => 'value'], $result->getRenderer()->viewBuilder()->getVars());
  328. }
  329. /**
  330. * testRenderWithLayoutAndAttachment method
  331. */
  332. public function testRenderWithLayoutAndAttachment(): void
  333. {
  334. $this->mailer->setEmailFormat('html');
  335. $this->mailer->viewBuilder()->setTemplate('html', 'default');
  336. $this->mailer->setAttachments([CAKE . 'basics.php']);
  337. $this->mailer->render();
  338. $result = $this->mailer->getBody();
  339. $this->assertNotEmpty($result);
  340. $result = $this->mailer->getBoundary();
  341. $this->assertMatchesRegularExpression('/^[0-9a-f]{32}$/', $result);
  342. }
  343. public function testSend(): void
  344. {
  345. $mailer = $this->getMockBuilder(Mailer::class)
  346. ->onlyMethods(['deliver'])
  347. ->addMethods(['test'])
  348. ->getMock();
  349. $mailer->expects($this->once())
  350. ->method('test')
  351. ->with('foo', 'bar');
  352. $mailer->expects($this->any())
  353. ->method('deliver')
  354. ->will($this->returnValue([]));
  355. $mailer->send('test', ['foo', 'bar']);
  356. $this->assertNull($mailer->viewBuilder()->getTemplate());
  357. }
  358. /**
  359. * Calling send() with no parameters should not overwrite the view variables.
  360. */
  361. public function testSendWithNoContentDoesNotOverwriteViewVar(): void
  362. {
  363. $this->mailer->reset();
  364. $this->mailer->setTransport('debug');
  365. $this->mailer->setFrom('cake@cakephp.org');
  366. $this->mailer->setTo('you@cakephp.org');
  367. $this->mailer->setSubject('My title');
  368. $this->mailer->setEmailFormat('text');
  369. $this->mailer->viewBuilder()
  370. ->setTemplate('default')
  371. ->setVars([
  372. 'content' => 'A message to you',
  373. ]);
  374. $result = $this->mailer->send();
  375. $this->assertStringContainsString('A message to you', $result['message']);
  376. }
  377. /**
  378. * testSendWithContent method
  379. */
  380. public function testSendWithContent(): void
  381. {
  382. $this->mailer->reset();
  383. $this->mailer->setTransport('debug');
  384. $this->mailer->setFrom('cake@cakephp.org');
  385. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  386. $this->mailer->setSubject('My title');
  387. $this->mailer->setProfile(['empty']);
  388. $result = $this->mailer->deliver("Here is my body, with multi lines.\nThis is the second line.\r\n\r\nAnd the last.");
  389. $expected = ['headers', 'message'];
  390. $this->assertEquals($expected, array_keys($result));
  391. $expected = "Here is my body, with multi lines.\r\nThis is the second line.\r\n\r\nAnd the last.\r\n\r\n";
  392. $this->assertSame($expected, $result['message']);
  393. $this->assertStringContainsString('Date: ', $result['headers']);
  394. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  395. $this->assertStringContainsString('To: ', $result['headers']);
  396. $result = $this->mailer->deliver('Other body');
  397. $expected = "Other body\r\n\r\n";
  398. $this->assertSame($expected, $result['message']);
  399. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  400. $this->assertStringContainsString('To: ', $result['headers']);
  401. }
  402. /**
  403. * test send without a transport method
  404. */
  405. public function testSendWithoutTransport(): void
  406. {
  407. $this->expectException(BadMethodCallException::class);
  408. $this->expectExceptionMessage(
  409. 'Transport was not defined. You must set on using setTransport() or set `transport` option in your mailer profile.'
  410. );
  411. $this->mailer->setTo('cake@cakephp.org');
  412. $this->mailer->setFrom('cake@cakephp.org');
  413. $this->mailer->setSubject('My title');
  414. $this->mailer->send();
  415. }
  416. /**
  417. * Test send() with no template.
  418. */
  419. public function testSendNoTemplateWithAttachments(): void
  420. {
  421. $this->mailer->setTransport('debug');
  422. $this->mailer->setFrom('cake@cakephp.org');
  423. $this->mailer->setTo('cake@cakephp.org');
  424. $this->mailer->setSubject('My title');
  425. $this->mailer->setEmailFormat('text');
  426. $this->mailer->setAttachments([CAKE . 'basics.php']);
  427. $result = $this->mailer->deliver('Hello');
  428. $boundary = $this->mailer->boundary;
  429. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  430. $expected = "--$boundary\r\n" .
  431. "Content-Type: text/plain; charset=UTF-8\r\n" .
  432. "Content-Transfer-Encoding: 8bit\r\n" .
  433. "\r\n" .
  434. 'Hello' .
  435. "\r\n" .
  436. "\r\n" .
  437. "\r\n" .
  438. "--$boundary\r\n" .
  439. "Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
  440. "Content-Type: text/x-php\r\n" .
  441. "Content-Transfer-Encoding: base64\r\n" .
  442. "\r\n";
  443. $this->assertStringContainsString($expected, $result['message']);
  444. }
  445. /**
  446. * Test send() with no template and data string attachment
  447. */
  448. public function testSendNoTemplateWithDataStringAttachment(): void
  449. {
  450. $this->mailer->setTransport('debug');
  451. $this->mailer->setFrom('cake@cakephp.org');
  452. $this->mailer->setTo('cake@cakephp.org');
  453. $this->mailer->setSubject('My title');
  454. $this->mailer->setEmailFormat('text');
  455. $data = file_get_contents(TEST_APP . 'webroot/img/cake.power.gif');
  456. $this->mailer->setAttachments(['cake.icon.gif' => [
  457. 'data' => $data,
  458. 'mimetype' => 'image/gif',
  459. ]]);
  460. $result = $this->mailer->deliver('Hello');
  461. $boundary = $this->mailer->boundary;
  462. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  463. $expected = "--$boundary\r\n" .
  464. "Content-Type: text/plain; charset=UTF-8\r\n" .
  465. "Content-Transfer-Encoding: 8bit\r\n" .
  466. "\r\n" .
  467. 'Hello' .
  468. "\r\n" .
  469. "\r\n" .
  470. "\r\n" .
  471. "--$boundary\r\n" .
  472. "Content-Disposition: attachment; filename=\"cake.icon.gif\"\r\n" .
  473. "Content-Type: image/gif\r\n" .
  474. "Content-Transfer-Encoding: base64\r\n\r\n";
  475. $expected .= chunk_split(base64_encode($data), 76, "\r\n");
  476. $this->assertStringContainsString($expected, $result['message']);
  477. }
  478. /**
  479. * Test send() with no template as both
  480. */
  481. public function testSendNoTemplateWithAttachmentsAsBoth(): void
  482. {
  483. $this->mailer->setTransport('debug');
  484. $this->mailer->setFrom('cake@cakephp.org');
  485. $this->mailer->setTo('cake@cakephp.org');
  486. $this->mailer->setSubject('My title');
  487. $this->mailer->setEmailFormat('both');
  488. $this->mailer->setAttachments([CORE_PATH . 'VERSION.txt']);
  489. $result = $this->mailer->deliver('Hello');
  490. $boundary = $this->mailer->boundary;
  491. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  492. $expected = "--$boundary\r\n" .
  493. "Content-Type: multipart/alternative; boundary=\"alt-$boundary\"\r\n" .
  494. "\r\n" .
  495. "--alt-$boundary\r\n" .
  496. "Content-Type: text/plain; charset=UTF-8\r\n" .
  497. "Content-Transfer-Encoding: 8bit\r\n" .
  498. "\r\n" .
  499. 'Hello' .
  500. "\r\n" .
  501. "\r\n" .
  502. "\r\n" .
  503. "--alt-$boundary\r\n" .
  504. "Content-Type: text/html; charset=UTF-8\r\n" .
  505. "Content-Transfer-Encoding: 8bit\r\n" .
  506. "\r\n" .
  507. 'Hello' .
  508. "\r\n" .
  509. "\r\n" .
  510. "\r\n" .
  511. "--alt-{$boundary}--\r\n" .
  512. "\r\n" .
  513. "--$boundary\r\n" .
  514. "Content-Disposition: attachment; filename=\"VERSION.txt\"\r\n" .
  515. "Content-Type: text/plain\r\n" .
  516. "Content-Transfer-Encoding: base64\r\n" .
  517. "\r\n";
  518. $this->assertStringContainsString($expected, $result['message']);
  519. }
  520. /**
  521. * Test setting inline attachments and messages.
  522. */
  523. public function testSendWithInlineAttachments(): void
  524. {
  525. $this->mailer->setTransport('debug');
  526. $this->mailer->setFrom('cake@cakephp.org');
  527. $this->mailer->setTo('cake@cakephp.org');
  528. $this->mailer->setSubject('My title');
  529. $this->mailer->setEmailFormat('both');
  530. $this->mailer->setAttachments([
  531. 'cake.png' => [
  532. 'file' => CORE_PATH . 'VERSION.txt',
  533. 'contentId' => 'abc123',
  534. ],
  535. ]);
  536. $result = $this->mailer->deliver('Hello');
  537. $boundary = $this->mailer->boundary;
  538. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  539. $expected = "--$boundary\r\n" .
  540. "Content-Type: multipart/related; boundary=\"rel-$boundary\"\r\n" .
  541. "\r\n" .
  542. "--rel-$boundary\r\n" .
  543. "Content-Type: multipart/alternative; boundary=\"alt-$boundary\"\r\n" .
  544. "\r\n" .
  545. "--alt-$boundary\r\n" .
  546. "Content-Type: text/plain; charset=UTF-8\r\n" .
  547. "Content-Transfer-Encoding: 8bit\r\n" .
  548. "\r\n" .
  549. 'Hello' .
  550. "\r\n" .
  551. "\r\n" .
  552. "\r\n" .
  553. "--alt-$boundary\r\n" .
  554. "Content-Type: text/html; charset=UTF-8\r\n" .
  555. "Content-Transfer-Encoding: 8bit\r\n" .
  556. "\r\n" .
  557. 'Hello' .
  558. "\r\n" .
  559. "\r\n" .
  560. "\r\n" .
  561. "--alt-{$boundary}--\r\n" .
  562. "\r\n" .
  563. "--rel-$boundary\r\n" .
  564. "Content-Disposition: inline; filename=\"cake.png\"\r\n" .
  565. "Content-Type: text/plain\r\n" .
  566. "Content-Transfer-Encoding: base64\r\n" .
  567. "Content-ID: <abc123>\r\n" .
  568. "\r\n";
  569. $this->assertStringContainsString($expected, $result['message']);
  570. $this->assertStringContainsString('--rel-' . $boundary . '--', $result['message']);
  571. $this->assertStringContainsString('--' . $boundary . '--', $result['message']);
  572. }
  573. /**
  574. * Test setting inline attachments and HTML only messages.
  575. */
  576. public function testSendWithInlineAttachmentsHtmlOnly(): void
  577. {
  578. $this->mailer->setTransport('debug');
  579. $this->mailer->setFrom('cake@cakephp.org');
  580. $this->mailer->setTo('cake@cakephp.org');
  581. $this->mailer->setSubject('My title');
  582. $this->mailer->setEmailFormat('html');
  583. $this->mailer->setAttachments([
  584. 'cake.png' => [
  585. 'file' => CORE_PATH . 'VERSION.txt',
  586. 'contentId' => 'abc123',
  587. ],
  588. ]);
  589. $result = $this->mailer->deliver('Hello');
  590. $boundary = $this->mailer->boundary;
  591. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  592. $expected = "--$boundary\r\n" .
  593. "Content-Type: multipart/related; boundary=\"rel-$boundary\"\r\n" .
  594. "\r\n" .
  595. "--rel-$boundary\r\n" .
  596. "Content-Type: text/html; charset=UTF-8\r\n" .
  597. "Content-Transfer-Encoding: 8bit\r\n" .
  598. "\r\n" .
  599. 'Hello' .
  600. "\r\n" .
  601. "\r\n" .
  602. "\r\n" .
  603. "--rel-$boundary\r\n" .
  604. "Content-Disposition: inline; filename=\"cake.png\"\r\n" .
  605. "Content-Type: text/plain\r\n" .
  606. "Content-Transfer-Encoding: base64\r\n" .
  607. "Content-ID: <abc123>\r\n" .
  608. "\r\n";
  609. $this->assertStringContainsString($expected, $result['message']);
  610. $this->assertStringContainsString('--rel-' . $boundary . '--', $result['message']);
  611. $this->assertStringContainsString('--' . $boundary . '--', $result['message']);
  612. }
  613. /**
  614. * Test disabling content-disposition.
  615. */
  616. public function testSendWithNoContentDispositionAttachments(): void
  617. {
  618. $this->mailer->setTransport('debug');
  619. $this->mailer->setFrom('cake@cakephp.org');
  620. $this->mailer->setTo('cake@cakephp.org');
  621. $this->mailer->setSubject('My title');
  622. $this->mailer->setEmailFormat('text');
  623. $this->mailer->setAttachments([
  624. 'cake.png' => [
  625. 'file' => CORE_PATH . 'VERSION.txt',
  626. 'contentDisposition' => false,
  627. ],
  628. ]);
  629. $result = $this->mailer->deliver('Hello');
  630. $boundary = $this->mailer->boundary;
  631. $this->assertStringContainsString('Content-Type: multipart/mixed; boundary="' . $boundary . '"', $result['headers']);
  632. $expected = "--$boundary\r\n" .
  633. "Content-Type: text/plain; charset=UTF-8\r\n" .
  634. "Content-Transfer-Encoding: 8bit\r\n" .
  635. "\r\n" .
  636. 'Hello' .
  637. "\r\n" .
  638. "\r\n" .
  639. "\r\n" .
  640. "--{$boundary}\r\n" .
  641. "Content-Type: text/plain\r\n" .
  642. "Content-Transfer-Encoding: base64\r\n" .
  643. "\r\n";
  644. $this->assertStringContainsString($expected, $result['message']);
  645. $this->assertStringContainsString('--' . $boundary . '--', $result['message']);
  646. }
  647. /**
  648. * testSendRender method
  649. */
  650. public function testSendRender(): void
  651. {
  652. $this->mailer->setTransport('debug');
  653. $this->mailer->setFrom('cake@cakephp.org');
  654. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  655. $this->mailer->setSubject('My title');
  656. $this->mailer->setProfile(['empty']);
  657. $this->mailer->viewBuilder()->setTemplate('default', 'default');
  658. $result = $this->mailer->send();
  659. $this->assertStringContainsString('This email was sent using the CakePHP Framework', $result['message']);
  660. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  661. $this->assertStringContainsString('To: ', $result['headers']);
  662. }
  663. /**
  664. * test sending and rendering with no layout
  665. */
  666. public function testSendRenderNoLayout(): void
  667. {
  668. $this->mailer->setTransport('debug');
  669. $this->mailer->setFrom('cake@cakephp.org');
  670. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  671. $this->mailer->setSubject('My title');
  672. $this->mailer->setConfig(['empty']);
  673. $this->mailer->viewBuilder()
  674. ->setTemplate('default')
  675. ->setVar('content', 'message body.')
  676. ->disableAutoLayout();
  677. $result = $this->mailer->send();
  678. $this->assertStringContainsString('message body.', $result['message']);
  679. $this->assertStringNotContainsString('This email was sent using the CakePHP Framework', $result['message']);
  680. }
  681. /**
  682. * testSendRender both method
  683. */
  684. public function testSendRenderBoth(): void
  685. {
  686. $this->mailer->setTransport('debug');
  687. $this->mailer->setFrom('cake@cakephp.org');
  688. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  689. $this->mailer->setSubject('My title');
  690. $this->mailer->setProfile(['empty']);
  691. $this->mailer->viewBuilder()->setTemplate('default', 'default');
  692. $this->mailer->setEmailFormat('both');
  693. $result = $this->mailer->send();
  694. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  695. $this->assertStringContainsString('To: ', $result['headers']);
  696. $boundary = $this->mailer->boundary;
  697. $this->assertStringContainsString('Content-Type: multipart/alternative; boundary="' . $boundary . '"', $result['headers']);
  698. $expected = "--$boundary\r\n" .
  699. "Content-Type: text/plain; charset=UTF-8\r\n" .
  700. "Content-Transfer-Encoding: 8bit\r\n" .
  701. "\r\n" .
  702. "\r\n" .
  703. "\r\n" .
  704. 'This email was sent using the CakePHP Framework, https://cakephp.org.' .
  705. "\r\n" .
  706. "\r\n" .
  707. "\r\n" .
  708. "--$boundary\r\n" .
  709. "Content-Type: text/html; charset=UTF-8\r\n" .
  710. "Content-Transfer-Encoding: 8bit\r\n" .
  711. "\r\n" .
  712. '<!DOCTYPE html';
  713. $this->assertStringStartsWith($expected, $result['message']);
  714. $expected = "</html>\r\n" .
  715. "\r\n" .
  716. "\r\n" .
  717. "\r\n" .
  718. "--$boundary--\r\n";
  719. $this->assertStringEndsWith($expected, $result['message']);
  720. }
  721. /**
  722. * testSendRender method for ISO-2022-JP
  723. */
  724. public function testSendRenderJapanese(): void
  725. {
  726. $this->mailer->setTransport('debug');
  727. $this->mailer->setFrom('cake@cakephp.org');
  728. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  729. $this->mailer->setSubject('My title');
  730. $this->mailer->setProfile(['empty']);
  731. $this->mailer->viewBuilder()->setTemplate('default');
  732. $this->mailer->viewBuilder()->setLayout('japanese');
  733. $this->mailer->setCharset('ISO-2022-JP');
  734. $result = $this->mailer->send();
  735. $expected = mb_convert_encoding('CakePHP Framework を使って送信したメールです。 https://cakephp.org.', 'ISO-2022-JP');
  736. $this->assertStringContainsString($expected, $result['message']);
  737. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  738. $this->assertStringContainsString('To: ', $result['headers']);
  739. }
  740. /**
  741. * testSendRenderThemed method
  742. */
  743. public function testSendRenderThemed(): void
  744. {
  745. $this->loadPlugins(['TestTheme']);
  746. $this->mailer->setTransport('debug');
  747. $this->mailer->setFrom('cake@cakephp.org');
  748. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  749. $this->mailer->setSubject('My title');
  750. $this->mailer->setProfile(['empty']);
  751. $this->mailer->viewBuilder()->setTheme('TestTheme');
  752. $this->mailer->viewBuilder()->setTemplate('themed', 'default');
  753. $result = $this->mailer->send();
  754. $this->assertStringContainsString('In TestTheme', $result['message']);
  755. $this->assertStringContainsString('/test_theme/img/test.jpg', $result['message']);
  756. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  757. $this->assertStringContainsString('To: ', $result['headers']);
  758. $this->assertStringContainsString('/test_theme/img/test.jpg', $result['message']);
  759. $this->clearPlugins();
  760. }
  761. /**
  762. * testSendRenderWithHTML method and assert line length is kept below the required limit
  763. */
  764. public function testSendRenderWithHTML(): void
  765. {
  766. $this->mailer->setTransport('debug');
  767. $this->mailer->setFrom('cake@cakephp.org');
  768. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  769. $this->mailer->setSubject('My title');
  770. $this->mailer->setProfile(['empty']);
  771. $this->mailer->setEmailFormat('html');
  772. $this->mailer->viewBuilder()->setTemplate('html', 'default');
  773. $result = $this->mailer->send();
  774. $this->assertTextContains('<h1>HTML Ipsum Presents</h1>', $result['message']);
  775. $this->assertLineLengths($result['message']);
  776. }
  777. /**
  778. * testSendRenderWithVars method
  779. */
  780. public function testSendRenderWithVars(): void
  781. {
  782. $this->mailer->setTransport('debug');
  783. $this->mailer->setFrom('cake@cakephp.org');
  784. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  785. $this->mailer->setSubject('My title');
  786. $this->mailer->setProfile(['empty']);
  787. $this->mailer->viewBuilder()->setTemplate('custom', 'default');
  788. $this->mailer->setViewVars(['value' => 12345]);
  789. $result = $this->mailer->send();
  790. $this->assertStringContainsString('Here is your value: 12345', $result['message']);
  791. }
  792. /**
  793. * testSendRenderWithVars method for ISO-2022-JP
  794. */
  795. public function testSendRenderWithVarsJapanese(): void
  796. {
  797. $this->mailer->setTransport('debug');
  798. $this->mailer->setFrom('cake@cakephp.org');
  799. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  800. $this->mailer->setSubject('My title');
  801. $this->mailer->setProfile(['empty']);
  802. $this->mailer->viewBuilder()->setTemplate('japanese', 'default');
  803. $this->mailer->setViewVars(['value' => '日本語の差し込み123']);
  804. $this->mailer->setCharset('ISO-2022-JP');
  805. $result = $this->mailer->send();
  806. $expected = mb_convert_encoding('ここにあなたの設定した値が入ります: 日本語の差し込み123', 'ISO-2022-JP');
  807. $this->assertStringContainsString($expected, $result['message']);
  808. }
  809. /**
  810. * testSendRenderWithHelpers method
  811. */
  812. public function testSendRenderWithHelpers(): void
  813. {
  814. $this->mailer->setTransport('debug');
  815. $timestamp = time();
  816. $this->mailer->setFrom('cake@cakephp.org');
  817. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  818. $this->mailer->setSubject('My title');
  819. $this->mailer->setProfile(['empty']);
  820. $this->mailer->viewBuilder()
  821. ->setTemplate('custom_helper')
  822. ->setLayout('default')
  823. ->setHelpers(['Time'], false);
  824. $this->mailer->setViewVars(['time' => $timestamp]);
  825. $result = $this->mailer->send();
  826. $dateTime = new DateTime();
  827. $dateTime->setTimestamp($timestamp);
  828. $this->assertStringContainsString('Right now: ' . $dateTime->format($dateTime::ATOM), $result['message']);
  829. $result = $this->mailer->viewBuilder()->getHelpers();
  830. $this->assertEquals(['Time'], $result);
  831. }
  832. /**
  833. * testSendRenderWithImage method
  834. */
  835. public function testSendRenderWithImage(): void
  836. {
  837. $this->mailer->setTransport('debug');
  838. $this->mailer->setFrom('cake@cakephp.org');
  839. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  840. $this->mailer->setSubject('My title');
  841. $this->mailer->setProfile(['empty']);
  842. $this->mailer->viewBuilder()->setTemplate('image');
  843. $this->mailer->setEmailFormat('html');
  844. $server = env('SERVER_NAME') ? env('SERVER_NAME') : 'localhost';
  845. if (env('SERVER_PORT') && env('SERVER_PORT') !== 80) {
  846. $server .= ':' . env('SERVER_PORT');
  847. }
  848. $expected = '<img src="http://' . $server . '/img/image.gif" alt="cool image" width="100" height="100"';
  849. $result = $this->mailer->send();
  850. $this->assertStringContainsString($expected, $result['message']);
  851. }
  852. /**
  853. * testSendRenderPlugin method
  854. */
  855. public function testSendRenderPlugin(): void
  856. {
  857. $this->loadPlugins(['TestPlugin', 'TestPluginTwo', 'TestTheme']);
  858. $this->mailer->setTransport('debug');
  859. $this->mailer->setFrom('cake@cakephp.org');
  860. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  861. $this->mailer->setSubject('My title');
  862. $this->mailer->setProfile(['empty']);
  863. $this->mailer->viewBuilder()
  864. ->setTemplate('TestPlugin.test_plugin_tpl')
  865. ->setLayout('default');
  866. $result = $this->mailer->send();
  867. $this->assertStringContainsString('Into TestPlugin.', $result['message']);
  868. $this->assertStringContainsString('This email was sent using the CakePHP Framework', $result['message']);
  869. $this->mailer->viewBuilder()
  870. ->setTemplate('TestPlugin.test_plugin_tpl')
  871. ->setLayout('TestPlugin.plug_default');
  872. $result = $this->mailer->send();
  873. $this->assertStringContainsString('Into TestPlugin.', $result['message']);
  874. $this->assertStringContainsString('This email was sent using the TestPlugin.', $result['message']);
  875. $this->mailer->viewBuilder()
  876. ->setTemplate('TestPlugin.test_plugin_tpl')
  877. ->setLayout('plug_default');
  878. $result = $this->mailer->send();
  879. $this->assertStringContainsString('Into TestPlugin.', $result['message']);
  880. $this->assertStringContainsString('This email was sent using the TestPlugin.', $result['message']);
  881. $this->mailer->viewBuilder()
  882. ->setTemplate('TestPlugin.test_plugin_tpl')
  883. ->setLayout('TestPluginTwo.default');
  884. $result = $this->mailer->send();
  885. $this->assertStringContainsString('Into TestPlugin.', $result['message']);
  886. $this->assertStringContainsString('This email was sent using TestPluginTwo.', $result['message']);
  887. // test plugin template overridden by theme
  888. $this->mailer->viewBuilder()->setTheme('TestTheme');
  889. $result = $this->mailer->send();
  890. $this->assertStringContainsString('Into TestPlugin. (themed)', $result['message']);
  891. $this->mailer->setViewVars(['value' => 12345]);
  892. $this->mailer->viewBuilder()
  893. ->setTemplate('custom')
  894. ->setLayout('TestPlugin.plug_default');
  895. $result = $this->mailer->send();
  896. $this->assertStringContainsString('Here is your value: 12345', $result['message']);
  897. $this->assertStringContainsString('This email was sent using the TestPlugin.', $result['message']);
  898. $this->clearPlugins();
  899. }
  900. /**
  901. * Test that a MissingTemplateException is thrown
  902. */
  903. public function testMissingTemplateException(): void
  904. {
  905. $this->expectException(MissingTemplateException::class);
  906. $this->mailer->setTransport('debug');
  907. $this->mailer->setFrom('cake@cakephp.org');
  908. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  909. $this->mailer->setSubject('My title');
  910. $this->mailer->viewBuilder()->setTemplate('fooo');
  911. $this->mailer->send();
  912. }
  913. /**
  914. * testSendMultipleMIME method
  915. */
  916. public function testSendMultipleMIME(): void
  917. {
  918. $this->mailer->setTransport('debug');
  919. $this->mailer->setFrom('cake@cakephp.org');
  920. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  921. $this->mailer->setSubject('My title');
  922. $this->mailer->viewBuilder()->setTemplate('custom', 'default');
  923. $this->mailer->setProfile([]);
  924. $this->mailer->setViewVars(['value' => 12345]);
  925. $this->mailer->setEmailFormat('both');
  926. $this->mailer->send();
  927. $message = $this->mailer->getBody();
  928. $boundary = $this->mailer->boundary;
  929. $this->assertNotEmpty($boundary);
  930. $this->assertContains('--' . $boundary, $message);
  931. $this->assertContains('--' . $boundary . '--', $message);
  932. $this->mailer->setAttachments(['fake.php' => __FILE__]);
  933. $this->mailer->send();
  934. $message = $this->mailer->getBody();
  935. $boundary = $this->mailer->boundary;
  936. $this->assertNotEmpty($boundary);
  937. $this->assertContains('--' . $boundary, $message);
  938. $this->assertContains('--' . $boundary . '--', $message);
  939. $this->assertContains('--alt-' . $boundary, $message);
  940. $this->assertContains('--alt-' . $boundary . '--', $message);
  941. }
  942. /**
  943. * testSendAttachment method
  944. */
  945. public function testSendAttachment(): void
  946. {
  947. $this->mailer->setTransport('debug');
  948. $this->mailer->setFrom('cake@cakephp.org');
  949. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  950. $this->mailer->setSubject('My title');
  951. $this->mailer->setProfile([]);
  952. $this->mailer->setAttachments([CAKE . 'basics.php']);
  953. $this->mailer->setBodyText('body');
  954. $result = $this->mailer->send();
  955. $expected = "Content-Disposition: attachment; filename=\"basics.php\"\r\n" .
  956. "Content-Type: text/x-php\r\n" .
  957. "Content-Transfer-Encoding: base64\r\n";
  958. $this->assertStringContainsString($expected, $result['message']);
  959. $this->mailer->setAttachments(['my.file.txt' => CAKE . 'basics.php']);
  960. $this->mailer->setBodyText('body');
  961. $result = $this->mailer->send();
  962. $expected = "Content-Disposition: attachment; filename=\"my.file.txt\"\r\n" .
  963. "Content-Type: text/x-php\r\n" .
  964. "Content-Transfer-Encoding: base64\r\n";
  965. $this->assertStringContainsString($expected, $result['message']);
  966. $this->mailer->setAttachments(['file.txt' => ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain']]);
  967. $this->mailer->setBodyText('body');
  968. $result = $this->mailer->send();
  969. $expected = "Content-Disposition: attachment; filename=\"file.txt\"\r\n" .
  970. "Content-Type: text/plain\r\n" .
  971. "Content-Transfer-Encoding: base64\r\n";
  972. $this->assertStringContainsString($expected, $result['message']);
  973. $this->mailer->setAttachments(['file2.txt' => ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain', 'contentId' => 'a1b1c1']]);
  974. $this->mailer->setBodyText('body');
  975. $result = $this->mailer->send();
  976. $expected = "Content-Disposition: inline; filename=\"file2.txt\"\r\n" .
  977. "Content-Type: text/plain\r\n" .
  978. "Content-Transfer-Encoding: base64\r\n" .
  979. "Content-ID: <a1b1c1>\r\n";
  980. $this->assertStringContainsString($expected, $result['message']);
  981. }
  982. public function testSendWithUnsetTemplateDefaultsToActionName(): void
  983. {
  984. $mailer = $this->getMockBuilder(Mailer::class)
  985. ->onlyMethods(['deliver', 'restore'])
  986. ->addMethods(['test'])
  987. ->getMock();
  988. $mailer->expects($this->once())
  989. ->method('test')
  990. ->with('foo', 'bar');
  991. $mailer->expects($this->any())
  992. ->method('deliver')
  993. ->will($this->returnValue([]));
  994. $mailer->send('test', ['foo', 'bar']);
  995. $this->assertSame('test', $mailer->viewBuilder()->getTemplate());
  996. }
  997. /**
  998. * testGetBody method
  999. */
  1000. public function testGetBody(): void
  1001. {
  1002. $this->mailer->setTransport('debug');
  1003. $this->mailer->setFrom('cake@cakephp.org');
  1004. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  1005. $this->mailer->setSubject('My title');
  1006. $this->mailer->setProfile(['empty']);
  1007. $this->mailer->viewBuilder()->setTemplate('default', 'default');
  1008. $this->mailer->setEmailFormat('both');
  1009. $this->mailer->send();
  1010. $expected = '<p>This email was sent using the <a href="https://cakephp.org">CakePHP Framework</a></p>';
  1011. $this->assertStringContainsString($expected, $this->mailer->getBodyHtml());
  1012. $expected = 'This email was sent using the CakePHP Framework, https://cakephp.org.';
  1013. $this->assertStringContainsString($expected, $this->mailer->getBodyText());
  1014. $message = $this->mailer->getBody();
  1015. $this->assertContains('Content-Type: text/plain; charset=UTF-8', $message);
  1016. $this->assertContains('Content-Type: text/html; charset=UTF-8', $message);
  1017. // UTF-8 is 8bit
  1018. $this->assertTrue($this->_checkContentTransferEncoding($message, '8bit'));
  1019. $this->mailer->setCharset('ISO-2022-JP');
  1020. $this->mailer->send();
  1021. $message = $this->mailer->getBody();
  1022. $this->assertContains('Content-Type: text/plain; charset=ISO-2022-JP', $message);
  1023. $this->assertContains('Content-Type: text/html; charset=ISO-2022-JP', $message);
  1024. // ISO-2022-JP is 7bit
  1025. $this->assertTrue($this->_checkContentTransferEncoding($message, '7bit'));
  1026. }
  1027. /**
  1028. * testZeroOnlyLinesNotBeingEmptied()
  1029. */
  1030. public function testZeroOnlyLinesNotBeingEmptied(): void
  1031. {
  1032. $message = "Lorem\r\n0\r\n0\r\nipsum";
  1033. $this->mailer->reset();
  1034. $this->mailer->setTransport('debug');
  1035. $this->mailer->setFrom('cake@cakephp.org');
  1036. $this->mailer->setTo('cake@cakephp.org');
  1037. $this->mailer->setSubject('Wordwrap Test');
  1038. $result = $this->mailer->deliver($message);
  1039. $expected = "{$message}\r\n\r\n";
  1040. $this->assertSame($expected, $result['message']);
  1041. }
  1042. /**
  1043. * testReset method
  1044. */
  1045. public function testReset(): void
  1046. {
  1047. $this->mailer->setTo('cake@cakephp.org');
  1048. $this->mailer->viewBuilder()->setTheme('TestTheme');
  1049. $this->mailer->setEmailPattern('/.+@.+\..+/i');
  1050. $this->assertSame(['cake@cakephp.org' => 'cake@cakephp.org'], $this->mailer->getTo());
  1051. $this->mailer->reset();
  1052. $this->assertSame([], $this->mailer->getTo());
  1053. $this->assertNull($this->mailer->viewBuilder()->getTheme());
  1054. $this->assertSame(Message::EMAIL_PATTERN, $this->mailer->getEmailPattern());
  1055. }
  1056. /**
  1057. * Test that mailers call reset() when send fails
  1058. */
  1059. public function testSendFailsEmailIsReset(): void
  1060. {
  1061. $mailer = $this->getMockBuilder(Mailer::class)
  1062. ->onlyMethods(['restore', 'deliver'])
  1063. ->addMethods(['welcome'])
  1064. ->getMock();
  1065. $mailer->expects($this->once())
  1066. ->method('deliver')
  1067. ->will($this->throwException(new RuntimeException('kaboom')));
  1068. // Mailer should be reset even if sending fails.
  1069. $mailer->expects($this->once())
  1070. ->method('restore');
  1071. try {
  1072. $mailer->send('welcome', ['foo', 'bar']);
  1073. $this->fail('Exception should bubble up.');
  1074. } catch (RuntimeException $e) {
  1075. $this->assertTrue(true, 'Exception was raised');
  1076. }
  1077. }
  1078. /**
  1079. * testSendWithLog method
  1080. */
  1081. public function testSendWithLog(): void
  1082. {
  1083. Log::setConfig('email', [
  1084. 'className' => 'Array',
  1085. ]);
  1086. $this->mailer->setTransport('debug');
  1087. $this->mailer->setTo('me@cakephp.org');
  1088. $this->mailer->setFrom('cake@cakephp.org');
  1089. $this->mailer->setSubject('My title');
  1090. $this->mailer->setProfile(['log' => 'debug']);
  1091. $text = 'Logging This';
  1092. $result = $this->mailer->deliver($text);
  1093. $this->assertNotEmpty($result);
  1094. $messages = Log::engine('email')->read();
  1095. $this->assertCount(1, $messages);
  1096. $this->assertStringContainsString($text, $messages[0]);
  1097. $this->assertStringContainsString('cake@cakephp.org', $messages[0]);
  1098. $this->assertStringContainsString('me@cakephp.org', $messages[0]);
  1099. }
  1100. /**
  1101. * testSendWithLogAndScope method
  1102. */
  1103. public function testSendWithLogAndScope(): void
  1104. {
  1105. Log::setConfig('email', [
  1106. 'className' => 'Array',
  1107. 'scopes' => ['email'],
  1108. ]);
  1109. $this->mailer->setTransport('debug');
  1110. $this->mailer->setTo('me@cakephp.org');
  1111. $this->mailer->setFrom('cake@cakephp.org');
  1112. $this->mailer->setSubject('My title');
  1113. $this->mailer->setProfile(['log' => ['scope' => 'email']]);
  1114. $text = 'Logging This';
  1115. $this->mailer->deliver($text);
  1116. $messages = Log::engine('email')->read();
  1117. $this->assertCount(1, $messages);
  1118. $this->assertStringContainsString($text, $messages[0]);
  1119. $this->assertStringContainsString('cake@cakephp.org', $messages[0]);
  1120. $this->assertStringContainsString('me@cakephp.org', $messages[0]);
  1121. }
  1122. /**
  1123. * test that initial email instance config is restored after email is sent.
  1124. */
  1125. public function testDefaultProfileRestoration(): void
  1126. {
  1127. $mailer = $this->getMockBuilder(Mailer::class)
  1128. ->onlyMethods(['deliver'])
  1129. ->addMethods(['test'])
  1130. ->setConstructorArgs([['template' => 'cakephp']])
  1131. ->getMock();
  1132. $mailer->expects($this->once())
  1133. ->method('test')
  1134. ->with('foo', 'bar');
  1135. $mailer->expects($this->once())
  1136. ->method('deliver')
  1137. ->will($this->returnValue([]));
  1138. $mailer->send('test', ['foo', 'bar']);
  1139. $this->assertSame('cakephp', $mailer->viewBuilder()->getTemplate());
  1140. }
  1141. public function testMissingActionThrowsException(): void
  1142. {
  1143. $this->expectException(MissingActionException::class);
  1144. $this->expectExceptionMessage('Mail Cake\Mailer\Mailer::test() could not be found, or is not accessible.');
  1145. (new Mailer())->send('test');
  1146. }
  1147. public function testDeliver(): void
  1148. {
  1149. $this->mailer->setTransport('debug');
  1150. $this->mailer->setFrom('cake@cakephp.org');
  1151. $this->mailer->setTo(['you@cakephp.org' => 'You']);
  1152. $this->mailer->setSubject('My title');
  1153. $result = $this->mailer->deliver("Here is my body, with multi lines.\nThis is the second line.\r\n\r\nAnd the last.");
  1154. $expected = ['headers', 'message'];
  1155. $this->assertEquals($expected, array_keys($result));
  1156. $expected = "Here is my body, with multi lines.\r\nThis is the second line.\r\n\r\nAnd the last.\r\n\r\n";
  1157. $this->assertSame($expected, $result['message']);
  1158. $this->assertStringContainsString('Date: ', $result['headers']);
  1159. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  1160. $this->assertStringContainsString('To: ', $result['headers']);
  1161. $result = $this->mailer->deliver('Other body');
  1162. $expected = "Other body\r\n\r\n";
  1163. $this->assertSame($expected, $result['message']);
  1164. $this->assertStringContainsString('Message-ID: ', $result['headers']);
  1165. $this->assertStringContainsString('To: ', $result['headers']);
  1166. }
  1167. protected function assertLineLengths(string $message): void
  1168. {
  1169. $lines = explode("\r\n", $message);
  1170. foreach ($lines as $line) {
  1171. $this->assertTrue(
  1172. strlen($line) <= Message::LINE_LENGTH_MUST,
  1173. 'Line length exceeds the max. limit of Message::LINE_LENGTH_MUST'
  1174. );
  1175. }
  1176. }
  1177. /**
  1178. * @param array|string $message
  1179. */
  1180. protected function _checkContentTransferEncoding($message, string $charset): bool
  1181. {
  1182. $boundary = '--' . $this->mailer->getBoundary();
  1183. $result['text'] = false;
  1184. $result['html'] = false;
  1185. $length = count($message);
  1186. for ($i = 0; $i < $length; ++$i) {
  1187. if ($message[$i] === $boundary) {
  1188. $flag = false;
  1189. $type = '';
  1190. while (!preg_match('/^$/', $message[$i])) {
  1191. if (preg_match('/^Content-Type: text\/plain/', $message[$i])) {
  1192. $type = 'text';
  1193. }
  1194. if (preg_match('/^Content-Type: text\/html/', $message[$i])) {
  1195. $type = 'html';
  1196. }
  1197. if ($message[$i] === 'Content-Transfer-Encoding: ' . $charset) {
  1198. $flag = true;
  1199. }
  1200. ++$i;
  1201. }
  1202. $result[$type] = $flag;
  1203. }
  1204. }
  1205. return $result['text'] && $result['html'];
  1206. }
  1207. }