├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── dusk-fakes.php └── src ├── Bus ├── PersistentBus.php ├── PersistentBusFake.php └── UncachedPersistentBusFake.php ├── LaravelDuskFakesServiceProvider.php ├── Mails ├── PersistentMailFake.php ├── PersistentMails.php └── UncachedPersistentMailFake.php ├── Notifications ├── PersistentNotificationFake.php ├── PersistentNotifications.php └── UncachedPersistentNotificationFake.php └── Queue ├── PersistentQueue.php ├── PersistentQueueFake.php └── UncachedPersistentQueueFake.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-dusk-fakes` will be documented in this file. 4 | 5 | ## 1.8.0 - 2025-02-23 6 | 7 | ### What's Changed 8 | 9 | * Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/25 10 | * Bump aglipanci/laravel-pint-action from 2.4 to 2.5 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/26 11 | * Support for Laravel 12 by @pascalbaljet in https://github.com/protonemedia/laravel-dusk-fakes/pull/27 12 | 13 | **Full Changelog**: https://github.com/protonemedia/laravel-dusk-fakes/compare/1.7.0...1.8.0 14 | 15 | ## 1.7.0 - 2024-11-20 16 | 17 | ### What's Changed 18 | 19 | * Support for PHP 8.4 by @pascalbaljet in https://github.com/protonemedia/laravel-dusk-fakes/pull/24 20 | 21 | **Full Changelog**: https://github.com/protonemedia/laravel-dusk-fakes/compare/1.6.0...1.7.0 22 | 23 | ## 1.6.0 - 2024-03-12 24 | 25 | ### What's Changed 26 | 27 | * Bump aglipanci/laravel-pint-action from 2.3.0 to 2.3.1 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/18 28 | * Support for Laravel 11 by @pascalbaljet in https://github.com/protonemedia/laravel-dusk-fakes/pull/19 29 | 30 | **Full Changelog**: https://github.com/protonemedia/laravel-dusk-fakes/compare/1.5.0...1.6.0 31 | 32 | ## 1.5.0 - 2024-01-02 33 | 34 | ### What's Changed 35 | 36 | * Bump dependabot/fetch-metadata from 1.3.6 to 1.4.0 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/10 37 | * Bump dependabot/fetch-metadata from 1.4.0 to 1.5.0 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/11 38 | * Bump dependabot/fetch-metadata from 1.5.0 to 1.5.1 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/12 39 | * Bump dependabot/fetch-metadata from 1.5.1 to 1.6.0 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/14 40 | * Support for PHP 8.3. Dropped support for Laravel 9 by @pascalbaljet in https://github.com/protonemedia/laravel-dusk-fakes/pull/17 41 | * Bump aglipanci/laravel-pint-action from 2.2.0 to 2.3.0 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/13 42 | * Bump actions/checkout from 3 to 4 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/15 43 | * Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/protonemedia/laravel-dusk-fakes/pull/16 44 | 45 | **Full Changelog**: https://github.com/protonemedia/laravel-dusk-fakes/compare/1.4.0...1.5.0 46 | 47 | ## 1.4.0 - 2023-04-04 48 | 49 | - Support for persistent Bus + Queue fake 50 | - Drop support for Laravel 9 51 | - Drop support for PHP 8.0 52 | 53 | ## 1.3.1 - 2023-02-09 54 | 55 | - Fix for restoring corrupted storage 56 | 57 | ## 1.3.0 - 2023-02-06 58 | 59 | - Support for Laravel 10 60 | 61 | ## 1.2.0 - 2022-12-09 62 | 63 | - Support for PHP 8.2 64 | 65 | ## 1.1.0 - 2022-09-05 66 | 67 | Support for testing Mails 68 | 69 | ## 1.0.0 - 2022-09-05 70 | 71 | Initial release 72 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Protone Media B.V. 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 | # Laravel Dusk Fakes 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/protonemedia/laravel-dusk-fakes.svg?style=flat-square)](https://packagist.org/packages/protonemedia/laravel-dusk-fakes) 4 | [![run-tests](https://github.com/protonemedia/laravel-dusk-fakes/actions/workflows/run-tests.yml/badge.svg?branch=main)](https://github.com/protonemedia/laravel-dusk-fakes/actions/workflows/run-tests.yml) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/protonemedia/laravel-dusk-fakes.svg?style=flat-square)](https://packagist.org/packages/protonemedia/laravel-dusk-fakes) 6 | [![Buy us a tree](https://img.shields.io/badge/Treeware-%F0%9F%8C%B3-lightgreen)](https://plant.treeware.earth/protonemedia/laravel-dusk-fakes) 7 | 8 | ## Sponsor Us 9 | 10 | [](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-dusk-fakes) 11 | 12 | ❤️ We proudly support the community by developing Laravel packages and giving them away for free. If this package saves you time or if you're relying on it professionally, please consider [sponsoring the maintenance and development](https://github.com/sponsors/pascalbaljet) and check out our latest premium package: [Inertia Table](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-dusk-fakes). Keeping track of issues and pull requests takes time, but we're happy to help! 13 | 14 | ## Installation 15 | 16 | You can install the package via composer: 17 | 18 | ```bash 19 | composer require protonemedia/laravel-dusk-fakes --dev 20 | ``` 21 | 22 | ### Persist Bus (queued jobs) 23 | 24 | Make sure you've set the `DUSK_FAKE_BUS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). 25 | 26 | Finally, add the `PersistentBus` trait to your test. You don't have to manually call the `fake()` method on the `Bus` facade. 27 | 28 | ```php 29 | browse(function (Browser $browser) { 49 | $order = Order::factory()->create(); 50 | 51 | $browser->visit('/order/'.$order->id) 52 | ->press('Confirm') 53 | ->waitForText('We will generate an invoice!'); 54 | 55 | Bus::assertDispatched(SendOrderInvoice::class); 56 | }); 57 | } 58 | } 59 | ``` 60 | 61 | If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `jobsToFake()` method: 62 | 63 | ```php 64 | Bus::jobsToFake(ShipOrder::class); 65 | 66 | $browser->visit(...); 67 | 68 | Bus::assertDispatched(SendOrderInvoice::class); 69 | ``` 70 | 71 | ### Persist Mails 72 | 73 | Make sure you've set the `DUSK_FAKE_MAILS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). 74 | 75 | Finally, add the `PersistentMails` trait to your test. You don't have to manually call the `fake()` method on the `Mail` facade. 76 | 77 | ```php 78 | browse(function (Browser $browser) { 98 | $order = Order::factory()->create(); 99 | 100 | $browser->visit('/order/'.$order->id) 101 | ->press('Confirm') 102 | ->waitForText('We have emailed your order confirmation!'); 103 | 104 | Mail::assertSent(OrderConfirmed::class, function ($mail) use ($user) { 105 | return $mail->hasTo($user->email); 106 | }); 107 | }); 108 | } 109 | } 110 | ``` 111 | 112 | ### Persist Notifications 113 | 114 | Make sure you've set the `DUSK_FAKE_NOTIFICATIONS` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). 115 | 116 | Finally, add the `PersistentNotifications` trait to your test. You don't have to manually call the `fake()` method on the `Notification` facade. 117 | 118 | ```php 119 | browse(function (Browser $browser) { 139 | $user = User::factory()->create(); 140 | 141 | $browser->visit('/forgot-password') 142 | ->type('email', $user->email) 143 | ->press('Email Password Reset Link') 144 | ->waitForText('We have emailed your password reset link!'); 145 | 146 | Notification::assertSentTo($user, ResetPassword::class); 147 | }); 148 | } 149 | } 150 | ``` 151 | 152 | ### Persist Queue 153 | 154 | Make sure you've set the `DUSK_FAKE_QUEUE` environment variable to `true` in the [Dusk environment](https://laravel.com/docs/9.x/dusk#environment-handling). 155 | 156 | Finally, add the `PersistentQueue` trait to your test. You don't have to manually call the `fake()` method on the `Queue` facade. 157 | 158 | ```php 159 | browse(function (Browser $browser) { 179 | $order = Order::factory()->create(); 180 | 181 | $browser->visit('/order/'.$order->id) 182 | ->press('Confirm') 183 | ->waitForText('We will generate an invoice!'); 184 | 185 | Queue::assertDispatched(SendOrderInvoice::class); 186 | }); 187 | } 188 | } 189 | ``` 190 | 191 | If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `jobsToFake()` method: 192 | 193 | ```php 194 | Queue::jobsToFake(ShipOrder::class); 195 | 196 | $browser->visit(...); 197 | 198 | Queue::assertDispatched(SendOrderInvoice::class); 199 | ``` 200 | 201 | ## Changelog 202 | 203 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 204 | 205 | ## Contributing 206 | 207 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 208 | 209 | ## Other Laravel packages 210 | 211 | * [`Inertia Table`](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-dusk-fakes): The Ultimate Table for Inertia.js with built-in Query Builder. 212 | * [`Laravel Blade On Demand`](https://github.com/protonemedia/laravel-blade-on-demand): Laravel package to compile Blade templates in memory. 213 | * [`Laravel Cross Eloquent Search`](https://github.com/protonemedia/laravel-cross-eloquent-search): Laravel package to search through multiple Eloquent models. 214 | * [`Laravel Eloquent Scope as Select`](https://github.com/protonemedia/laravel-eloquent-scope-as-select): Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery. 215 | * [`Laravel FFMpeg`](https://github.com/protonemedia/laravel-ffmpeg): This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem. 216 | * [`Laravel MinIO Testing Tools`](https://github.com/protonemedia/laravel-minio-testing-tools): Run your tests against a MinIO S3 server. 217 | * [`Laravel Mixins`](https://github.com/protonemedia/laravel-mixins): A collection of Laravel goodies. 218 | * [`Laravel Paddle`](https://github.com/protonemedia/laravel-paddle): Paddle.com API integration for Laravel with support for webhooks/events. 219 | * [`Laravel Task Runner`](https://github.com/protonemedia/laravel-task-runner): Write Shell scripts like Blade Components and run them locally or on a remote server. 220 | * [`Laravel Verify New Email`](https://github.com/protonemedia/laravel-verify-new-email): This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified. 221 | * [`Laravel XSS Protection`](https://github.com/protonemedia/laravel-xss-protection): Laravel Middleware to protect your app against Cross-site scripting (XSS). It sanitizes request input, and it can sanatize Blade echo statements. 222 | 223 | ## Security 224 | 225 | If you discover any security related issues, please email pascal@protone.media instead of using the issue tracker. 226 | 227 | ## Credits 228 | 229 | - [Pascal Baljet](https://github.com/protonemedia) 230 | - [All Contributors](../../contributors) 231 | 232 | ## License 233 | 234 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 235 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protonemedia/laravel-dusk-fakes", 3 | "description": "Persistent Fakes for Laravel Dusk", 4 | "keywords": [ 5 | "protonemedia", 6 | "laravel", 7 | "laravel-dusk-fakes" 8 | ], 9 | "homepage": "https://github.com/protonemedia/laravel-dusk-fakes", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Pascal Baljet", 14 | "email": "pascal@protone.media", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.2|^8.3|^8.4", 20 | "illuminate/contracts": "^11.43|^12.0", 21 | "laravel/dusk": "^8.0", 22 | "spatie/invade": "^1.1" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.0", 26 | "nesbot/carbon": "^2.66|^3.0", 27 | "nunomaduro/collision": "^7.0|^8.0", 28 | "orchestra/testbench": "^9.11|^10.0", 29 | "pestphp/pest": "^3.0", 30 | "pestphp/pest-plugin-laravel": "^3.0", 31 | "phpunit/phpunit": "^10.4|^11.5.3" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "ProtoneMedia\\LaravelDuskFakes\\": "src", 36 | "ProtoneMedia\\LaravelDuskFakes\\Database\\Factories\\": "database/factories" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "ProtoneMedia\\LaravelDuskFakes\\Tests\\": "tests" 42 | } 43 | }, 44 | "scripts": { 45 | "test": "vendor/bin/pest", 46 | "test-coverage": "vendor/bin/pest --coverage", 47 | "format": "vendor/bin/pint" 48 | }, 49 | "config": { 50 | "sort-packages": true, 51 | "allow-plugins": { 52 | "pestphp/pest-plugin": true 53 | } 54 | }, 55 | "extra": { 56 | "laravel": { 57 | "providers": [ 58 | "ProtoneMedia\\LaravelDuskFakes\\LaravelDuskFakesServiceProvider" 59 | ] 60 | } 61 | }, 62 | "minimum-stability": "dev", 63 | "prefer-stable": true 64 | } -------------------------------------------------------------------------------- /config/dusk-fakes.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'enabled' => env('DUSK_FAKE_BUS', false), 6 | 'storage_root' => storage_path('framework/testing/bus'), 7 | ], 8 | 9 | 'mails' => [ 10 | 'enabled' => env('DUSK_FAKE_MAILS', false), 11 | 'storage_root' => storage_path('framework/testing/mails'), 12 | ], 13 | 14 | 'notifications' => [ 15 | 'enabled' => env('DUSK_FAKE_NOTIFICATIONS', false), 16 | 'storage_root' => storage_path('framework/testing/notifications'), 17 | ], 18 | 19 | 'queue' => [ 20 | 'enabled' => env('DUSK_FAKE_QUEUE', false), 21 | 'storage_root' => storage_path('framework/testing/queue'), 22 | ], 23 | ]; 24 | -------------------------------------------------------------------------------- /src/Bus/PersistentBus.php: -------------------------------------------------------------------------------- 1 | directory = rtrim(config('dusk-fakes.bus.storage_root'), '/'); 23 | 24 | $this->storage = $this->directory.'/serialized'; 25 | 26 | (new Filesystem)->ensureDirectoryExists($this->directory); 27 | 28 | $this->loadBus(); 29 | } 30 | 31 | public function jobsToFake($jobsToFake = []) 32 | { 33 | $this->jobsToFake = Arr::wrap($jobsToFake); 34 | 35 | $this->storeBus(); 36 | } 37 | 38 | public function cleanStorage() 39 | { 40 | (new Filesystem)->cleanDirectory($this->directory); 41 | } 42 | 43 | public function loadBus(): self 44 | { 45 | $unserialized = file_exists($this->storage) 46 | ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) 47 | : []; 48 | 49 | $this->jobsToFake = $unserialized['jobsToFake'] ?? []; 50 | $this->commands = $unserialized['commands'] ?? []; 51 | $this->commandsSync = $unserialized['commandsSync'] ?? []; 52 | $this->commandsAfterResponse = $unserialized['commandsAfterResponse'] ?? []; 53 | $this->batches = $unserialized['batches'] ?? []; 54 | 55 | return $this; 56 | } 57 | 58 | public function dispatch($command) 59 | { 60 | return tap(parent::dispatch($command), fn () => $this->storeBus()); 61 | } 62 | 63 | public function dispatchSync($command, $handler = null) 64 | { 65 | return tap(parent::dispatchSync($command, $handler), fn () => $this->storeBus()); 66 | } 67 | 68 | public function dispatchNow($command, $handler = null) 69 | { 70 | return tap(parent::dispatchNow($command, $handler), fn () => $this->storeBus()); 71 | } 72 | 73 | public function dispatchToQueue($command) 74 | { 75 | return tap(parent::dispatchToQueue($command), fn () => $this->storeBus()); 76 | } 77 | 78 | public function dispatchAfterResponse($command) 79 | { 80 | return tap(parent::dispatchAfterResponse($command), fn () => $this->storeBus()); 81 | } 82 | 83 | public function recordPendingBatch(PendingBatch $pendingBatch) 84 | { 85 | return tap(parent::recordPendingBatch($pendingBatch), fn () => $this->storeBus()); 86 | } 87 | 88 | public function cleanupCommand(array $jobs): array 89 | { 90 | return collect($jobs)->map(function ($job) { 91 | tap(invade($job), function ($job) { 92 | if (! $job->job) { 93 | return; 94 | } 95 | 96 | $job = invade($job->job); 97 | $job->container = null; 98 | 99 | if (! $job->instance) { 100 | return; 101 | } 102 | 103 | invade($job->instance)->container = null; 104 | invade($job->instance)->dispatcher = null; 105 | }); 106 | 107 | return $job; 108 | })->all(); 109 | } 110 | 111 | private function storeBus() 112 | { 113 | (new Filesystem)->ensureDirectoryExists($this->directory); 114 | 115 | file_put_contents($this->storage, serialize([ 116 | 'jobsToFake' => $this->jobsToFake, 117 | 'commands' => collect($this->commands)->map([$this, 'cleanupCommand'])->all(), 118 | 'commandsSync' => collect($this->commandsSync)->map([$this, 'cleanupCommand'])->all(), 119 | 'commandsAfterResponse' => collect($this->commandsAfterResponse)->map([$this, 'cleanupCommand'])->all(), 120 | 'batches' => collect($this->batches)->each(function (PendingBatchFake $batch) { 121 | tap(invade($batch), function ($batch) { 122 | $batch->bus = null; 123 | }); 124 | 125 | return $batch; 126 | })->all(), 127 | ])); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Bus/UncachedPersistentBusFake.php: -------------------------------------------------------------------------------- 1 | forwardCallTo($this->fake->loadBus(), $method, $parameters); 26 | } 27 | 28 | /** 29 | * Handle dynamic static method calls into the fake. 30 | * 31 | * @param string $method 32 | * @param array $parameters 33 | * @return mixed 34 | */ 35 | public static function __callStatic($method, $parameters) 36 | { 37 | return app(static::class)->$method(...$parameters); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/LaravelDuskFakesServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom( 22 | __DIR__.'/../config/dusk-fakes.php', 23 | 'dusk-fakes' 24 | ); 25 | } 26 | 27 | public function boot() 28 | { 29 | if ($this->app->runningInConsole()) { 30 | $this->publishes([ 31 | __DIR__.'/../config/dusk-fakes.php' => config_path('dusk-fakes.php'), 32 | ], 'config'); 33 | } 34 | 35 | $this->bootFakeBus(); 36 | $this->bootFakeMails(); 37 | $this->bootFakeNotifications(); 38 | $this->bootFakeQueue(); 39 | } 40 | 41 | private function bootFakeBus() 42 | { 43 | if (! config('dusk-fakes.bus.enabled')) { 44 | return; 45 | } 46 | 47 | $fake = new PersistentBusFake(app(QueueingDispatcher::class)); 48 | 49 | $this->app->singleton( 50 | PersistentBusFake::class, 51 | fn () => $fake 52 | ); 53 | 54 | Bus::swap($fake); 55 | } 56 | 57 | private function bootFakeMails() 58 | { 59 | if (! config('dusk-fakes.mails.enabled')) { 60 | return; 61 | } 62 | 63 | $fake = new PersistentMailFake; 64 | 65 | $this->app->singleton( 66 | PersistentMailFake::class, 67 | fn () => $fake 68 | ); 69 | 70 | Mail::swap($fake); 71 | } 72 | 73 | private function bootFakeNotifications() 74 | { 75 | if (! config('dusk-fakes.notifications.enabled')) { 76 | return; 77 | } 78 | 79 | $fake = new PersistentNotificationFake; 80 | 81 | $this->app->singleton( 82 | PersistentNotificationFake::class, 83 | fn () => $fake 84 | ); 85 | 86 | Notification::swap($fake); 87 | } 88 | 89 | private function bootFakeQueue() 90 | { 91 | if (! config('dusk-fakes.queue.enabled')) { 92 | return; 93 | } 94 | 95 | $fake = new PersistentQueueFake(app(), [], app(QueueContract::class)); 96 | 97 | $this->app->singleton( 98 | PersistentQueueFake::class, 99 | fn () => $fake 100 | ); 101 | 102 | Queue::swap($fake); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Mails/PersistentMailFake.php: -------------------------------------------------------------------------------- 1 | directory = rtrim(config('dusk-fakes.mails.storage_root'), '/'); 17 | 18 | $this->storage = $this->directory.'/serialized'; 19 | 20 | (new Filesystem)->ensureDirectoryExists($this->directory); 21 | 22 | $this->loadMails(); 23 | } 24 | 25 | public function cleanStorage() 26 | { 27 | (new Filesystem)->cleanDirectory($this->directory); 28 | } 29 | 30 | public function loadMails(): self 31 | { 32 | $unserialized = file_exists($this->storage) 33 | ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) 34 | : []; 35 | 36 | $this->mailables = $unserialized['mailables'] ?? []; 37 | $this->queuedMailables = $unserialized['queuedMailables'] ?? []; 38 | 39 | return $this; 40 | } 41 | 42 | public function send($view, array $data = [], $callback = null) 43 | { 44 | parent::send($view, $data, $callback); 45 | 46 | $this->storeMails(); 47 | } 48 | 49 | public function queue($view, $queue = null) 50 | { 51 | parent::queue($view, $queue); 52 | 53 | $this->storeMails(); 54 | } 55 | 56 | private function storeMails() 57 | { 58 | (new Filesystem)->ensureDirectoryExists($this->directory); 59 | 60 | file_put_contents($this->storage, serialize([ 61 | 'mailables' => $this->mailables, 62 | 'queuedMailables' => $this->queuedMailables, 63 | ])); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Mails/PersistentMails.php: -------------------------------------------------------------------------------- 1 | forwardCallTo($this->fake->loadMails(), $method, $parameters); 26 | } 27 | 28 | /** 29 | * Handle dynamic static method calls into the fake. 30 | * 31 | * @param string $method 32 | * @param array $parameters 33 | * @return mixed 34 | */ 35 | public static function __callStatic($method, $parameters) 36 | { 37 | return app(static::class)->$method(...$parameters); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Notifications/PersistentNotificationFake.php: -------------------------------------------------------------------------------- 1 | directory = rtrim(config('dusk-fakes.notifications.storage_root'), '/'); 17 | 18 | $this->storage = $this->directory.'/serialized'; 19 | 20 | (new Filesystem)->ensureDirectoryExists($this->directory); 21 | 22 | $this->loadNotifications(); 23 | } 24 | 25 | public function cleanStorage() 26 | { 27 | (new Filesystem)->cleanDirectory($this->directory); 28 | } 29 | 30 | public function loadNotifications(): self 31 | { 32 | $this->notifications = file_exists($this->storage) 33 | ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) 34 | : []; 35 | 36 | return $this; 37 | } 38 | 39 | public function sendNow($notifiables, $notification, ?array $channels = null) 40 | { 41 | parent::sendNow($notifiables, $notification, $channels); 42 | 43 | $this->storeNotifications(); 44 | } 45 | 46 | private function storeNotifications() 47 | { 48 | (new Filesystem)->ensureDirectoryExists($this->directory); 49 | 50 | file_put_contents($this->storage, serialize($this->notifications)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Notifications/PersistentNotifications.php: -------------------------------------------------------------------------------- 1 | forwardCallTo($this->fake->loadNotifications(), $method, $parameters); 26 | } 27 | 28 | /** 29 | * Handle dynamic static method calls into the fake. 30 | * 31 | * @param string $method 32 | * @param array $parameters 33 | * @return mixed 34 | */ 35 | public static function __callStatic($method, $parameters) 36 | { 37 | return app(static::class)->$method(...$parameters); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Queue/PersistentQueue.php: -------------------------------------------------------------------------------- 1 | directory = rtrim(config('dusk-fakes.queue.storage_root'), '/'); 20 | 21 | $this->storage = $this->directory.'/serialized'; 22 | 23 | (new Filesystem)->ensureDirectoryExists($this->directory); 24 | 25 | $this->loadQueue(); 26 | } 27 | 28 | public function jobsToFake($jobsToFake = []) 29 | { 30 | $this->jobsToFake = Collection::wrap($jobsToFake); 31 | 32 | $this->storeQueue(); 33 | } 34 | 35 | public function cleanStorage() 36 | { 37 | (new Filesystem)->cleanDirectory($this->directory); 38 | } 39 | 40 | public function loadQueue(): self 41 | { 42 | $unserialized = file_exists($this->storage) 43 | ? rescue(fn () => unserialize(file_get_contents($this->storage)), [], false) 44 | : []; 45 | 46 | $this->jobsToFake = Collection::make($unserialized['jobsToFake'] ?? []); 47 | $this->jobsToBeQueued = Collection::make($unserialized['jobsToBeQueued'] ?? []); 48 | $this->jobs = $unserialized['jobs'] ?? []; 49 | 50 | return $this; 51 | } 52 | 53 | public function push($job, $data = '', $queue = null) 54 | { 55 | parent::push($job, $data, $queue); 56 | 57 | $this->storeQueue(); 58 | } 59 | 60 | private function storeQueue() 61 | { 62 | (new Filesystem)->ensureDirectoryExists($this->directory); 63 | 64 | file_put_contents($this->storage, serialize([ 65 | 'jobsToFake' => $this->jobsToFake->all(), 66 | 'jobsToBeQueued' => $this->jobsToBeQueued->all(), 67 | 'jobs' => $this->jobs, 68 | ])); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Queue/UncachedPersistentQueueFake.php: -------------------------------------------------------------------------------- 1 | forwardCallTo($this->fake->loadQueue(), $method, $parameters); 26 | } 27 | 28 | /** 29 | * Handle dynamic static method calls into the fake. 30 | * 31 | * @param string $method 32 | * @param array $parameters 33 | * @return mixed 34 | */ 35 | public static function __callStatic($method, $parameters) 36 | { 37 | return app(static::class)->$method(...$parameters); 38 | } 39 | } 40 | --------------------------------------------------------------------------------