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: ');
+
+ $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: ');
+
+ $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 @@
+