diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..cd8eb86
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+; This file is for unifying the coding style for different editors and IDEs.
+; More information at http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_size = 4
+indent_style = space
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bb6265e
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# Path-based git attributes
+# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
+
+# Ignore all test and documentation with "export-ignore".
+/.gitattributes export-ignore
+/.gitignore export-ignore
+/.travis.yml export-ignore
+/phpunit.xml.dist export-ignore
+/.scrutinizer.yml export-ignore
+/tests export-ignore
+/.editorconfig export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..896e906
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+build
+composer.lock
+docs
+vendor
+coverage
+.phpunit.result.cache
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..df16b68
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,19 @@
+filter:
+ excluded_paths: [tests/*]
+
+checks:
+ php:
+ remove_extra_empty_lines: true
+ remove_php_closing_tag: true
+ remove_trailing_whitespace: true
+ fix_use_statements:
+ remove_unused: true
+ preserve_multiple: false
+ preserve_blanklines: true
+ order_alphabetically: true
+ fix_php_opening_tag: true
+ fix_linefeed: true
+ fix_line_ending: true
+ fix_identation_4spaces: true
+ fix_doc_comments: true
+
diff --git a/.styleci.yml b/.styleci.yml
new file mode 100644
index 0000000..f4d3cbc
--- /dev/null
+++ b/.styleci.yml
@@ -0,0 +1,4 @@
+preset: laravel
+
+disabled:
+ - single_class_element_per_statement
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..24c2d6c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: php
+
+php:
+ - 7.1
+ - 7.2
+ - 7.3
+
+env:
+ matrix:
+ - COMPOSER_FLAGS="--prefer-lowest"
+ - COMPOSER_FLAGS=""
+
+before_script:
+ - travis_retry composer self-update
+ - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source
+
+script:
+ - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
+
+after_script:
+ - php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..5a7bba5
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+
+All notable changes to `laravel-test-mail` will be documented in this file
+
+## 1.0.0 - 201X-XX-XX
+
+- initial release
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b4ae1c4
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# Contributing
+
+Contributions are **welcome** and will be fully **credited**.
+
+Please read and understand the contribution guide before creating an issue or pull request.
+
+## Etiquette
+
+This project is open source, and as such, the maintainers give their free time to build and maintain the source code
+held within. They make the code freely available in the hope that it will be of use to other developers. It would be
+extremely unfair for them to suffer abuse or anger for their hard work.
+
+Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
+world that developers are civilized and selfless people.
+
+It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
+quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
+
+## Viability
+
+When requesting or submitting new features, first consider whether it might be useful to others. Open
+source projects are used by many developers, who may have entirely different needs to your own. Think about
+whether or not your feature is likely to be used by other users of the project.
+
+## Procedure
+
+Before filing an issue:
+
+- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
+- Check to make sure your feature suggestion isn't already present within the project.
+- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
+- Check the pull requests tab to ensure that the feature isn't already in progress.
+
+Before submitting a pull request:
+
+- Check the codebase to ensure that your feature doesn't already exist.
+- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
+
+## Requirements
+
+If the project maintainer has any additional requirements, you will find them listed here.
+
+- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
+
+- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
+
+- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
+
+- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
+
+- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
+
+- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
+
+**Happy coding**!
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..7d7e69b
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Sean White
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d7e68a5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,91 @@
+# Laravel Test Mail
+
+[![Latest Version on Packagist](https://img.shields.io/packagist/v/resohead/laravel-test-mail.svg?style=flat-square)](https://packagist.org/packages/resohead/laravel-test-mail)
+[![Build Status](https://img.shields.io/travis/resohead/laravel-test-mail/master.svg?style=flat-square)](https://travis-ci.org/resohead/laravel-test-mail)
+[![Quality Score](https://img.shields.io/scrutinizer/g/resohead/laravel-test-mail.svg?style=flat-square)](https://scrutinizer-ci.com/g/resohead/laravel-test-mail)
+[![Total Downloads](https://img.shields.io/packagist/dt/resohead/laravel-test-mail.svg?style=flat-square)](https://packagist.org/packages/resohead/laravel-test-mail)
+
+A simple package to send test emails from artisan commands.
+
+## Installation
+
+You can install the package via composer:
+
+```bash
+composer require resohead/laravel-test-mail
+```
+
+The package will automatically register itself.
+
+## Usage
+
+To send a test email run the following artisan command:
+
+``` php
+php artisan mail:test
+```
+
+By default this will use:
+- the 'from' address defined in your mail config,
+- your default mail driver,
+- synchronous processing
+
+Alternatively you have three other options in the command signature:
+- set the email address,
+- change the mail driver,
+- enable for queuing
+- change the queue connection
+
+Changing the mail driver and running through a queue might require the queue worker to be reset.
+
+``` bash
+// send using the default mail driver and default queue/stack to the specified email
+php artisan mail:test name@example.com --queue
+
+// queue using the 'log' mail driver
+php artisan mail:test --driver=log
+
+// queue using the 'emails' queue on the default connection
+php artisan mail:test --stack=emails
+
+// queue using the sqs queue connection, default queue and default mail driver
+php artisan mail:test --connection=sqs
+
+// send a test mail using the SMTP driver via the emails queue on the redis connection
+php artisan mail:test name@example.com --driver=smtp --connection=redis --stack=emails
+
+```
+> You might need to start the your queue if using the connection option, for example
+```
+php artisan queue:work sqs
+```
+## Alternatives
+
+This is a simple package designed to quickly trigger an email to check your configuration.
+
+If you want to check what an email looks like in the browser use the Laravel documentation to [render mailables](https://laravel.com/docs/mail#rendering-mailables) (available since Laravel 5.5).
+
+If you need a package to send a mailable using fake data try using [Spatie's laravel-mailable-test package](https://github.com/spatie/laravel-mailable-test).
+
+### Testing
+
+``` bash
+composer test
+```
+
+### Changelog
+
+Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
+
+## Contributing
+
+Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
+
+## Credits
+
+- [Sean White](https://github.com/resohead)
+- [All Contributors](../../contributors)
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..ce56ab2
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,54 @@
+{
+ "name": "resohead/laravel-test-mail",
+ "description": "Quickly send test emails using commands in Laravel applications",
+ "keywords": [
+ "resohead",
+ "laravel-test-mail"
+ ],
+ "homepage": "https://github.com/resohead/laravel-test-mail",
+ "license": "MIT",
+ "type": "library",
+ "authors": [
+ {
+ "name": "Sean White",
+ "email": "s.white9904@gmail.com",
+ "role": "Developer"
+ }
+ ],
+ "require": {
+ "php": "^7.1",
+ "illuminate/support": "^6.0|^7.0"
+ },
+ "require-dev": {
+ "orchestra/testbench": "^4.0",
+ "phpunit/phpunit": "^8.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Resohead\\LaravelTestMail\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Resohead\\LaravelTestMail\\Tests\\": "tests"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpunit",
+ "test-coverage": "vendor/bin/phpunit --coverage-html coverage"
+
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Resohead\\LaravelTestMail\\LaravelTestMailServiceProvider"
+ ],
+ "aliases": {
+ "LaravelTestMail": "Resohead\\LaravelTestMail\\LaravelTestMailFacade"
+ }
+ }
+ }
+}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..22fe879
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,29 @@
+
+
+
+
+ tests
+
+
+
+
+ src/
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/views/emails/test.blade.php b/resources/views/emails/test.blade.php
new file mode 100644
index 0000000..fa7fb99
--- /dev/null
+++ b/resources/views/emails/test.blade.php
@@ -0,0 +1,11 @@
+@component('mail::message')
+
+This is a test email.
+
+@component('mail::button', ['url' => config('app.url')])
+Open Website
+@endcomponent
+
+Thanks,
+{{ config('app.name') }}
+@endcomponent
diff --git a/src/LaravelTestMailServiceProvider.php b/src/LaravelTestMailServiceProvider.php
new file mode 100644
index 0000000..579cf0e
--- /dev/null
+++ b/src/LaravelTestMailServiceProvider.php
@@ -0,0 +1,27 @@
+loadViewsFrom(__DIR__.'/../resources/views', 'laravel-test-mail');
+ }
+
+ /**
+ * Register the application services.
+ */
+ public function register()
+ {
+ $this->commands([
+ TestMailCommand::class,
+ ]);
+ }
+}
diff --git a/src/TestMail.php b/src/TestMail.php
new file mode 100644
index 0000000..5be7d8e
--- /dev/null
+++ b/src/TestMail.php
@@ -0,0 +1,37 @@
+recipient = $recipient;
+ }
+
+ /**
+ * Build the message.
+ *
+ * @return $this
+ */
+ public function build()
+ {
+ return $this->subject('Test email from '. config('app.name'))
+ ->to($this->recipient)
+ ->markdown('laravel-test-mail::emails.test');
+ }
+}
diff --git a/src/TestMailCommand.php b/src/TestMailCommand.php
new file mode 100644
index 0000000..8e50907
--- /dev/null
+++ b/src/TestMailCommand.php
@@ -0,0 +1,86 @@
+validator = $validator;
+ $this->config = $config;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $recipient = $this->argument('recipient') ?? config('mail.from.address');
+ $driver = $this->option('driver') ?? $this->config->get('mail.driver');
+ $stack = $this->option('stack') ?: 'default';
+ $connection = $this->option('connection') ?? $this->config->get('queue.default');
+
+ $validation = $this->validator::make([
+ 'email' => $recipient,
+ 'driver' => $driver
+ ], $this->rules()
+ );
+
+ if ($validation->fails()) {
+ collect($validation->errors()->all())->each(function($error){
+ $this->error($error);
+ });
+ return 1;
+ }
+
+ $this->config->set('mail.driver', $driver);
+
+ $mailable = new TestMail($recipient);
+
+ (bool) $this->option('queue') || ($this->option('stack') || $this->option('connection'))
+ ? Mail::queue($mailable->onConnection($connection)->onQueue($stack))
+ : Mail::send($mailable);
+
+ $this->comment("A test email (${driver}) has been sent to ${recipient}");
+ }
+
+ protected function rules()
+ {
+ return [
+ 'email' => 'email',
+ 'driver' => 'required'
+ ];
+ }
+}
diff --git a/tests/TestMailTest.php b/tests/TestMailTest.php
new file mode 100644
index 0000000..e438375
--- /dev/null
+++ b/tests/TestMailTest.php
@@ -0,0 +1,276 @@
+defaultEmailAddress = 'recipient@example.com';
+ $app['config']->set('mail.from.address', $this->defaultEmailAddress);
+ }
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->command = 'mail:test';
+ $this->emailAddress = 'recipient@example.com';
+
+ Mail::fake();
+ }
+
+ /** @test */
+ public function it_can_be_sent_without_any_arguments()
+ {
+ $exitCode = Artisan::call($this->command);
+ $this->assertStringContainsString($this->defaultEmailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertSent(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_can_be_sent_synchronously_to_an_email()
+ {
+ $exitCode = Artisan::call($this->command,[
+ 'recipient' => $this->emailAddress
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertSent(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+
+ /** @test */
+ public function it_is_queued_with_the_queue_flag_only()
+ {
+ $exitCode = Artisan::call($this->command,[
+ 'recipient' => $this->emailAddress,
+ '--queue' => null
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertQueued(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_is_queued_with_stack_only()
+ {
+ $exitCode = Artisan::call($this->command,[
+ '--stack' => 'emails'
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertQueued(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertEquals('emails', $mail->queue);
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_is_queued_with_connection_only()
+ {
+ $exitCode = Artisan::call($this->command,[
+ '--connection' => 'sync'
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertQueued(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertEquals('sync', $mail->connection);
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_is_queued_with_connection_and_stack()
+ {
+ $exitCode = Artisan::call($this->command,[
+ '--stack' => 'emails',
+ '--connection' => 'sync'
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertQueued(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertEquals('emails', $mail->queue);
+ $this->assertEquals('sync', $mail->connection);
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_is_queued_with__queue_flag_connection_and_stack()
+ {
+ $exitCode = Artisan::call($this->command,[
+ '--queue' => null,
+ '--stack' => 'emails',
+ '--connection' => 'sync'
+ ]);
+
+ $this->assertStringContainsString($this->emailAddress, Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ Mail::assertQueued(TestMail::class, function (TestMail $mail) {
+ $mail->build();
+ $this->assertEquals('emails', $mail->queue);
+ $this->assertEquals('sync', $mail->connection);
+ $this->assertCount(1, $mail->to);
+ $this->assertEquals($this->emailAddress, $mail->to[0]['address']);
+ $this->assertCount(0, $mail->cc);
+ $this->assertCount(0, $mail->bcc);
+
+ return true;
+ });
+ }
+
+ /** @test */
+ public function it_will_throw_an_exception_when_passing_an_invalid_mail_address()
+ {
+ $exitCode = Artisan::call($this->command, [
+ 'recipient' => 'notanemailaddress',
+ ]);
+
+ Mail::assertNotSent(TestMail::class);
+
+ $this->assertEquals(1, $exitCode);
+ }
+
+ /** @test */
+ public function it_will_throw_an_exception_if_it_cannot_find_an_email_address()
+ {
+ config(['mail.from.address' => null]);
+ //$this->expectException(Exception::class);
+
+ $exitCode = Artisan::call($this->command, [
+ 'recipient' => 'notanemailaddress',
+ ]);
+
+ Mail::assertNotQueued(TestMail::class);
+
+ $this->assertEquals(1, $exitCode);
+ }
+
+ /** @test */
+ public function it_can_be_sent_using_different_mail_drivers()
+ {
+ config(['mail.driver' => 'smtp']);
+
+ $this->assertEquals('smtp', config('mail.driver'));
+ $exitCode = Artisan::call($this->command);
+
+ $this->assertStringContainsString('smtp', Artisan::output());
+ $this->assertEquals(0, $exitCode);
+
+ $exitCode = Artisan::call($this->command, ['--driver' => 'log']);
+ $this->assertStringContainsString('log', Artisan::output());
+ $this->assertEquals(0, $exitCode);
+ }
+
+ /** @test */
+ public function the_driver_option_is_optional_but_cannot_be_blank()
+ {
+ config(['mail.driver' => 'smtp']);
+
+ $this->assertEquals('smtp', config('mail.driver'));
+ $exitCode = Artisan::call($this->command, ['--driver'=> '']);
+
+ $this->assertStringContainsString('The driver field is required.', Artisan::output());
+
+ Mail::assertNotSent(TestMail::class);
+
+ $this->assertEquals(1, $exitCode);
+ }
+
+ /** @test */
+ public function input_validation_shows_multiple_errors()
+ {
+ config(['mail.driver' => 'smtp']);
+
+ $this->assertEquals('smtp', config('mail.driver'));
+
+ $exitCode = Artisan::call($this->command, [
+ 'recipient' => 'notanemailaddress',
+ '--driver'=> '',
+ ]
+ );
+
+ $output = Artisan::output();
+
+ $this->assertStringContainsString('The email must be a valid email address.', $output);
+ $this->assertStringContainsString('The driver field is required.', $output);
+
+ Mail::assertNotSent(TestMail::class);
+
+ $this->assertEquals(1, $exitCode);
+ }
+}