diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d48d74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# phpstorm project files +.idea + +# netbeans project files +nbproject + +# zend studio for eclipse project files +.buildpath +.project +.settings + +# windows thumbnail cache +Thumbs.db + +# composer vendor dir +/vendor + +# composer itself is not needed +composer.phar + +# Mac DS_Store Files +.DS_Store + +# phpunit itself is not needed +phpunit.phar +# local phpunit config +/phpunit.xml + +# local tests configuration +/tests/data/config.local.php \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6a6d5e2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,53 @@ +language: php + +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + - hhvm-nightly + +# run build against hhvm but allow them to fail +# http://docs.travis-ci.com/user/build-configuration/#Rows-That-are-Allowed-To-Fail +matrix: + fast_finish: true + allow_failures: + - php: hhvm-nightly + - php: 7.0 + +services: + - mongodb + +# faster builds on new travis setup not using sudo +sudo: false + +# cache vendor dirs +cache: + directories: + - vendor + - $HOME/.composer/cache + +install: + - travis_retry composer self-update && composer --version + - travis_retry composer global require "fxp/composer-asset-plugin:1.0.0" + - export PATH="$HOME/.composer/vendor/bin:$PATH" + - travis_retry composer install --prefer-dist --no-interaction + +before_script: + - | + if [ $TRAVIS_PHP_VERSION = '5.6' ]; then + PHPUNIT_FLAGS="--coverage-clover=coverage.clover" + fi + + +script: + - vendor/bin/phpunit --verbose $PHPUNIT_FLAGS + +after_script: + - | + if [ $TRAVIS_PHP_VERSION = '5.6' ]; then + cd ../../.. + travis_retry wget https://scrutinizer-ci.com/ocular.phar + php ocular.phar code-coverage:upload --format=php-clover coverage.clover + fi diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d93d427 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + ./tests + + + diff --git a/tests/MailerTest.php b/tests/MailerTest.php new file mode 100644 index 0000000..13e9509 --- /dev/null +++ b/tests/MailerTest.php @@ -0,0 +1,124 @@ +mockApplication([ + 'components' => [ + 'email' => $this->createTestEmailComponent() + ] + ]); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new Mailer(); + + return $component; + } + + // Tests : + + public function testSetupTransport() + { + $mailer = new Mailer(); + + $transport = \Swift_MailTransport::newInstance(); + $mailer->setTransport($transport); + $this->assertEquals($transport, $mailer->getTransport(), 'Unable to setup transport!'); + } + + /** + * @depends testSetupTransport + */ + public function testConfigureTransport() + { + $mailer = new Mailer(); + + $transportConfig = [ + 'class' => 'Swift_SmtpTransport', + 'host' => 'localhost', + 'username' => 'username', + 'password' => 'password', + ]; + $mailer->setTransport($transportConfig); + $transport = $mailer->getTransport(); + $this->assertTrue(is_object($transport), 'Unable to setup transport via config!'); + $this->assertEquals($transportConfig['class'], get_class($transport), 'Invalid transport class!'); + $this->assertEquals($transportConfig['host'], $transport->getHost(), 'Invalid transport host!'); + } + + /** + * @depends testConfigureTransport + */ + public function testConfigureTransportConstruct() + { + $mailer = new Mailer(); + + $class = 'Swift_SmtpTransport'; + $host = 'some.test.host'; + $port = 999; + $transportConfig = [ + 'class' => $class, + 'constructArgs' => [ + $host, + $port, + ], + ]; + $mailer->setTransport($transportConfig); + $transport = $mailer->getTransport(); + $this->assertTrue(is_object($transport), 'Unable to setup transport via config!'); + $this->assertEquals($class, get_class($transport), 'Invalid transport class!'); + $this->assertEquals($host, $transport->getHost(), 'Invalid transport host!'); + $this->assertEquals($port, $transport->getPort(), 'Invalid transport host!'); + } + + /** + * @depends testConfigureTransportConstruct + */ + public function testConfigureTransportWithPlugins() + { + $mailer = new Mailer(); + + $pluginClass = 'Swift_Plugins_ThrottlerPlugin'; + $rate = 10; + + $transportConfig = [ + 'class' => 'Swift_SmtpTransport', + 'plugins' => [ + [ + 'class' => $pluginClass, + 'constructArgs' => [ + $rate, + ], + ], + ], + ]; + $mailer->setTransport($transportConfig); + $transport = $mailer->getTransport(); + $this->assertTrue(is_object($transport), 'Unable to setup transport via config!'); + $this->assertContains(':' . $pluginClass . ':', print_r($transport, true), 'Plugin not added'); + } + + public function testGetSwiftMailer() + { + $mailer = new Mailer(); + $this->assertTrue(is_object($mailer->getSwiftMailer()), 'Unable to get Swift mailer instance!'); + } +} diff --git a/tests/MessageTest.php b/tests/MessageTest.php new file mode 100644 index 0000000..a9dc83f --- /dev/null +++ b/tests/MessageTest.php @@ -0,0 +1,365 @@ +mockApplication([ + 'components' => [ + 'mailer' => $this->createTestEmailComponent() + ] + ]); + $filePath = $this->getTestFilePath(); + if (!file_exists($filePath)) { + FileHelper::createDirectory($filePath); + } + } + + public function tearDown() + { + $filePath = $this->getTestFilePath(); + if (file_exists($filePath)) { + FileHelper::removeDirectory($filePath); + } + } + + /** + * @return string test file path. + */ + protected function getTestFilePath() + { + return Yii::getAlias('@yiiunit/extensions/swiftmailer/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid(); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new Mailer([ + 'useFileTransport' => true, + ]); + + return $component; + } + + /** + * @return Message test message instance. + */ + protected function createTestMessage() + { + return Yii::$app->get('mailer')->compose(); + } + + /** + * Creates image file with given text. + * @param string $fileName file name. + * @param string $text text to be applied on image. + * @return string image file full name. + */ + protected function createImageFile($fileName = 'test.jpg', $text = 'Test Image') + { + if (!function_exists('imagecreatetruecolor')) { + $this->markTestSkipped('GD lib required.'); + } + $fileFullName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $fileName; + $image = imagecreatetruecolor(120, 20); + $textColor = imagecolorallocate($image, 233, 14, 91); + imagestring($image, 1, 5, 5, $text, $textColor); + imagejpeg($image, $fileFullName); + imagedestroy($image); + + return $fileFullName; + } + + /** + * Finds the attachment object in the message. + * @param Message $message message instance + * @return null|\Swift_Mime_Attachment attachment instance. + */ + protected function getAttachment(Message $message) + { + $messageParts = $message->getSwiftMessage()->getChildren(); + $attachment = null; + foreach ($messageParts as $part) { + if ($part instanceof \Swift_Mime_Attachment) { + $attachment = $part; + break; + } + } + + return $attachment; + } + + // Tests : + + public function testGetSwiftMessage() + { + $message = new Message(); + $this->assertTrue(is_object($message->getSwiftMessage()), 'Unable to get Swift message!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSetGet() + { + $message = new Message(); + + $charset = 'utf-16'; + $message->setCharset($charset); + $this->assertEquals($charset, $message->getCharset(), 'Unable to set charset!'); + + $subject = 'Test Subject'; + $message->setSubject($subject); + $this->assertEquals($subject, $message->getSubject(), 'Unable to set subject!'); + + $from = 'from@somedomain.com'; + $message->setFrom($from); + $this->assertContains($from, array_keys($message->getFrom()), 'Unable to set from!'); + + $replyTo = 'reply-to@somedomain.com'; + $message->setReplyTo($replyTo); + $this->assertContains($replyTo, array_keys($message->getReplyTo()), 'Unable to set replyTo!'); + + $to = 'someuser@somedomain.com'; + $message->setTo($to); + $this->assertContains($to, array_keys($message->getTo()), 'Unable to set to!'); + + $cc = 'ccuser@somedomain.com'; + $message->setCc($cc); + $this->assertContains($cc, array_keys($message->getCc()), 'Unable to set cc!'); + + $bcc = 'bccuser@somedomain.com'; + $message->setBcc($bcc); + $this->assertContains($bcc, array_keys($message->getBcc()), 'Unable to set bcc!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSetupHeaders() + { + $charset = 'utf-16'; + $subject = 'Test Subject'; + $from = 'from@somedomain.com'; + $replyTo = 'reply-to@somedomain.com'; + $to = 'someuser@somedomain.com'; + $cc = 'ccuser@somedomain.com'; + $bcc = 'bccuser@somedomain.com'; + + $messageString = $this->createTestMessage() + ->setCharset($charset) + ->setSubject($subject) + ->setFrom($from) + ->setReplyTo($replyTo) + ->setTo($to) + ->setCc($cc) + ->setBcc($bcc) + ->toString(); + + $this->assertContains('charset=' . $charset, $messageString, 'Incorrect charset!'); + $this->assertContains('Subject: ' . $subject, $messageString, 'Incorrect "Subject" header!'); + $this->assertContains('From: ' . $from, $messageString, 'Incorrect "From" header!'); + $this->assertContains('Reply-To: ' . $replyTo, $messageString, 'Incorrect "Reply-To" header!'); + $this->assertContains('To: ' . $to, $messageString, 'Incorrect "To" header!'); + $this->assertContains('Cc: ' . $cc, $messageString, 'Incorrect "Cc" header!'); + $this->assertContains('Bcc: ' . $bcc, $messageString, 'Incorrect "Bcc" header!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSend() + { + $message = $this->createTestMessage(); + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Test'); + $message->setTextBody('Yii Swift Test body'); + $this->assertTrue($message->send()); + } + + /** + * @depends testSend + */ + public function testAttachFile() + { + $message = $this->createTestMessage(); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Attach File Test'); + $message->setTextBody('Yii Swift Attach File Test body'); + $fileName = __FILE__; + $message->attach($fileName); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testAttachContent() + { + $message = $this->createTestMessage(); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Create Attachment Test'); + $message->setTextBody('Yii Swift Create Attachment Test body'); + $fileName = 'test.txt'; + $fileContent = 'Test attachment content'; + $message->attachContent($fileContent, ['fileName' => $fileName]); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testEmbedFile() + { + $fileName = $this->createImageFile('embed_file.jpg', 'Embed Image File'); + + $message = $this->createTestMessage(); + + $cid = $message->embed($fileName); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Embed File Test'); + $message->setHtmlBody('Embed image: pic'); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testEmbedContent() + { + $fileFullName = $this->createImageFile('embed_file.jpg', 'Embed Image File'); + $message = $this->createTestMessage(); + + $fileName = basename($fileFullName); + $contentType = 'image/jpeg'; + $fileContent = file_get_contents($fileFullName); + + $cid = $message->embedContent($fileContent, ['fileName' => $fileName, 'contentType' => $contentType]); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Embed File Test'); + $message->setHtmlBody('Embed image: pic'); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!'); + $this->assertEquals($contentType, $attachment->getContentType(), 'Invalid content type!'); + } + + /** + * @depends testSend + */ + public function testSendAlternativeBody() + { + $message = $this->createTestMessage(); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Alternative Body Test'); + $message->setHtmlBody('Yii Swift test HTML body'); + $message->setTextBody('Yii Swift test plain text body'); + + $this->assertTrue($message->send()); + + $messageParts = $message->getSwiftMessage()->getChildren(); + $textPresent = false; + $htmlPresent = false; + foreach ($messageParts as $part) { + if (!($part instanceof \Swift_Mime_Attachment)) { + /* @var $part \Swift_Mime_MimePart */ + if ($part->getContentType() == 'text/plain') { + $textPresent = true; + } + if ($part->getContentType() == 'text/html') { + $htmlPresent = true; + } + } + } + $this->assertTrue($textPresent, 'No text!'); + $this->assertTrue($htmlPresent, 'No HTML!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSerialize() + { + $message = $this->createTestMessage(); + + $message->setTo($this->testEmailReceiver); + $message->setFrom('someuser@somedomain.com'); + $message->setSubject('Yii Swift Alternative Body Test'); + $message->setTextBody('Yii Swift test plain text body'); + + $serializedMessage = serialize($message); + $this->assertNotEmpty($serializedMessage, 'Unable to serialize message!'); + + $unserializedMessaage = unserialize($serializedMessage); + $this->assertEquals($message, $unserializedMessaage, 'Unable to unserialize message!'); + } + + /** + * @depends testSendAlternativeBody + */ + public function testAlternativeBodyCharset() + { + $message = $this->createTestMessage(); + $charset = 'windows-1251'; + $message->setCharset($charset); + + $message->setTextBody('some text'); + $message->setHtmlBody('some html'); + $content = $message->toString(); + $this->assertEquals(2, substr_count($content, $charset), 'Wrong charset for alternative body.'); + + $message->setTextBody('some text override'); + $content = $message->toString(); + $this->assertEquals(2, substr_count($content, $charset), 'Wrong charset for alternative body override.'); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..3b9ba78 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,50 @@ +destroyApplication(); + } + + /** + * Populates Yii::$app with a new application + * The application will be destroyed on tearDown() automatically. + * @param array $config The application configuration, if needed + * @param string $appClass name of the application class to create + */ + protected function mockApplication($config = [], $appClass = '\yii\console\Application') + { + new $appClass(ArrayHelper::merge([ + 'id' => 'testapp', + 'basePath' => __DIR__, + 'vendorPath' => $this->getVendorPath(), + ], $config)); + } + + protected function getVendorPath() + { + $vendor = dirname(dirname(__DIR__)) . '/vendor'; + if (!is_dir($vendor)) { + $vendor = dirname(dirname(dirname(dirname(__DIR__)))); + } + return $vendor; + } + + /** + * Destroys application in Yii::$app by setting it to null. + */ + protected function destroyApplication() + { + \Yii::$app = null; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..caf694a --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,15 @@ +