diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 62a5960f9..f6fb2ea62 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -16,6 +16,7 @@ class InstallCommand extends Command */ protected $signature = 'breeze:install {stack=blade : The development stack that should be installed (blade,react,vue)} {--inertia : Indicate that the Vue Inertia stack should be installed (Deprecated)} + {--pest : Indicate that Pest should be installed } {--composer=global : Absolute path to the Composer binary which should be used to install packages}'; /** @@ -76,7 +77,15 @@ public function handle() (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/App/View/Components', app_path('View/Components')); // Tests... - (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/tests/Feature', base_path('tests/Feature')); + if ($this->option('pest')) { + $this->requireComposerPackages('pestphp/pest:^1.16', 'pestphp/pest-plugin-laravel:^1.1'); + + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/pest-tests/Feature', base_path('tests/Feature')); + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/pest-tests/Unit', base_path('tests/Unit')); + (new Filesystem)->copy(__DIR__.'/../../stubs/default/pest-tests/Pest.php', base_path('tests/Pest.php')); + } else { + (new Filesystem)->copyDirectory(__DIR__.'/../../stubs/default/tests/Feature', base_path('tests/Feature')); + } // Routes... copy(__DIR__.'/../../stubs/default/routes/web.php', base_path('routes/web.php')); diff --git a/stubs/default/pest-tests/Feature/AuthenticationTest.php b/stubs/default/pest-tests/Feature/AuthenticationTest.php new file mode 100644 index 000000000..a19f60e3e --- /dev/null +++ b/stubs/default/pest-tests/Feature/AuthenticationTest.php @@ -0,0 +1,33 @@ +get('/login'); + + $response->assertStatus(200); +}); + +test('users can authenticate using the login screen', function () { + $user = User::factory()->create(); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); +}); + +test('users can not authenticate with invalid password', function () { + $user = User::factory()->create(); + + $this->post('/login', [ + 'email' => $user->email, + 'password' => 'wrong-password', + ]); + + $this->assertGuest(); +}); diff --git a/stubs/default/pest-tests/Feature/EmailVerificationTest.php b/stubs/default/pest-tests/Feature/EmailVerificationTest.php new file mode 100644 index 000000000..0e6a6d1d7 --- /dev/null +++ b/stubs/default/pest-tests/Feature/EmailVerificationTest.php @@ -0,0 +1,53 @@ +create([ + 'email_verified_at' => null, + ]); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); +}); + +test('email can be verified', function () { + $user = User::factory()->create([ + 'email_verified_at' => null, + ]); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); + $response->assertRedirect(RouteServiceProvider::HOME.'?verified=1'); +}); + +test('email is not verified with invalid hash', function () { + $user = User::factory()->create([ + 'email_verified_at' => null, + ]); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); +}); diff --git a/stubs/default/pest-tests/Feature/ExampleTest.php b/stubs/default/pest-tests/Feature/ExampleTest.php new file mode 100644 index 000000000..b46239fd0 --- /dev/null +++ b/stubs/default/pest-tests/Feature/ExampleTest.php @@ -0,0 +1,7 @@ +get('/'); + + $response->assertStatus(200); +}); diff --git a/stubs/default/pest-tests/Feature/PasswordConfirmationTest.php b/stubs/default/pest-tests/Feature/PasswordConfirmationTest.php new file mode 100644 index 000000000..8a42902e3 --- /dev/null +++ b/stubs/default/pest-tests/Feature/PasswordConfirmationTest.php @@ -0,0 +1,32 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response->assertStatus(200); +}); + +test('password can be confirmed', function () { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'password', + ]); + + $response->assertRedirect(); + $response->assertSessionHasNoErrors(); +}); + +test('password is not confirmed with invalid password', function () { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'wrong-password', + ]); + + $response->assertSessionHasErrors(); +}); diff --git a/stubs/default/pest-tests/Feature/PasswordResetTest.php b/stubs/default/pest-tests/Feature/PasswordResetTest.php new file mode 100644 index 000000000..065ea9af3 --- /dev/null +++ b/stubs/default/pest-tests/Feature/PasswordResetTest.php @@ -0,0 +1,58 @@ +get('/forgot-password'); + + $response->assertStatus(200); +}); + +test('reset password link can be requested', function () { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class); +}); + +test('reset password screen can be rendered', function () { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response->assertStatus(200); + + return true; + }); +}); + +test('password can be reset with valid token', function () { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $response = $this->post('/reset-password', [ + 'token' => $notification->token, + 'email' => $user->email, + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $response->assertSessionHasNoErrors(); + + return true; + }); +}); diff --git a/stubs/default/pest-tests/Feature/RegistrationTest.php b/stubs/default/pest-tests/Feature/RegistrationTest.php new file mode 100644 index 000000000..7b15f47fd --- /dev/null +++ b/stubs/default/pest-tests/Feature/RegistrationTest.php @@ -0,0 +1,21 @@ +get('/register'); + + $response->assertStatus(200); +}); + +test('new users can register', function () { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(RouteServiceProvider::HOME); +}); diff --git a/stubs/default/pest-tests/Pest.php b/stubs/default/pest-tests/Pest.php new file mode 100644 index 000000000..e2eb38087 --- /dev/null +++ b/stubs/default/pest-tests/Pest.php @@ -0,0 +1,48 @@ +in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ + +function something() +{ + // .. +} diff --git a/stubs/default/pest-tests/Unit/ExampleTest.php b/stubs/default/pest-tests/Unit/ExampleTest.php new file mode 100644 index 000000000..61cd84c32 --- /dev/null +++ b/stubs/default/pest-tests/Unit/ExampleTest.php @@ -0,0 +1,5 @@ +toBeTrue(); +});