Add Google login tests

This commit is contained in:
Dan Harrin
2024-11-01 10:17:01 +00:00
parent 3ef22a31f3
commit 7fffec27f1
11 changed files with 590 additions and 24 deletions
@@ -23,8 +23,7 @@ class OneTimeCodeInput extends Field
{
parent::setUp();
$this->rule('numeric');
$this->rule('integer');
$this->rule('numeric'); // Integer validation does not allow leading zeros.
$this->rule(static fn (OneTimeCodeInput $component): string => "digits:{$component->getLength()}");
}
@@ -60,11 +60,16 @@ class EmailCodeAuthentication implements HasBeforeChallengeHook, MultiFactorAuth
}
$user->notify(app($this->getCodeNotification(), [
'code' => $this->google2FA->getCurrentOtp($secret ?? $this->getSecret($user)),
'code' => $this->getCurrentCode($user, $secret),
'codeWindow' => $this->getCodeWindow(),
]));
}
public function getCurrentCode(HasEmailCodeAuthentication $user, ?string $secret = null): string
{
return $this->google2FA->getCurrentOtp($secret ?? $this->getSecret($user));
}
public function getSecret(HasEmailCodeAuthentication $user): string
{
return $user->getEmailCodeAuthenticationSecret();
@@ -97,6 +97,11 @@ class GoogleTwoFactorAuthentication implements MultiFactorAuthenticationProvider
return $this->google2FA->generateSecretKey();
}
public function getCurrentCode(HasGoogleTwoFactorAuthentication $user, ?string $secret = null): string
{
return $this->google2FA->getCurrentOtp($secret ?? $this->getSecret($user));
}
public function generateQRCodeDataUri(string $secret): string
{
/** @var HasGoogleTwoFactorAuthentication $user */
+15 -15
View File
@@ -33,7 +33,7 @@ use Livewire\Attributes\Locked;
/**
* @property-read Action $registerAction
* @property-read Schema $form
* @property-read Schema $multiFactorForm
* @property-read Schema $multiFactorChallengeForm
*/
class Login extends SimplePage
{
@@ -83,7 +83,7 @@ class Login extends SimplePage
filled($this->userUndertakingMultiFactorAuthentication) &&
(decrypt($this->userUndertakingMultiFactorAuthentication) === $user->getAuthIdentifier())
) {
$this->multiFactorForm->validate();
$this->multiFactorChallengeForm->validate();
} else {
foreach (Filament::getMultiFactorAuthenticationProviders() as $multiFactorAuthenticationProvider) {
if (! $multiFactorAuthenticationProvider->isEnabled($user)) {
@@ -98,7 +98,7 @@ class Login extends SimplePage
}
if (filled($this->userUndertakingMultiFactorAuthentication)) {
$this->multiFactorForm->fill();
$this->multiFactorChallengeForm->fill();
return null;
}
@@ -150,7 +150,7 @@ class Login extends SimplePage
return $form;
}
public function multiFactorForm(Schema $form): Schema
public function multiFactorChallengeForm(Schema $form): Schema
{
return $form;
}
@@ -170,7 +170,7 @@ class Login extends SimplePage
])
->statePath('data'),
),
'multiFactorForm' => $this->multiFactorForm(
'multiFactorChallengeForm' => $this->multiFactorChallengeForm(
$this->makeSchema()
->schema(function (): array {
if (blank($this->userUndertakingMultiFactorAuthentication)) {
@@ -262,7 +262,7 @@ class Login extends SimplePage
/**
* @return array<Action | ActionGroup>
*/
protected function getMultiFactorFormActions(): array
protected function getMultiFactorChallengeFormActions(): array
{
return [
$this->getMultiFactorAuthenticateFormAction(),
@@ -281,7 +281,7 @@ class Login extends SimplePage
return true;
}
protected function hasFullWidthMultiFactorFormActions(): bool
protected function hasFullWidthMultiFactorChallengeFormActions(): bool
{
return $this->hasFullWidthFormActions();
}
@@ -317,7 +317,7 @@ class Login extends SimplePage
->components([
RenderHook::make(PanelsRenderHook::AUTH_LOGIN_FORM_BEFORE),
$this->getFormContentComponent(),
$this->getMultiFactorFormContentComponent(),
$this->getMultiFactorChallengeFormContentComponent(),
RenderHook::make(PanelsRenderHook::AUTH_LOGIN_FORM_AFTER),
]);
}
@@ -333,18 +333,18 @@ class Login extends SimplePage
->visible(fn (): bool => blank($this->userUndertakingMultiFactorAuthentication));
}
public function getMultiFactorFormContentComponent(): Component
public function getMultiFactorChallengeFormContentComponent(): Component
{
return Form::make([NestedSchema::make('multiFactorForm')])
->id('multiFactorForm')
return Form::make([NestedSchema::make('multiFactorChallengeForm')])
->id('multiFactorChallengeForm')
->livewireSubmitHandler('authenticate')
->footer(FormActionsDecorations::make($this->getMultiFactorFormActions())
->alignment($this->getMultiFactorFormActionsAlignment())
->fullWidth($this->hasFullWidthMultiFactorFormActions()))
->footer(FormActionsDecorations::make($this->getMultiFactorChallengeFormActions())
->alignment($this->getMultiFactorChallengeFormActionsAlignment())
->fullWidth($this->hasFullWidthMultiFactorChallengeFormActions()))
->visible(fn (): bool => filled($this->userUndertakingMultiFactorAuthentication));
}
public function getMultiFactorFormActionsAlignment(): string | Alignment
public function getMultiFactorChallengeFormActionsAlignment(): string | Alignment
{
return $this->getFormActionsAlignment();
}
+21
View File
@@ -2,6 +2,8 @@
namespace Filament\Tests\Database\Factories;
use Filament\MultiFactorAuthentication\EmailCode\EmailCodeAuthentication;
use Filament\MultiFactorAuthentication\GoogleTwoFactor\GoogleTwoFactorAuthentication;
use Filament\Tests\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
@@ -20,4 +22,23 @@ class UserFactory extends Factory
'remember_token' => Str::random(10),
];
}
public function hasEmailCodeAuthentication(): self
{
$emailCodeAuthentication = EmailCodeAuthentication::make();
return $this->state(fn (): array => [
'email_code_authentication_secret' => $emailCodeAuthentication->generateSecret(),
]);
}
public function hasGoogleTwoFactorAuthentication(): self
{
$googleTwoFactorAuthentication = GoogleTwoFactorAuthentication::make();
return $this->state(fn (): array => [
'google_two_factor_authentication_secret' => $googleTwoFactorAuthentication->generateSecret(),
'google_two_factor_authentication_recovery_codes' => $googleTwoFactorAuthentication->generateRecoveryCodes(),
]);
}
}
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->after('password', function (Blueprint $table) {
$table->string('email_code_authentication_secret')->nullable();
$table->string('google_two_factor_authentication_secret')->nullable();
$table->text('google_two_factor_authentication_recovery_codes')->nullable();
});
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'email_code_authentication_secret',
'google_two_factor_authentication_secret',
'google_two_factor_authentication_recovery_codes',
]);
});
}
};
@@ -0,0 +1,45 @@
<?php
namespace Filament\Tests;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\MultiFactorAuthentication\EmailCode\EmailCodeAuthentication;
use Filament\Panel;
use Filament\PanelProvider;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class EmailCodeAuthenticationPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->id('email-code-authentication')
->path('email-code-authentication')
->login()
->multiFactorAuthentication(EmailCodeAuthentication::make())
->resources([])
->pages([])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}
@@ -0,0 +1,45 @@
<?php
namespace Filament\Tests;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\MultiFactorAuthentication\GoogleTwoFactor\GoogleTwoFactorAuthentication;
use Filament\Panel;
use Filament\PanelProvider;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class GoogleTwoFactorAuthenticationPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->id('google-two-factor-authentication')
->path('google-two-factor-authentication')
->login()
->multiFactorAuthentication(GoogleTwoFactorAuthentication::make()->recoverable())
->resources([])
->pages([])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}
+65 -2
View File
@@ -4,6 +4,8 @@ namespace Filament\Tests\Models;
use Filament\Models\Contracts\FilamentUser;
use Filament\Models\Contracts\HasTenants;
use Filament\MultiFactorAuthentication\GoogleTwoFactor\Contracts\HasGoogleTwoFactorAuthentication;
use Filament\MultiFactorAuthentication\GoogleTwoFactor\Contracts\HasGoogleTwoFactorAuthenticationRecovery;
use Filament\Panel;
use Filament\Tests\Database\Factories\UserFactory;
use Illuminate\Contracts\Auth\MustVerifyEmail;
@@ -14,7 +16,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Collection;
class User extends Authenticatable implements FilamentUser, HasTenants, MustVerifyEmail
class User extends Authenticatable implements FilamentUser, HasGoogleTwoFactorAuthentication, HasGoogleTwoFactorAuthenticationRecovery, HasTenants, MustVerifyEmail
{
use HasFactory;
use Notifiable;
@@ -24,11 +26,24 @@ class User extends Authenticatable implements FilamentUser, HasTenants, MustVeri
protected $hidden = [
'password',
'remember_token',
'google_two_factor_authentication_secret',
'google_two_factor_authentication_recovery_codes',
'email_code_authentication_secret',
];
/**
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'google_two_factor_authentication_secret' => 'encrypted',
'google_two_factor_authentication_recovery_codes' => 'encrypted:array',
'email_code_authentication_secret' => 'encrypted',
];
public function canAccessPanel(Panel $panel): bool
{
return in_array($panel->getId(), ['admin', 'slugs']);
return in_array($panel->getId(), ['admin', 'slugs', 'google-two-factor-authentication', 'email-code-authentication']);
}
public function posts(): HasMany
@@ -50,4 +65,52 @@ class User extends Authenticatable implements FilamentUser, HasTenants, MustVeri
{
return Team::all();
}
public function hasGoogleTwoFactorAuthentication(): bool
{
return filled($this->google_two_factor_authentication_secret);
}
public function getGoogleTwoFactorAuthenticationSecret(): string
{
return $this->google_two_factor_authentication_secret;
}
public function saveGoogleTwoFactorAuthenticationSecret(?string $secret): void
{
$this->google_two_factor_authentication_secret = $secret;
$this->save();
}
public function getGoogleTwoFactorAuthenticationRecoveryCodes(): array
{
return $this->google_two_factor_authentication_recovery_codes;
}
public function saveGoogleTwoFactorAuthenticationRecoveryCodes(?array $codes): void
{
$this->google_two_factor_authentication_recovery_codes = $codes;
$this->save();
}
public function getGoogleTwoFactorAuthenticationHolderName(): string
{
return $this->email;
}
public function hasEmailCodeAuthentication(): bool
{
return filled($this->email_code_authentication_secret);
}
public function getEmailCodeAuthenticationSecret(): string
{
return $this->email_code_authentication_secret;
}
public function saveEmailCodeAuthenticationSecret(?string $secret): void
{
$this->email_code_authentication_secret = $secret;
$this->save();
}
}
@@ -0,0 +1,345 @@
<?php
use Filament\Actions\Testing\Fixtures\TestAction;
use Filament\Facades\Filament;
use Filament\Forms\Components\TextInput;
use Filament\Pages\Auth\Login;
use Filament\Tests\Models\User;
use Filament\Tests\TestCase;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use function Filament\Tests\livewire;
uses(TestCase::class);
beforeEach(function () {
Filament::setCurrentPanel('google-two-factor-authentication');
});
it('can render the challenge form after valid login credentials are successfully used', function () {
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
$livewire = livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->assertSet('userUndertakingMultiFactorAuthentication', null)
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect();
expect(decrypt($livewire->instance()->userUndertakingMultiFactorAuthentication))
->toBe($userToAuthenticate->getKey());
$this->assertGuest();
});
it('will authenticate the user after a valid challenge code is used', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'code' => $googleTwoFactorAuthentication->getCurrentCode($userToAuthenticate),
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasNoErrors()
->assertRedirect(Filament::getUrl());
$this->assertAuthenticatedAs($userToAuthenticate);
});
it('will make the recovery code field visible when the user requests it', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->assertFormFieldExists(
"{$googleTwoFactorAuthentication->getId()}.recoveryCode",
'multiFactorChallengeForm',
fn (TextInput $field): bool => $field->isHidden(),
)
->callAction(TestAction::make('useRecoveryCode')
->schemaComponent("multiFactorChallengeForm.{$googleTwoFactorAuthentication->getId()}.code"))
->assertFormFieldExists(
"{$googleTwoFactorAuthentication->getId()}.recoveryCode",
'multiFactorChallengeForm',
fn (TextInput $field): bool => $field->isVisible(),
);
});
it('will authenticate the user after a valid recovery code is used', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->callAction(TestAction::make('useRecoveryCode')
->schemaComponent("multiFactorChallengeForm.{$googleTwoFactorAuthentication->getId()}.code"))
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'recoveryCode' => Arr::random($googleTwoFactorAuthentication->getRecoveryCodes($userToAuthenticate)),
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasNoErrors()
->assertRedirect(Filament::getUrl());
$this->assertAuthenticatedAs($userToAuthenticate);
});
it('will not render the challenge form after invalid login credentials are used', function () {
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'incorrect-password',
])
->assertSet('userUndertakingMultiFactorAuthentication', null)
->call('authenticate')
->assertSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect();
$this->assertGuest();
});
it('will not render the challenge form if a user does not have multi-factor authentication enabled', function () {
$userToAuthenticate = User::factory()->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->assertSet('userUndertakingMultiFactorAuthentication', null)
->call('authenticate')
->assertSet('userUndertakingMultiFactorAuthentication', null)
->assertRedirect(Filament::getUrl());
$this->assertAuthenticatedAs($userToAuthenticate);
});
it('will not authenticate the user when an invalid challenge code is used', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'code' => ($googleTwoFactorAuthentication->getCurrentCode($userToAuthenticate) === '000000')
? '111111'
: '000000',
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasFormErrors([
"{$googleTwoFactorAuthentication->getId()}.code",
], 'multiFactorChallengeForm')
->assertNoRedirect();
$this->assertGuest();
});
test('challenge codes are required', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'code' => '',
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasFormErrors([
"{$googleTwoFactorAuthentication->getId()}.code" => 'required',
], 'multiFactorChallengeForm')
->assertNoRedirect();
$this->assertGuest();
});
test('challenge codes must be numeric', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'code' => Str::random(6),
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasFormErrors([
"{$googleTwoFactorAuthentication->getId()}.code" => 'numeric',
], 'multiFactorChallengeForm')
->assertNoRedirect();
$this->assertGuest();
});
test('challenge codes must be 6 digits', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'code' => Str::limit($googleTwoFactorAuthentication->getCurrentCode($userToAuthenticate), limit: 5, end: ''),
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasFormErrors([
"{$googleTwoFactorAuthentication->getId()}.code" => 'digits',
], 'multiFactorChallengeForm')
->assertNoRedirect();
$this->assertGuest();
});
it('will not authenticate the user when an invalid recovery code is used', function () {
$googleTwoFactorAuthentication = Arr::first(filament::getCurrentPanel()->getMultiFactorAuthenticationProviders());
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->assertFormFieldExists(
"{$googleTwoFactorAuthentication->getId()}.recoveryCode",
'multiFactorChallengeForm',
fn (TextInput $field): bool => $field->isHidden(),
)
->callAction(TestAction::make('useRecoveryCode')
->schemaComponent("multiFactorChallengeForm.{$googleTwoFactorAuthentication->getId()}.code"))
->assertFormFieldExists(
"{$googleTwoFactorAuthentication->getId()}.recoveryCode",
'multiFactorChallengeForm',
fn (TextInput $field): bool => $field->isVisible(),
)
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'recoveryCode' => 'invalid-recovery-code',
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasFormErrors([
"{$googleTwoFactorAuthentication->getId()}.recoveryCode",
], 'multiFactorChallengeForm')
->assertNoRedirect();
$this->assertGuest();
});
it('will not authenticate the user with a valid recovery code if recovery is disabled', function () {
$googleTwoFactorAuthentication = Arr::first(Filament::getCurrentPanel()->getMultiFactorAuthenticationProviders())
->recoverable(false);
$userToAuthenticate = User::factory()
->hasGoogleTwoFactorAuthentication()
->create();
livewire(Login::class)
->fillForm([
'email' => $userToAuthenticate->email,
'password' => 'password',
])
->call('authenticate')
->assertNotSet('userUndertakingMultiFactorAuthentication', null)
->assertNoRedirect()
->fillForm([
$googleTwoFactorAuthentication->getId() => [
'recoveryCode' => Arr::random($googleTwoFactorAuthentication->getRecoveryCodes($userToAuthenticate)),
],
], 'multiFactorChallengeForm')
->call('authenticate')
->assertHasErrors()
->assertNoRedirect();
$this->assertGuest();
});
+6 -4
View File
@@ -47,11 +47,13 @@ abstract class TestCase extends BaseTestCase
WidgetsServiceProvider::class,
AdminPanelProvider::class,
CustomPanelProvider::class,
SlugsPanelProvider::class,
SingleDomainPanel::class,
MultiDomainPanel::class,
TenancyPanelProvider::class,
EmailCodeAuthenticationPanelProvider::class,
GoogleTwoFactorAuthenticationPanelProvider::class,
DomainTenancyPanelProvider::class,
MultiDomainPanel::class,
SingleDomainPanel::class,
SlugsPanelProvider::class,
TenancyPanelProvider::class,
];
sort($providers);