diff --git a/src/Facades/Mail.php b/src/Facades/Mail.php index 48c48fb5..7387ee98 100644 --- a/src/Facades/Mail.php +++ b/src/Facades/Mail.php @@ -6,16 +6,19 @@ use Phenix\Mail\Constants\MailerType; use Phenix\Mail\Contracts\Mailable as MailableContract; +use Phenix\Mail\Contracts\Mailer; use Phenix\Mail\MailManager; use Phenix\Runtime\Facade; use Phenix\Testing\TestMail; /** - * @method static \Phenix\Mail\Contracts\Mailer mailer(MailerType|null $mailerType = null) - * @method static \Phenix\Mail\Contracts\Mailer using(MailerType $mailerType) - * @method static \Phenix\Mail\Contracts\Mailer to(array|string $to) - * @method static void send(\Phenix\Mail\Contracts\Mailable $mailable) - * @method static \Phenix\Mail\Contracts\Mailer fake(\Phenix\Mail\Constants\MailerType|null $mailerType = null) + * @method static Mailer mailer(MailerType|null $mailerType = null) + * @method static Mailer using(MailerType $mailerType) + * @method static Mailer to(array|string $to) + * @method static void send(MailableContract $mailable) + * @method static Mailer fake(MailerType|null $mailerType = null) + * @method static array getSendingLog(MailerType|null $mailerType = null) + * @method static void resetSendingLog(MailerType|null $mailerType = null) * @method static TestMail expect(MailableContract|string $mailable, MailerType|null $mailerType = null) * * @see \Phenix\Mail\MailManager @@ -29,11 +32,9 @@ public static function getKeyName(): string public static function expect(MailableContract|string $mailable, MailerType|null $mailerType = null): TestMail { - $mailerType ??= MailerType::from(Config::get('mail.default')); - return new TestMail( $mailable, - self::mailer($mailerType)->getSendingLog() + self::getSendingLog($mailerType) ); } } diff --git a/src/Mail/Contracts/Mailer.php b/src/Mail/Contracts/Mailer.php index 39324c59..cdb97402 100644 --- a/src/Mail/Contracts/Mailer.php +++ b/src/Mail/Contracts/Mailer.php @@ -17,4 +17,6 @@ public function bcc(array|string $bcc): self; public function send(Mailable $mailable): void; public function getSendingLog(): array; + + public function resetSendingLog(): void; } diff --git a/src/Mail/MailManager.php b/src/Mail/MailManager.php index 4c7c93f9..7ff99453 100644 --- a/src/Mail/MailManager.php +++ b/src/Mail/MailManager.php @@ -58,6 +58,20 @@ public function fake(MailerType|null $mailerType = null): void $this->config->setLogTransport($mailerType); } + public function getSendingLog(MailerType|null $mailerType = null): array + { + $mailerType ??= MailerType::from($this->config->default()); + + return $this->mailer($mailerType)->getSendingLog(); + } + + public function resetSendingLog(MailerType|null $mailerType = null): void + { + $mailerType ??= MailerType::from($this->config->default()); + + $this->mailer($mailerType)->resetSendingLog(); + } + protected function resolveMailer(MailerType $mailer): MailerContract { return match ($mailer) { diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php index d1447993..0a0ffcf2 100644 --- a/src/Mail/Mailer.php +++ b/src/Mail/Mailer.php @@ -90,6 +90,11 @@ public function getSendingLog(): array return $this->sendingLog; } + public function resetSendingLog(): void + { + $this->sendingLog = []; + } + protected function serviceConfig(): array { return []; diff --git a/src/Testing/Concerns/InteractWithHeaders.php b/src/Testing/Concerns/InteractWithHeaders.php new file mode 100644 index 00000000..8abd499f --- /dev/null +++ b/src/Testing/Concerns/InteractWithHeaders.php @@ -0,0 +1,76 @@ +response->getHeaders(); + } + + public function getHeader(string $name): string|null + { + return $this->response->getHeader($name); + } + + public function assertHeaderContains(array $needles): self + { + $needles = (array) $needles; + + foreach ($needles as $header => $value) { + Assert::assertNotNull($this->response->getHeader($header)); + Assert::assertEquals($value, $this->response->getHeader($header)); + } + + return $this; + } + + public function assertIsJson(): self + { + $contentType = $this->response->getHeader('content-type'); + + Assert::assertNotNull($contentType, $this->missingHeaderMessage); + Assert::assertStringContainsString( + 'application/json', + $contentType, + 'Response does not have a JSON content type.' + ); + + return $this; + } + + public function assertIsHtml(): self + { + $contentType = $this->response->getHeader('content-type'); + + Assert::assertNotNull($contentType, $this->missingHeaderMessage); + Assert::assertStringContainsString( + 'text/html', + $contentType, + 'Response does not have an HTML content type.' + ); + + return $this; + } + + public function assertIsPlainText(): self + { + $contentType = $this->response->getHeader('content-type'); + + Assert::assertNotNull($contentType, $this->missingHeaderMessage); + Assert::assertStringContainsString( + 'text/plain', + $contentType, + 'Response does not have a plain text content type.' + ); + + return $this; + } +} diff --git a/src/Testing/Concerns/InteractWithJson.php b/src/Testing/Concerns/InteractWithJson.php new file mode 100644 index 00000000..060ef86a --- /dev/null +++ b/src/Testing/Concerns/InteractWithJson.php @@ -0,0 +1,338 @@ +body, true); + + Assert::assertNotNull($json, 'Response body is not valid JSON.'); + Assert::assertIsArray($json, 'Response JSON is not an array.'); + + return $json; + } + + /** + * @param array $data + * @return self + */ + public function assertJsonContains(array $data, string|null $path = null): self + { + $json = $this->getDecodedBody(); + + if ($path) { + Assert::assertArrayHasKey( + $path, + $json, + "Response JSON does not have the expected '{$path}' wrapper." + ); + + $json = Arr::get($json, $path, []); + } + + foreach ($data as $key => $value) { + Assert::assertArrayHasKey($key, $json); + Assert::assertEquals($value, $json[$key]); + } + + return $this; + } + + /** + * @param array $data + * @return self + */ + public function assertJsonDoesNotContain(array $data, string|null $path = null): self + { + $json = $this->getDecodedBody(); + + if ($path) { + Assert::assertArrayHasKey( + $path, + $json, + "Response JSON does not have the expected '{$path}' wrapper." + ); + + $json = Arr::get($json, $path, []); + } + + foreach ($data as $key => $value) { + if (array_key_exists($key, $json)) { + Assert::assertNotEquals($value, $json[$key]); + } + } + + return $this; + } + + /** + * @param array $fragment + * @return self + */ + public function assertJsonFragment(array $fragment): self + { + $json = $this->getDecodedBody(); + + Assert::assertTrue( + $this->hasFragment($json, $fragment), + 'Unable to find JSON fragment in response.' + ); + + return $this; + } + + /** + * @param array $fragment + * @return self + */ + public function assertJsonMissingFragment(array $fragment): self + { + $json = $this->getDecodedBody(); + + Assert::assertFalse( + $this->hasFragment($json, $fragment), + 'Found unexpected JSON fragment in response.' + ); + + return $this; + } + + /** + * @param string $path + * @param mixed $expectedValue + * @return self + */ + public function assertJsonPath(string $path, mixed $expectedValue): self + { + $json = $this->getDecodedBody(); + + Assert::assertTrue( + Arr::has($json, $path), + "Path '{$path}' does not exist in JSON response." + ); + + $value = Arr::get($json, $path); + + Assert::assertEquals( + $expectedValue, + $value, + "Failed asserting that JSON path '{$path}' equals expected value." + ); + + return $this; + } + + /** + * @param string $path + * @param mixed $expectedValue + * @return self + */ + public function assertJsonPathNotEquals(string $path, mixed $expectedValue): self + { + $json = $this->getDecodedBody(); + + Assert::assertTrue( + Arr::has($json, $path), + "Path '{$path}' does not exist in JSON response." + ); + + $value = Arr::get($json, $path); + + Assert::assertNotEquals( + $expectedValue, + $value, + "Failed asserting that JSON path '{$path}' does not equal the given value." + ); + + return $this; + } + + /** + * @param array $structure + * @return self + */ + public function assertJsonStructure(array $structure): self + { + $json = $this->getDecodedBody(); + + $this->assertStructure($structure, $json); + + return $this; + } + + /** + * @param int $count + * @param string|null $path + * @return self + */ + public function assertJsonCount(int $count, string|null $path = null): self + { + $json = $this->getDecodedBody(); + + if ($path) { + Assert::assertArrayHasKey( + $path, + $json, + "Path '{$path}' does not exist in JSON response." + ); + + $json = Arr::get($json, $path); + } + + Assert::assertIsArray($json, 'Response JSON is not an array.'); + Assert::assertCount($count, $json, "Expected JSON array to have {$count} items."); + + return $this; + } + + /** + * @param array $data + * @param array $fragment + * @return bool + */ + protected function hasFragment(array $data, array $fragment): bool + { + $matches = true; + foreach ($fragment as $key => $value) { + if (! array_key_exists($key, $data) || $data[$key] !== $value) { + $matches = false; + + break; + } + } + + if ($matches) { + return true; + } + + foreach ($data as $value) { + if (is_array($value) && $this->hasFragment($value, $fragment)) { + return true; + } + } + + return false; + } + + /** + * @param array $structure + * @param array $data + * @param string $path + * @return void + */ + protected function assertStructure(array $structure, array $data, string $path = ''): void + { + foreach ($structure as $key => $value) { + $currentPath = $this->buildPath($path, $key); + + if (is_array($value)) { + $this->assertNestedStructure($key, $value, $data, $path, $currentPath); + } else { + $this->assertScalarKey($value, $data, $currentPath); + } + } + } + + /** + * @param string $path + * @param string|int $key + * @return string + */ + protected function buildPath(string $path, string|int $key): string + { + return $path ? "{$path}.{$key}" : (string) $key; + } + + /** + * @param string|int $key + * @param array $value + * @param array $data + * @param string $path + * @param string $currentPath + * @return void + */ + protected function assertNestedStructure( + string|int $key, + array $value, + array $data, + string $path, + string $currentPath + ): void { + if ($key === '*') { + $this->assertWildcardStructure($value, $data, $path); + } else { + $this->assertKeyedNestedStructure($key, $value, $data, $currentPath); + } + } + + /** + * @param array $value + * @param array $data + * @param string $path + * @return void + */ + protected function assertWildcardStructure(array $value, array $data, string $path): void + { + Assert::assertIsArray( + $data, + "Expected array at path '{$path}' but got " . gettype($data) + ); + + foreach ($data as $index => $item) { + $itemPath = $this->buildPath($path, $index); + + Assert::assertIsArray( + $item, + "Expected array at path '{$itemPath}' but got " . gettype($item) + ); + $this->assertStructure($value, $item, $itemPath); + } + } + + /** + * @param string|int $key + * @param array $value + * @param array $data + * @param string $currentPath + * @return void + */ + protected function assertKeyedNestedStructure( + string|int $key, + array $value, + array $data, + string $currentPath + ): void { + Assert::assertArrayHasKey( + $key, + $data, + "Missing key '{$key}' at path '{$currentPath}'" + ); + Assert::assertIsArray( + $data[$key], + "Expected array at path '{$currentPath}' but got " . gettype($data[$key]) + ); + $this->assertStructure($value, $data[$key], $currentPath); + } + + /** + * @param string|int $value + * @param array $data + * @param string $currentPath + * @return void + */ + protected function assertScalarKey(string|int $value, array $data, string $currentPath): void + { + Assert::assertArrayHasKey( + $value, + $data, + "Missing key '{$value}' at path '{$currentPath}'" + ); + } +} diff --git a/src/Testing/Concerns/InteractWithStatusCode.php b/src/Testing/Concerns/InteractWithStatusCode.php new file mode 100644 index 00000000..4bb4c1b5 --- /dev/null +++ b/src/Testing/Concerns/InteractWithStatusCode.php @@ -0,0 +1,53 @@ +value, $this->response->getStatus()); + + return $this; + } + + public function assertOk(): self + { + Assert::assertEquals(HttpStatus::OK->value, $this->response->getStatus()); + + return $this; + } + + public function assertCreated(): self + { + Assert::assertEquals(HttpStatus::CREATED->value, $this->response->getStatus()); + + return $this; + } + + public function assertNotFound(): self + { + Assert::assertEquals(HttpStatus::NOT_FOUND->value, $this->response->getStatus()); + + return $this; + } + + public function assertNotAcceptable(): self + { + Assert::assertEquals(HttpStatus::NOT_ACCEPTABLE->value, $this->response->getStatus()); + + return $this; + } + + public function assertUnprocessableEntity(): self + { + Assert::assertEquals(HttpStatus::UNPROCESSABLE_ENTITY->value, $this->response->getStatus()); + + return $this; + } +} diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index 1df28909..b8f2a99f 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -10,6 +10,7 @@ use Phenix\AppProxy; use Phenix\Console\Phenix; use Phenix\Facades\Event; +use Phenix\Facades\Mail; use Phenix\Facades\Queue; use Phenix\Testing\Concerns\InteractWithResponses; use Phenix\Testing\Concerns\RefreshDatabase; @@ -48,6 +49,7 @@ protected function tearDown(): void Event::resetFaking(); Queue::resetFaking(); + Mail::resetSendingLog(); $this->app = null; } diff --git a/src/Testing/TestResponse.php b/src/Testing/TestResponse.php index f3ad85c2..93dfa340 100644 --- a/src/Testing/TestResponse.php +++ b/src/Testing/TestResponse.php @@ -5,11 +5,17 @@ namespace Phenix\Testing; use Amp\Http\Client\Response; -use Phenix\Http\Constants\HttpStatus; +use Phenix\Testing\Concerns\InteractWithHeaders; +use Phenix\Testing\Concerns\InteractWithJson; +use Phenix\Testing\Concerns\InteractWithStatusCode; use PHPUnit\Framework\Assert; class TestResponse { + use InteractWithJson; + use InteractWithHeaders; + use InteractWithStatusCode; + public readonly string $body; public function __construct(public Response $response) @@ -22,44 +28,6 @@ public function getBody(): string return $this->body; } - public function getHeaders(): array - { - return $this->response->getHeaders(); - } - - public function getHeader(string $name): string|null - { - return $this->response->getHeader($name); - } - - public function assertOk(): self - { - Assert::assertEquals(HttpStatus::OK->value, $this->response->getStatus()); - - return $this; - } - - public function assertNotFound(): self - { - Assert::assertEquals(HttpStatus::NOT_FOUND->value, $this->response->getStatus()); - - return $this; - } - - public function assertNotAcceptable(): self - { - Assert::assertEquals(HttpStatus::NOT_ACCEPTABLE->value, $this->response->getStatus()); - - return $this; - } - - public function assertUnprocessableEntity(): self - { - Assert::assertEquals(HttpStatus::UNPROCESSABLE_ENTITY->value, $this->response->getStatus()); - - return $this; - } - /** * @param array|string $needles * @return self @@ -74,16 +42,4 @@ public function assertBodyContains(array|string $needles): self return $this; } - - public function assertHeaderContains(array $needles): self - { - $needles = (array) $needles; - - foreach ($needles as $header => $value) { - Assert::assertNotNull($this->response->getHeader($header)); - Assert::assertEquals($value, $this->response->getHeader($header)); - } - - return $this; - } } diff --git a/tests/Feature/RequestTest.php b/tests/Feature/RequestTest.php index fb23a471..2f39e356 100644 --- a/tests/Feature/RequestTest.php +++ b/tests/Feature/RequestTest.php @@ -8,6 +8,7 @@ use Amp\Http\Server\RequestBody; use Phenix\Facades\Route; use Phenix\Http\Constants\ContentType; +use Phenix\Http\Constants\HttpStatus; use Phenix\Http\Request; use Phenix\Http\Response; use Phenix\Testing\TestResponse; @@ -17,7 +18,7 @@ $this->app->stop(); }); -it('can send requests to server', function () { +it('can send requests to server', function (): void { Route::get('/', fn () => response()->plain('Hello')) ->middleware(AcceptJsonResponses::class); @@ -46,7 +47,7 @@ ->assertNotFound(); }); -it('can decode x-www-form-urlencode body', function () { +it('can decode x-www-form-urlencode body', function (): void { Route::post('/posts', function (Request $request) { expect($request->body()->has('title'))->toBeTruthy(); expect($request->body('title'))->toBe('Post title'); @@ -75,7 +76,7 @@ ->assertOk(); }); -it('can decode multipart form data body', function () { +it('can decode multipart form data body', function (): void { Route::post('/files', function (Request $request) { expect($request->body()->has('description'))->toBeTruthy(); expect($request->body()->has('file'))->toBeTruthy(); @@ -104,7 +105,7 @@ ->assertOk(); }); -it('responds with a view', function () { +it('responds with a view', function (): void { Route::get('/users', function (): Response { return response()->view('users.index', [ 'title' => 'New title', @@ -121,3 +122,337 @@ ->assertBodyContains('') ->assertBodyContains('User index'); }); + +it('can assert response is html', function (): void { + Route::get('/page', function (): Response { + return response()->view('users.index', [ + 'title' => 'Test Page', + ]); + }); + + $this->app->run(); + + $this->get('/page') + ->assertOk() + ->assertIsHtml() + ->assertBodyContains(''); +}); + +it('can assert response is plain text', function (): void { + Route::get('/text', function (): Response { + return response()->plain('This is plain text content'); + }); + + $this->app->run(); + + $this->get('/text') + ->assertOk() + ->assertIsPlainText() + ->assertBodyContains('plain text'); +}); + +it('can assert json contains', function (): void { + Route::get('/api/user', function (): Response { + return response()->json([ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'role' => 'admin', + ]); + }); + + $this->app->run(); + + $this->get('/api/user') + ->assertOk() + ->assertIsJson() + ->assertJsonPath('data.id', 1) + ->assertJsonPath('data.name', 'John Doe'); +}); + +it('can assert json does not contain', function (): void { + Route::get('/api/user', function (): Response { + return response()->json([ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + ]); + }); + + $this->app->run(); + + $this->get('/api/user') + ->assertOk() + ->assertJsonDoesNotContain([ + 'name' => 'Jane Doe', + 'password' => 'secret', + ]) + ->assertJsonPathNotEquals('data.name', 'Jane Doe'); +}); + +it('can assert json fragment', function (): void { + Route::get('/api/posts', function (): Response { + return response()->json([ + [ + 'id' => 1, + 'title' => 'First Post', + 'author' => [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + ], + ], + [ + 'id' => 2, + 'title' => 'Second Post', + 'author' => [ + 'name' => 'Jane Smith', + 'email' => 'jane@example.com', + ], + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/posts') + ->assertOk() + ->assertJsonFragment([ + 'name' => 'John Doe', + 'email' => 'john@example.com', + ]) + ->assertJsonFragment([ + 'id' => 2, + 'title' => 'Second Post', + ]); +}); + +it('can assert json missing fragment', function (): void { + Route::get('/api/posts', function (): Response { + return response()->json([ + [ + 'id' => 1, + 'title' => 'First Post', + 'author' => [ + 'name' => 'John Doe', + ], + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/posts') + ->assertOk() + ->assertJsonMissingFragment([ + 'name' => 'Jane Smith', + ]) + ->assertJsonMissingFragment([ + 'title' => 'Third Post', + ]); +}); + +it('can assert json path', function (): void { + Route::get('/api/profile', function (): Response { + return response()->json([ + 'user' => [ + 'profile' => [ + 'name' => 'John Doe', + 'age' => 30, + ], + 'settings' => [ + 'theme' => 'dark', + 'notifications' => true, + ], + ], + 'posts' => [ + ['id' => 1, 'title' => 'First'], + ['id' => 2, 'title' => 'Second'], + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/profile') + ->assertOk() + ->assertJsonPath('data.user.profile.name', 'John Doe') + ->assertJsonPath('data.user.profile.age', 30) + ->assertJsonPath('data.user.settings.theme', 'dark') + ->assertJsonPath('data.user.settings.notifications', true) + ->assertJsonPath('data.posts.0.title', 'First') + ->assertJsonPath('data.posts.1.id', 2); +}); + +it('can assert json path not equals', function (): void { + Route::get('/api/user', function (): Response { + return response()->json([ + 'user' => [ + 'name' => 'John Doe', + 'role' => 'admin', + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/user') + ->assertOk() + ->assertJsonPathNotEquals('data.user.name', 'Jane Doe') + ->assertJsonPathNotEquals('data.user.role', 'user'); +}); + +it('can assert json structure', function (): void { + Route::get('/api/users', function (): Response { + return response()->json([ + 'users' => [ + [ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + ], + [ + 'id' => 2, + 'name' => 'Jane Smith', + 'email' => 'jane@example.com', + ], + ], + 'meta' => [ + 'total' => 2, + 'page' => 1, + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/users') + ->assertOk() + ->assertJsonStructure([ + 'data' => [ + 'users' => [ + '*' => ['id', 'name', 'email'], + ], + 'meta' => ['total', 'page'], + ], + ]); +}); + +it('can assert json structure with nested arrays', function (): void { + Route::get('/api/posts', function (): Response { + return response()->json([ + [ + 'id' => 1, + 'title' => 'First Post', + 'author' => [ + 'name' => 'John', + 'email' => 'john@example.com', + ], + 'comments' => [ + ['id' => 1, 'body' => 'Great!'], + ['id' => 2, 'body' => 'Nice!'], + ], + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/posts') + ->assertOk() + ->assertJsonStructure([ + 'data' => [ + '*' => [ + 'id', + 'title', + 'author' => ['name', 'email'], + 'comments' => [ + '*' => ['id', 'body'], + ], + ], + ], + ]); +}); + +it('can assert json count', function (): void { + Route::get('/api/items', function (): Response { + return response()->json([ + ['id' => 1, 'name' => 'Item 1'], + ['id' => 2, 'name' => 'Item 2'], + ['id' => 3, 'name' => 'Item 3'], + ]); + }); + + $this->app->run(); + + $this->get('/api/items') + ->assertOk() + ->assertJsonPath('data.0.id', 1) + ->assertJsonPath('data.1.id', 2) + ->assertJsonPath('data.2.id', 3) + ->assertJsonCount(3, 'data'); +}); + +it('can chain multiple json assertions', function (): void { + Route::get('/api/data', function (): Response { + return response()->json([ + 'status' => 'success', + 'code' => 200, + 'user' => [ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + ], + ]); + }); + + $this->app->run(); + + $this->get('/api/data') + ->assertOk() + ->assertIsJson() + ->assertJsonFragment(['name' => 'John Doe']) + ->assertJsonPath('data.status', 'success') + ->assertJsonPath('data.code', 200) + ->assertJsonPath('data.user.id', 1) + ->assertJsonPath('data.user.email', 'john@example.com') + ->assertJsonStructure([ + 'data' => [ + 'status', + 'code', + 'user' => ['id', 'name', 'email'], + ], + ]) + ->assertJsonPathNotEquals('data.status', 'error') + ->assertJsonMissingFragment(['error' => 'Something went wrong']); +}); + +it('can assert record was created', function (): void { + Route::post('/api/users', function (): Response { + return response()->json([ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + ], HttpStatus::CREATED); + }); + + $this->app->run(); + + $this->post('/api/users', [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + ]) + ->assertCreated() + ->assertStatusCode(HttpStatus::CREATED) + ->assertJsonFragment(['name' => 'John Doe']) + ->assertJsonPath('data.id', 1) + ->assertJsonPath('data.email', 'john@example.com') + ->assertJsonContains([ + 'id' => 1, + 'name' => 'John Doe', + 'email' => 'john@example.com', + ], 'data') + ->assertJsonDoesNotContain([ + 'name' => 'Jane Doe', + 'email' => 'jane@example.com', + ], 'data'); +}); diff --git a/tests/Unit/Mail/MailTest.php b/tests/Unit/Mail/MailTest.php index 3aff9d15..c1e65b4e 100644 --- a/tests/Unit/Mail/MailTest.php +++ b/tests/Unit/Mail/MailTest.php @@ -153,6 +153,10 @@ public function build(): self Mail::expect($mailable)->toNotBeSent(function (array $matches): bool { return $matches['success'] === false; }); + + Mail::resetSendingLog(); + + expect(Mail::getSendingLog())->toBeEmpty(); }); it('send email successfully using smtps', function (): void {