├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── php-cs-fixer.yml │ ├── run-tests.yml │ └── update-changelog.yml ├── .php-cs-fixer.cache ├── .php-cs-fixer.dist.php ├── .phpunit.cache └── test-results ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── mailable-test.php └── src ├── ArgumentValueProvider.php ├── Exceptions ├── CouldNotDetermineValue.php └── InvalidConfiguration.php ├── FakerArgumentValueProvider.php ├── MailableFactory.php ├── MailableTestServiceProvider.php └── SendTestMail.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://spatie.be/open-source/support-us 2 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: Check & fix styling 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v2 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Run PHP CS Fixer 16 | uses: docker://oskarstark/php-cs-fixer-ga 17 | with: 18 | args: --config=.php-cs-fixer.dist.php --allow-risky=yes 19 | 20 | - name: Commit changes 21 | uses: stefanzweifel/git-auto-commit-action@v4 22 | with: 23 | commit_message: Fix styling 24 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | test: 9 | runs-on: ${{ matrix.os }} 10 | 11 | strategy: 12 | fail-fast: true 13 | matrix: 14 | os: [ubuntu-latest] 15 | php: [7.4, 8.0, 8.1, '8.2'] 16 | laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] 17 | dependency-version: [prefer-stable] 18 | include: 19 | - laravel: 10.* 20 | testbench: ^8.0 21 | - laravel: 9.* 22 | testbench: ^7.0 23 | - laravel: 8.* 24 | testbench: ^6.23 25 | - laravel: 11.* 26 | testbench: ^9.0 27 | - laravel: 12.* 28 | testbench: ^10.0 29 | exclude: 30 | - laravel: 10.* 31 | php: 8.0 32 | - laravel: 10.* 33 | php: 7.4 34 | - laravel: 9.* 35 | php: 7.4 36 | - laravel: 11.* 37 | php: 7.4 38 | - laravel: 11.* 39 | php: 8.0 40 | - laravel: 11.* 41 | php: 8.1 42 | - laravel: 12.* 43 | php: 7.4 44 | - laravel: 12.* 45 | php: 8.0 46 | - laravel: 12.* 47 | php: 8.1 48 | 49 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 50 | 51 | steps: 52 | - name: Checkout code 53 | uses: actions/checkout@v2 54 | 55 | - name: Cache dependencies 56 | uses: actions/cache@v2 57 | with: 58 | path: ~/.composer/cache/files 59 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 60 | 61 | - name: Setup PHP 62 | uses: shivammathur/setup-php@v2 63 | with: 64 | php-version: ${{ matrix.php }} 65 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick 66 | coverage: none 67 | 68 | - name: Install dependencies 69 | run: | 70 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 71 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 72 | 73 | - name: Execute tests 74 | run: vendor/bin/phpunit 75 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | with: 15 | ref: main 16 | 17 | - name: Update Changelog 18 | uses: stefanzweifel/changelog-updater-action@v1 19 | with: 20 | latest-version: ${{ github.event.release.name }} 21 | release-notes: ${{ github.event.release.body }} 22 | 23 | - name: Commit updated CHANGELOG 24 | uses: stefanzweifel/git-auto-commit-action@v4 25 | with: 26 | branch: main 27 | commit_message: Update CHANGELOG 28 | file_pattern: CHANGELOG.md 29 | -------------------------------------------------------------------------------- /.php-cs-fixer.cache: -------------------------------------------------------------------------------- 1 | {"php":"8.3.17","version":"3.69.1","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces_position":true,"class_definition":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","do","else","elseif","final","for","foreach","function","if","interface","namespace","private","protected","public","static","switch","trait","try","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["method","property"]},"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"ordered_imports":{"sort_algorithm":"alpha"},"no_unused_imports":true,"not_operator_with_successor_space":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"unary_operator_spaces":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true},"hashes":{"src\/SendTestMail.php":"784d7dbcba45bf6cc0146ef3e36038de","src\/Exceptions\/CouldNotDetermineValue.php":"cf383d5541a9a8b32b26738c3981180e","src\/Exceptions\/InvalidConfiguration.php":"cc725b829c92ea4d6d6dcfdc3066e7c4","src\/ArgumentValueProvider.php":"a38b5b3cc04ebec0cdf687be1590cfe5","src\/MailableFactory.php":"dbdc04d11a4278dc2b985a248b89c929","src\/FakerArgumentValueProvider.php":"cdcbde13b2c817c328e61ac2afa1b0e3","src\/MailableTestServiceProvider.php":"cfca05aec3c193df5ee91a6de12d3add","tests\/MailableFactoryTest.php":"967823ccfd1f45170d4b2ae7019c4549","tests\/TestCase.php":"384b7a921c1cd0d132534353c2121cc4","tests\/ArgumentValueProviderTest.php":"1d13c9d251114bbca78c6398ed64354d","tests\/SendTestMailTest.php":"318cb75f3040b7fa52f61eebdee26adf","tests\/TestMailable.php":"7cc202370af41583eb004f300ba73d2a","tests\/TestModel.php":"e2b56a1f4ae4e46acb02be1c65ae41fc","tests\/ConfigTest.php":"677624a02adc970ea80687a8c489de03"}} -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | __DIR__ . '/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return (new PhpCsFixer\Config) 17 | ->setRules([ 18 | '@PSR2' => true, 19 | 'array_syntax' => ['syntax' => 'short'], 20 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 21 | 'no_unused_imports' => true, 22 | 'not_operator_with_successor_space' => true, 23 | 'trailing_comma_in_multiline' => true, 24 | 'phpdoc_scalar' => true, 25 | 'unary_operator_spaces' => true, 26 | 'binary_operator_spaces' => true, 27 | 'blank_line_before_statement' => [ 28 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 29 | ], 30 | 'phpdoc_single_line_var_spacing' => true, 31 | 'phpdoc_var_without_name' => true, 32 | 'method_argument_space' => [ 33 | 'on_multiline' => 'ensure_fully_multiline', 34 | 'keep_multiple_spaces_after_comma' => true, 35 | ] 36 | ]) 37 | ->setFinder($finder); 38 | -------------------------------------------------------------------------------- /.phpunit.cache/test-results: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":[],"times":{"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_can_generate_a_string":0.009,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_can_generate_an_integer":0,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_can_generate_a_boolean":0,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_can_resolve_a_type_bound_in_the_container":0,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_can_get_a_model_instance":0.004,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_will_throw_an_exception_if_an_instance_of_model_cannot_be_determined":0,"Spatie\\MailableTest\\Test\\ArgumentValueProviderTest::it_will_throw_an_exception_when_requesting_an_argument_with_an_unknown_type":0.002,"Spatie\\MailableTest\\Test\\ConfigTest::it_will_throw_an_exception_if_argument_value_provider_class_contains_an_invalid_class":0.022,"Spatie\\MailableTest\\Test\\MailableFactoryTest::it_will_create_a_mailable_where_all_recepients_have_been_removed_and_the_given_recepient_added":0,"Spatie\\MailableTest\\Test\\MailableFactoryTest::it_can_create_a_mailable_without_giving_default_values":0,"Spatie\\MailableTest\\Test\\MailableFactoryTest::it_can_create_a_mailable_with_the_given_default_values":0.001,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_can_send_a_mail_without_passing_values":0.002,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_can_send_a_mail_with_passing_values":0.001,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_will_throw_an_exception_when_passing_an_invalid_mail_address":0.001,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_will_throw_an_exception_when_passing_invalid_values":0.001,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_will_use_base_namespace_variable_if_class_is_not_found":0.001,"Spatie\\MailableTest\\Test\\SendTestMailTest::it_will_fail_if_mailable_class_is_not_found":0.001}} -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-mailable-test` will be documented in this file 4 | 5 | ## 2.4.1 - 2025-02-21 6 | 7 | ### What's Changed 8 | 9 | * Laravel 12.x Compatibility by @laravel-shift in https://github.com/spatie/laravel-mailable-test/pull/35 10 | 11 | **Full Changelog**: https://github.com/spatie/laravel-mailable-test/compare/2.4.0...2.4.1 12 | 13 | ## 2.4.0 - 2024-03-08 14 | 15 | ### What's Changed 16 | 17 | * docs: Fix broken test badge by @oliverearl in https://github.com/spatie/laravel-mailable-test/pull/33 18 | * Laravel 11.x Compatibility by @laravel-shift in https://github.com/spatie/laravel-mailable-test/pull/34 19 | 20 | ### New Contributors 21 | 22 | * @oliverearl made their first contribution in https://github.com/spatie/laravel-mailable-test/pull/33 23 | 24 | **Full Changelog**: https://github.com/spatie/laravel-mailable-test/compare/2.3.1...2.4.0 25 | 26 | ## 2.3.1 - 2023-01-25 27 | 28 | ### What's Changed 29 | 30 | - Laravel 10.x Compatibility by @laravel-shift in https://github.com/spatie/laravel-mailable-test/pull/31 31 | 32 | **Full Changelog**: https://github.com/spatie/laravel-mailable-test/compare/2.3.0...2.3.1 33 | 34 | ## 2.3.0 - 2022-01-13 35 | 36 | ## What's Changed 37 | 38 | - Update readme Faker repo URL by @krsriq in https://github.com/spatie/laravel-mailable-test/pull/29 39 | - Add PHP 8.1 Support by @patinthehat in https://github.com/spatie/laravel-mailable-test/pull/30 40 | - Add Laravel 9 support 41 | 42 | ## New Contributors 43 | 44 | - @krsriq made their first contribution in https://github.com/spatie/laravel-mailable-test/pull/29 45 | - @patinthehat made their first contribution in https://github.com/spatie/laravel-mailable-test/pull/30 46 | 47 | **Full Changelog**: https://github.com/spatie/laravel-mailable-test/compare/2.2.3...2.3.0 48 | 49 | ## 2.2.3 - 2021-01-06 50 | 51 | - add PHP 8 support [#28](https://github.com/spatie/laravel-mailable-test/pull/28) 52 | 53 | ## 2.2.2 - 2020-12-17 54 | 55 | - update faker dependency 56 | 57 | ## 2.2.1 - 2020-09-09 58 | 59 | - Add Laravel 8.0 support 60 | 61 | ## 2.2.0 - 2020-03-03 62 | 63 | - Add Laravel 7.0 support 64 | 65 | ## 2.1.0 - 2019-09-04 66 | 67 | - Add Laravel 6.0 support 68 | 69 | ## 2.0.2 - 2018-08-28 70 | 71 | - add support for Laravel 5.7 72 | 73 | ## 2.0.1 - 2018-02-08 74 | 75 | - Support Laravel 5.6 76 | 77 | ## 2.0.0 - 2017-09-01 78 | 79 | - add support for Laravel 5.5 80 | - dropped support for older versions of Laravel 81 | - renamed config file from `laravel-mailable-test` to `mailable-test` 82 | 83 | ## 1.1.0 - 2017-06-15 84 | 85 | - adds a `base_namespace` configuration option 86 | 87 | ## 1.0.3 - 2017-03-23 88 | 89 | - fixes publishing of config file 90 | 91 | ## 1.0.2 - 2017-02-04 92 | 93 | - improve console output 94 | 95 | ## 1.0.1 - 2017-01-30 96 | 97 | - fix naming of config file 98 | - fix binding issues 99 | 100 | ## 1.0.0 - 2017-01-30 101 | 102 | - initial release 103 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Spatie bvba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An artisan command to easily test a mailable 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-mailable-test.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-mailable-test) 4 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spatie/laravel-mailable-test/run-tests.yml?branch=main&label=tests)](https://github.com/spatie/laravel-mailable-test/actions/workflows/run-tests.yml) 6 | ![Check & fix styling](https://github.com/spatie/laravel-mailable-test/workflows/Check%20&%20fix%20styling/badge.svg) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-mailable-test.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-mailable-test) 8 | 9 | Do you have to fill out an entire form just to test a mail sent by your app? Or even worse, complete an entire checkout process to just view and debug an order confirmation mail? Stop. The. Madness. 10 | 11 | This package provides an artisan command that can send a mailable to an email-address. It can be used like this: 12 | 13 | ```bash 14 | php artisan mail:send-test "OrderConfirmation" recipient@mail.com 15 | ``` 16 | 17 | The given mailable will be sent to the given recipient. Any parameters handed to the `__construct` method of the mailable class will be automatically passed in. 18 | 19 | ## Support us 20 | 21 | [](https://spatie.be/github-ad-click/laravel-mailable-test) 22 | 23 | We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us). 24 | 25 | We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards). 26 | 27 | ## Installation 28 | 29 | You can install the package via composer: 30 | 31 | ``` bash 32 | composer require spatie/laravel-mailable-test 33 | ``` 34 | 35 | The package will automatically register itself. 36 | 37 | Optionally you can publish the config-file with: 38 | 39 | ```bash 40 | php artisan vendor:publish --provider="Spatie\MailableTest\MailableTestServiceProvider" --tag="config" 41 | ``` 42 | 43 | This is the contents of the published config file: 44 | 45 | ```php 46 | return [ 47 | 48 | /* 49 | * This class will be used to generate argument values for the constructor 50 | * of a mailable. This can be any class as long as it 51 | * extends \Spatie\MailableTest\ArgumentValueProvider::class 52 | */ 53 | 'argument_value_provider_class' => \Spatie\MailableTest\FakerArgumentValueProvider::class, 54 | 55 | /* 56 | * Base namespace Mailable classes 57 | */ 58 | 'base_namespace' => 'App\Mail', 59 | ]; 60 | ``` 61 | 62 | ## Usage 63 | 64 | To send any mailable issue this artisan command: 65 | 66 | ```bash 67 | php artisan mail:send-test "App\Mail\MyMailable" recipient@mail.com 68 | ``` 69 | 70 | This will send the given mailable to the given mail-address. The to-, cc- and bcc-address that may be set in the given mailable will be cleared. The mail will only be sent to the mail-address given in the artisan command. 71 | 72 | The package will provide a value for any typehinted argument of the constructor of the mailable. If a argument is a `int`, `string` or `bool` the package will generated a value using [Faker](https://github.com/FakerPHP/Faker). Any argument that typehints an Eloquent model will receive the first record of that model. For all arguments that have a class typehint an argument will be generated using the container. 73 | 74 | ## Customizing the values passed to the mailable constructor 75 | 76 | ### Via the command 77 | 78 | The easiest way to customize the values passed to the mailable constructor is to use the `values` option of the `mail:send-test` command. Image the constructor for your mailable looks like this: 79 | 80 | ```php 81 | public function __construct(string $title, Order $order) 82 | { 83 | ... 84 | } 85 | ``` 86 | 87 | The `Order` class in this example is an eloquent model. If you don't want the package to generate fake values or use the first `Order`, you can pass a `values` option to the command. The option should get a string with comma separated pair. The first value of each pair (separated by ':') should be the name of the argument in the mailable constructor. The second value should be the value that should be passed to that argument. For arguments concerning Eloquent models, the passed value will be used as the id. 88 | 89 | So in this example `My title` will be passed to `$title` and an `Order` with id 5 will be passed to `$order`. 90 | 91 | ```php 92 | php artisan mail:send-test "App\Mail\OrderConfirmation" recipient@mail.com --values="title:My title,order:5" 93 | ``` 94 | 95 | ### By overriding the `ArgumentValueProvider` 96 | 97 | The class that is responsible for getting values that should be passed on to the mailable constructor is `Spatie\MailableTest\FakerArgumentValueProvider`. You can override this class by specifying your own class in the `argument_value_provider_class` in the `laravel-mailable-test` config file. 98 | 99 | By default the package will pass the first record of an Eloquent model to each argument that typehints a model. If you want to use your factories instead, you can do this. 100 | 101 | ```php 102 | namespace App; 103 | 104 | use Spatie\MailableTest\FakerArgumentValueProvider; 105 | 106 | class MyCustomArgumentValueProvider extends FakerArgumentValueProvider 107 | { 108 | protected function getModelInstance(string $mailableClass, string $argumentName, Model $model, $id): Model 109 | { 110 | return factory(get_class($model)); 111 | } 112 | } 113 | ``` 114 | 115 | ## Changelog 116 | 117 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 118 | 119 | ## Testing 120 | 121 | ``` bash 122 | $ composer test 123 | ``` 124 | 125 | ## Contributing 126 | 127 | Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details. 128 | 129 | ## Security 130 | 131 | If you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker. 132 | 133 | ## Postcardware 134 | 135 | You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. 136 | 137 | Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium. 138 | 139 | We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards). 140 | 141 | ## Credits 142 | 143 | - [Freek Van der Herten](https://github.com/freekmurze) 144 | - [All Contributors](../../contributors) 145 | 146 | ## License 147 | 148 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 149 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spatie/laravel-mailable-test", 3 | "description": "An artisan command to easily test a mailable", 4 | "keywords": [ 5 | "spatie", 6 | "laravel-mailable-test" 7 | ], 8 | "homepage": "https://github.com/spatie/laravel-mailable-test", 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Freek Van der Herten", 13 | "email": "freek@spatie.be", 14 | "homepage": "https://spatie.be", 15 | "role": "Developer" 16 | } 17 | ], 18 | "minimum-stability": "dev", 19 | "prefer-stable": true, 20 | "require": { 21 | "php": "^7.3|^8.0", 22 | "fakerphp/faker": "^1.16|^1.9.1", 23 | "illuminate/contracts": "^7.0|^8.73|^9.0|^10.0|^11.0|^12.0", 24 | "illuminate/database": "^7.0|^8.73|^9.0|^10.0|^11.0|^12.0" 25 | }, 26 | "require-dev": { 27 | "mockery/mockery": "^1.0|^1.4", 28 | "orchestra/testbench": "^5.0|^6.23|^7.0|^8.0|^9.0|^10.0" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Spatie\\MailableTest\\": "src" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Spatie\\MailableTest\\Test\\": "tests" 38 | } 39 | }, 40 | "scripts": { 41 | "test": "vendor/bin/phpunit" 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "extra": { 47 | "laravel": { 48 | "providers": [ 49 | "Spatie\\MailableTest\\MailableTestServiceProvider" 50 | ] 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /config/mailable-test.php: -------------------------------------------------------------------------------- 1 | \Spatie\MailableTest\FakerArgumentValueProvider::class, 11 | 12 | /* 13 | * Base namespace Mailable classes 14 | */ 15 | 'base_namespace' => 'App\Mail', 16 | ]; 17 | -------------------------------------------------------------------------------- /src/ArgumentValueProvider.php: -------------------------------------------------------------------------------- 1 | faker = $faker; 18 | } 19 | 20 | /** 21 | * @param string $mailableClass 22 | * @param string $argumentName 23 | * @param string $argumentType 24 | * @param string|null $defaultValue 25 | * 26 | * @return mixed 27 | */ 28 | public function getValue(string $mailableClass, string $argumentName, string $argumentType = '', $defaultValue = null) 29 | { 30 | if ($argumentType === 'int') { 31 | return $defaultValue ?? $this->faker->numberBetween(1, 100); 32 | } 33 | 34 | if ($argumentType === 'string') { 35 | return $defaultValue ?? $this->faker->sentence(); 36 | } 37 | 38 | if ($argumentType === 'bool') { 39 | $defaultValue = ($defaultValue == 'false' ? false : $defaultValue); 40 | 41 | return $defaultValue ?? $this->faker->boolean(50); 42 | } 43 | 44 | try { 45 | $argumentValue = app($argumentType); 46 | } catch (Exception $exception) { 47 | throw CouldNotDetermineValue::create($argumentType, $argumentName, $exception); 48 | } 49 | 50 | if ($argumentValue instanceof Model) { 51 | return $this->getModelInstance($mailableClass, $argumentName, $argumentValue, $id = $defaultValue); 52 | } 53 | 54 | return $argumentValue; 55 | } 56 | 57 | /** 58 | * @param string $mailableClass 59 | * @param string $argumentName 60 | * @param \Illuminate\Database\Eloquent\Model $model 61 | * @param string|int|null $id 62 | * 63 | * @return \Illuminate\Database\Eloquent\Model 64 | * @throws \Spatie\MailableTest\Exceptions\CouldNotDetermineValue 65 | */ 66 | protected function getModelInstance(string $mailableClass, string $argumentName, Model $model, $id): Model 67 | { 68 | $modelInstance = $id ? $model->find($id) : $model->first(); 69 | 70 | if (! $modelInstance) { 71 | throw CouldNotDetermineValue::noModelInstanceFound($model); 72 | } 73 | 74 | return $modelInstance; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/MailableFactory.php: -------------------------------------------------------------------------------- 1 | argumentValueProvider = $argumentValueProvider; 17 | } 18 | 19 | public function getInstance(string $mailableClass, string $toEmail, $defaultValues): Mailable 20 | { 21 | $argumentValues = $this->getArguments($mailableClass, $defaultValues); 22 | 23 | $mailable = new $mailableClass(...$argumentValues); 24 | 25 | $mailable = $this->setRecipient($mailable, $toEmail); 26 | 27 | return $mailable; 28 | } 29 | 30 | public function getArguments(string $mailableClass, array $defaultValues) 31 | { 32 | $parameters = (new ReflectionClass($mailableClass)) 33 | ->getConstructor() 34 | ->getParameters(); 35 | 36 | return collect($parameters) 37 | ->map(function (ReflectionParameter $reflectionParameter) use ($mailableClass, $defaultValues) { 38 | return $this->argumentValueProvider->getValue( 39 | $mailableClass, 40 | $reflectionParameter->getName(), 41 | $reflectionParameter->getType()->getName(), 42 | $defaultValues[$reflectionParameter->getName()] ?? null 43 | ); 44 | }); 45 | } 46 | 47 | protected function setRecipient(Mailable $mailable, string $email): Mailable 48 | { 49 | $mailable->to($email); 50 | $mailable->cc([]); 51 | $mailable->bcc([]); 52 | 53 | return $mailable; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/MailableTestServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 14 | __DIR__.'/../config/mailable-test.php' => config_path('mailable-test.php'), 15 | ], 'config'); 16 | } 17 | 18 | public function register() 19 | { 20 | $this->mergeConfigFrom(__DIR__.'/../config/mailable-test.php', 'mailable-test'); 21 | 22 | $this->app->bind(MailableFactory::class, function () { 23 | $argumentValueProviderClass = config('mailable-test.argument_value_provider_class'); 24 | 25 | if (! is_a($argumentValueProviderClass, ArgumentValueProvider::class, true)) { 26 | throw InvalidConfiguration::invalidValueProviderClass($argumentValueProviderClass); 27 | } 28 | 29 | $argumentValueProvider = app($argumentValueProviderClass); 30 | 31 | return new MailableFactory($argumentValueProvider); 32 | }); 33 | 34 | $this->app->bind(ArgumentValueProvider::class, function () { 35 | $argumentValueProvider = config('mailable-test.argument_value_provider_class'); 36 | 37 | return new $argumentValueProvider( 38 | Faker::create() 39 | ); 40 | }); 41 | 42 | $this->commands([ 43 | SendTestMail::class, 44 | ]); 45 | } 46 | 47 | public function provides(): array 48 | { 49 | return ['command.mail.send.test']; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SendTestMail.php: -------------------------------------------------------------------------------- 1 | guardAgainstInvalidArguments(); 19 | 20 | $mailableClass = $this->getMailableClass(); 21 | 22 | $mailable = app(MailableFactory::class)->getInstance( 23 | $mailableClass, 24 | $this->argument('recipient'), 25 | $this->getValues() 26 | ); 27 | 28 | Mail::send($mailable); 29 | 30 | $this->comment("Mailable `{$mailableClass}` sent to {$this->argument('recipient')}!"); 31 | } 32 | 33 | protected function guardAgainstInvalidArguments() 34 | { 35 | $validator = Validator::make( 36 | ['email' => $this->argument('recipient')], 37 | ['email' => 'email'] 38 | ); 39 | 40 | if (! $validator->passes()) { 41 | throw new InvalidArgumentException("`{$this->argument('recipient')}` is not a valid e-mail address"); 42 | } 43 | } 44 | 45 | protected function getValues(): array 46 | { 47 | if (! $this->option('values')) { 48 | return []; 49 | } 50 | 51 | $values = explode(',', $this->option('values')); 52 | 53 | return collect($values) 54 | ->mapWithKeys(function (string $value) { 55 | $values = explode(':', $value); 56 | 57 | if (count($values) != 2) { 58 | throw new InvalidArgumentException("The given value for the option 'values' `{$this->option('values')}` is not valid."); 59 | } 60 | 61 | return [$values[0] => $values[1]]; 62 | }) 63 | ->toArray(); 64 | } 65 | 66 | protected function getMailableClass() 67 | { 68 | $mailableClass = $this->argument('mailableClass'); 69 | 70 | if (! class_exists($mailableClass)) { 71 | $mailableClass = sprintf('%s\\%s', config('mailable-test.base_namespace'), $mailableClass); 72 | 73 | if (! class_exists($mailableClass)) { 74 | throw new InvalidArgumentException("Mailable `{$mailableClass}` does not exist."); 75 | } 76 | } 77 | 78 | return $mailableClass; 79 | } 80 | } 81 | --------------------------------------------------------------------------------