Cleanup test framework; drop all the unused browser tests

This commit is contained in:
Dane Everitt 2022-02-13 17:59:53 -05:00
parent a4359064ca
commit 341ff6e178
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
19 changed files with 18 additions and 712 deletions

View file

@ -63,9 +63,9 @@ jobs:
run: php artisan test tests/Unit run: php artisan test tests/Unit
if: ${{ always() }} if: ${{ always() }}
env: env:
TESTING_DB_PORT: ${{ job.services.database.ports[3306] }} DB_PORT: ${{ job.services.database.ports[3306] }}
- name: Execute Integration Tests - name: Execute Integration Tests
run: php artisan test tests/Integration run: php artisan test tests/Integration
if: ${{ always() }} if: ${{ always() }}
env: env:
TESTING_DB_PORT: ${{ job.services.database.ports[3306] }} DB_PORT: ${{ job.services.database.ports[3306] }}

View file

@ -55,7 +55,7 @@ class RequireTwoFactorAuthentication
// send them right through, nothing else needs to be checked. // send them right through, nothing else needs to be checked.
// //
// If the level is set as admin and the user is not an admin, pass them through as well. // If the level is set as admin and the user is not an admin, pass them through as well.
if ($level === self::LEVEL_NONE || ($user->use_totp || $user->webauthnKeys()->count() > 0)) { if ($level === self::LEVEL_NONE || ($user->use_totp || !empty($user->securityKeys))) {
return $next($request); return $next($request);
} elseif ($level === self::LEVEL_ADMIN && !$user->root_admin) { } elseif ($level === self::LEVEL_ADMIN && !$user->root_admin) {
return $next($request); return $next($request);

View file

@ -22,9 +22,12 @@ $kernel->bootstrap();
$output = new ConsoleOutput(); $output = new ConsoleOutput();
if (config('database.default') !== 'testing') { $connection = config('database.default');
$database = config("database.connections.$connection.database");
if ($database !== 'panel_test') {
$output->writeln(PHP_EOL . '<error>Cannot run test process against non-testing database.</error>'); $output->writeln(PHP_EOL . '<error>Cannot run test process against non-testing database.</error>');
$output->writeln(PHP_EOL . '<error>Environment is currently pointed at: "' . config('database.default') . '".</error>'); $output->writeln(PHP_EOL . "<error>Environment is currently pointed at: [$database]</error>");
exit(1); exit(1);
} }
@ -34,10 +37,10 @@ if (config('database.default') !== 'testing') {
*/ */
if (!env('SKIP_MIGRATIONS')) { if (!env('SKIP_MIGRATIONS')) {
$output->writeln(PHP_EOL . '<info>Refreshing database for Integration tests...</info>'); $output->writeln(PHP_EOL . '<info>Refreshing database for Integration tests...</info>');
$kernel->call('migrate:fresh', ['--database' => 'testing']); $kernel->call('migrate:fresh');
$output->writeln('<info>Seeding database for Integration tests...</info>' . PHP_EOL); $output->writeln('<info>Seeding database for Integration tests...</info>' . PHP_EOL);
$kernel->call('db:seed', ['--database' => 'testing']); $kernel->call('db:seed');
} else { } else {
$output->writeln(PHP_EOL . '<comment>Skipping database migrations...</comment>' . PHP_EOL); $output->writeln(PHP_EOL . '<comment>Skipping database migrations...</comment>' . PHP_EOL);
} }

View file

@ -57,36 +57,6 @@ return [
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true), PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true),
]) : [], ]) : [],
], ],
/*
| -------------------------------------------------------------------------
| Test Database Connection
| -------------------------------------------------------------------------
|
| This connection is used by the integration and HTTP tests for Pterodactyl
| development. Normal users of the Panel do not need to adjust any settings
| in here.
*/
'testing' => [
'driver' => 'mysql',
'host' => env('TESTING_DB_HOST', '127.0.0.1'),
'port' => env('TESTING_DB_PORT', '3306'),
'database' => env('TESTING_DB_DATABASE', 'panel_test'),
'username' => env('TESTING_DB_USERNAME', 'pterodactyl_test'),
'password' => env('TESTING_DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => env('TESTING_DB_STRICT_MODE', false),
'timezone' => env('TESTING_DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))),
'sslmode' => env('TESTING_DB_SSLMODE', 'prefer'),
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
PDO::MYSQL_ATTR_SSL_CERT => env('MYSQL_ATTR_SSL_CERT'),
PDO::MYSQL_ATTR_SSL_KEY => env('MYSQL_ATTR_SSL_KEY'),
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => env('MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', true),
]) : [],
],
], ],
/* /*

View file

@ -1,17 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit <phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
backupGlobals="false" xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
backupStaticAttributes="false"
bootstrap="bootstrap/tests.php" bootstrap="bootstrap/tests.php"
colors="true" colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer" printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"
processIsolation="false"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
> >
<coverage processUncoveredFiles="true"> <coverage processUncoveredFiles="true">
<include> <include>
@ -19,21 +12,17 @@
</include> </include>
</coverage> </coverage>
<testsuites> <testsuites>
<testsuite name="Browser">
<directory suffix="Test.php">./tests/Browser/Processes</directory>
</testsuite>
<testsuite name="Integration"> <testsuite name="Integration">
<directory suffix="Test.php">./tests/Integration</directory> <directory>./tests/Integration</directory>
</testsuite> </testsuite>
<testsuite name="Unit"> <testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory> <directory>./tests/Unit</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<php> <php>
<env name="APP_ENV" value="testing"/> <env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/> <env name="BCRYPT_ROUNDS" value="4"/>
<env name="DB_CONNECTION" value="testing"/> <env name="DB_DATABASE" value="panel_test"/>
<env name="TESTING_DB_USERNAME" value="root"/>
<env name="CACHE_DRIVER" value="array"/> <env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/> <env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/> <env name="QUEUE_CONNECTION" value="sync"/>

View file

@ -1,122 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser;
use Laravel\Dusk\TestCase;
use BadMethodCallException;
use Pterodactyl\Models\User;
use Pterodactyl\Console\Kernel;
use Illuminate\Support\Facades\Hash;
use Illuminate\Database\Eloquent\Model;
use Pterodactyl\Tests\CreatesApplication;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class BrowserTestCase extends TestCase
{
use CreatesApplication;
/**
* The default password to use for new accounts.
*
* @var string
*/
protected static $userPassword = 'Password123';
/**
* Create a fresh database instance before each test class is initialized. This is different
* than the default DatabaseMigrations as it is only run when the class is setup. The trait
* provided by Laravel will run on EACH test function, slowing things down significantly.
*
* If you need to reset the DB between function runs just include the trait in that specific
* test. In most cases you probably wont need to do this, or can modify the test slightly to
* avoid the need to do so.
*/
public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();
$app = require __DIR__ . '/../../bootstrap/app.php';
/** @var \Pterodactyl\Console\Kernel $kernel */
$kernel = $app->make(Kernel::class);
$kernel->bootstrap();
$kernel->call('migrate:fresh');
}
/**
* Setup tests.
*/
protected function setUp(): void
{
// Don't accidentally run the migrations aganist the non-testing database. Ask me
// how many times I've accidentally dropped my database...
if (env('DB_CONNECTION') !== 'testing') {
throw new BadMethodCallException('Cannot call browser tests using the non-testing database connection.');
}
parent::setUp();
// Gotta unset this to continue avoiding issues with the validation.
Model::unsetEventDispatcher();
}
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--disable-infobars',
]);
return RemoteWebDriver::create(
'http://host.pterodactyl.local:4444/wd/hub',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
}
/**
* Return an instance of the browser to be used for tests.
*
* @param \Facebook\WebDriver\Remote\RemoteWebDriver $driver
*
* @return \Pterodactyl\Tests\Browser\PterodactylBrowser
*/
protected function newBrowser($driver): PterodactylBrowser
{
return new PterodactylBrowser($driver);
}
/**
* Tear down the test and delete all cookies from the browser instance to address
* instances where the test would be kicked over to the login page.
*/
protected function tearDown(): void
{
/** @var \Pterodactyl\Tests\Browser\PterodactylBrowser $browser */
foreach (static::$browsers as $browser) {
$browser->driver->manage()->deleteAllCookies();
}
parent::tearDown();
}
/**
* Return a user model to authenticate aganist and use in the tests.
*/
protected function user(array $attributes = []): User
{
return User::factory()->create(array_merge([
'password' => Hash::make(static::$userPassword),
], $attributes));
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Pages;
use Laravel\Dusk\Page;
abstract class BasePage extends Page
{
/**
* @return array
*/
public static function siteElements()
{
return [
'@@success' => '.alert.success[role="alert"]',
'@@error' => '.alert.error[role="alert"]',
];
}
}

View file

@ -1,41 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Pages\Dashboard;
use Pterodactyl\Tests\Browser\Pages\BasePage;
class AccountPage extends BasePage
{
/**
* @return string
*/
public function url()
{
return '/account';
}
/**
* @return array
*/
public function elements()
{
return array_merge(parent::elements(), [
'@email' => '#update-email-container #grid-email',
'@password' => '#update-email-container #grid-password[type="password"]',
'@submit' => '#update-email-container button[type="submit"]',
'@current_password' => '#change-password-container #grid-password-current[type="password"]',
'@new_password' => '#change-password-container #grid-password-new[type="password"]',
'@confirm_password' => '#change-password-container #grid-password-new-confirm[type="password"]',
'@submit_password' => '#change-password-container button[type="submit"]',
'@2fa_button' => '#grid-open-two-factor-modal',
'@2fa_modal' => '.modal-mask #configure-two-factor',
'@2fa_token' => '#configure-two-factor #container-enable-two-factor #grid-two-factor-token[type="number"]',
'@2fa_token_disable' => '#configure-two-factor #container-disable-two-factor #grid-two-factor-token-disable',
'@2fa_enable' => '#configure-two-factor #container-enable-two-factor button[type="submit"]',
'@2fa_disable' => '#configure-two-factor #container-disable-two-factor button.btn-red[type="submit"]',
'@2fa_cancel' => '#configure-two-factor #container-disable-two-factor button.btn-secondary',
]);
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Pages;
class LoginPage extends BasePage
{
public function url(): string
{
return '/auth/login';
}
public function elements()
{
return [
'@email' => '#grid-email',
'@username' => '#grid-username',
'@password' => '#grid-password',
'@loginButton' => '#grid-login-button',
'@submitButton' => 'button.btn.btn-jumbo[type="submit"]',
'@forgotPassword' => 'a[href="/auth/password"][aria-label="Forgot password"]',
'@goToLogin' => 'a[href="/auth/login"][aria-label="Go to login"]',
'@alertSuccess' => 'div[role="alert"].success > span.message',
'@alertDanger' => 'div[role="alert"].danger > span.message',
];
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Authentication;
use Pterodactyl\Tests\Browser\BrowserTestCase;
use Pterodactyl\Tests\Browser\Pages\LoginPage;
use Pterodactyl\Tests\Browser\PterodactylBrowser;
class ForgotPasswordProcessTest extends BrowserTestCase
{
/**
* Test that the password reset page works as expected and displays the expected
* success messages to the client when submitted.
*/
public function testResetPasswordWithInvalidAccount()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->visit(new LoginPage())
->assertSee(trans('auth.forgot_password.label'))
->click('@forgotPassword')
->waitForLocation('/auth/password')
->assertFocused('@email')
->assertSeeIn('.input-open > p.text-xs', trans('auth.forgot_password.label_help'))
->assertSeeIn('@submitButton', trans('auth.forgot_password.button'))
->type('@email', 'unassociated@example.com')
->assertSeeIn('@goToLogin', trans('auth.go_to_login'))
->press('@submitButton')
->waitForLocation('/auth/login')
->assertSeeIn('div[role="alert"].success > span.message', 'We have e-mailed your password reset link!')
->assertFocused('@username')
->assertValue('@username', 'unassociated@example.com');
});
}
/**
* Test that you can type in your email address and then click forgot password and have
* the email maintained on the new page.
*/
public function testEmailCarryover()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->visit(new LoginPage())
->type('@username', 'dane@example.com')
->click('@forgotPassword')
->waitForLocation('/auth/password')
->assertFocused('@email')
->assertValue('@email', 'dane@example.com');
});
}
}

View file

@ -1,83 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Authentication;
use Facebook\WebDriver\WebDriverKeys;
use Pterodactyl\Tests\Browser\BrowserTestCase;
use Pterodactyl\Tests\Browser\Pages\LoginPage;
use Pterodactyl\Tests\Browser\PterodactylBrowser;
class LoginProcessTest extends BrowserTestCase
{
private $user;
/**
* Setup tests.
*/
protected function setUp(): void
{
parent::setUp();
$this->user = $this->user();
}
/**
* Test that a user can login successfully using their email address.
*/
public function testLoginUsingEmail()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->visit(new LoginPage())
->waitFor('@username')
->type('@username', $this->user->email)
->type('@password', self::$userPassword)
->click('@loginButton')
->waitForReload()
->assertPathIs('/')
->assertAuthenticatedAs($this->user);
});
}
/**
* Test that a user can login successfully using their username.
*/
public function testLoginUsingUsername()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->visit(new LoginPage())
->waitFor('@username')
->type('@username', $this->user->username)
->type('@password', self::$userPassword)
->click('@loginButton')
->waitForReload()
->assertPathIs('/')
->assertAuthenticatedAs($this->user);
});
}
/**
* Test that entering the wrong password shows the expected error and then allows
* us to login without clearing the username field.
*/
public function testLoginWithErrors()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->logout()
->visit(new LoginPage())
->waitFor('@username')
->type('@username', $this->user->email)
->type('@password', 'invalid')
->click('@loginButton')
->waitFor('.alert.error')
->assertSeeIn('.alert.error', trans('auth.failed'))
->assertValue('@username', $this->user->email)
->assertValue('@password', '')
->assertFocused('@password')
->type('@password', self::$userPassword)
->keys('@password', [WebDriverKeys::ENTER])
->waitForReload()
->assertPathIs('/')
->assertAuthenticatedAs($this->user);
});
}
}

View file

@ -1,67 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
use Pterodactyl\Tests\Browser\PterodactylBrowser;
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
class AccountEmailProcessTest extends DashboardTestCase
{
/**
* Test that an email address can be changed successfully.
*/
public function testEmailCanBeChanged()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->assertValue('@email', $this->user->email)
->type('@email', 'new.email@example.com')
->type('@password', 'Password123')
->click('@submit')
->waitFor('@@success')
->assertSeeIn('@@success', trans('dashboard/account.email.updated'))
->assertValue('@email', 'new.email@example.com');
$this->assertDatabaseHas('users', ['id' => $this->user->id, 'email' => 'new.email@example.com']);
});
}
/**
* Test that the validation error message shows up when an invalid email is entered.
*/
public function testInvalidEmailShowsErrors()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->assertMissing('@email ~ .input-help.error')
->type('@email', 'admin')
->assertVisible('@email ~ .input-help.error')
->assertSeeIn('@email ~ .input-help.error', 'The email field must be a valid email.')
->type('@email', 'admin@example.com')
->assertMissing('@email ~ .input-help.error');
});
}
/**
* Test that entering the wrong password for an account returns an error.
*/
public function testInvalidPasswordShowsError()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->type('@email', 'new.email@example.com')
->click('@submit')
->assertFocused('@password')
->type('@password', 'test1234')
->click('@submit')
->waitFor('@@error')
->assertSeeIn('@@error', trans('validation.internal.invalid_password'))
->assertValue('@email', 'new.email@example.com');
$this->assertDatabaseMissing('users', ['id' => $this->user->id, 'email' => 'new.email@example.com']);
});
}
}

View file

@ -1,55 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
use Pterodactyl\Tests\Browser\PterodactylBrowser;
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
class AccountPasswordProcessTest extends DashboardTestCase
{
/**
* Test that a user is able to change their password.
*/
public function testPasswordCanBeChanged()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->type('@current_password', self::$userPassword)
->assertMissing('@new_password ~ .input-help.error')
->type('@new_password', 'test')
->assertSeeIn('@new_password ~ .input-help.error', 'The password field must be at least 8 characters.')
->type('@new_password', 'Test1234')
->assertMissing('@new_password ~ .input-help.error')
->assertMissing('@confirm_password ~ .input-help.error')
->type('@confirm_password', 'test')
->assertSeeIn('@confirm_password ~ .input-help.error', 'The password value is not valid.')
->type('@confirm_password', 'Test1234')
->assertMissing('@confirm_password ~ .input-help.error')
->click('@submit_password')
->waitFor('@@success')
->assertSeeIn('@@success', 'Your password has been updated.')
->assertInputValue('@current_password', '')
->assertInputValue('@new_password', '')
->assertInputValue('@confirm_password', '');
});
}
/**
* Test that invalid passwords result in the expected error message.
*/
public function testInvalidPassword()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->type('@current_password', 'badpassword')
->type('@new_password', 'testtest')
->type('@confirm_password', 'testtest')
->click('@submit_password')
->waitFor('@@error')
->assertSeeIn('@@error', trans('validation.internal.invalid_password'))
->assertInputValue('@current_password', '');
});
}
}

View file

@ -1,23 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
use Pterodactyl\Tests\Browser\BrowserTestCase;
abstract class DashboardTestCase extends BrowserTestCase
{
/**
* @var \Pterodactyl\Models\User
*/
protected $user;
/**
* Setup tests and provide a default user to calling functions.
*/
protected function setUp(): void
{
parent::setUp();
$this->user = $this->user();
}
}

View file

@ -1,111 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
use PragmaRX\Google2FA\Google2FA;
use Facebook\WebDriver\WebDriverKeys;
use Illuminate\Support\Facades\Crypt;
use Pterodactyl\Tests\Browser\PterodactylBrowser;
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
class TwoFactorAuthenticationProcessTest extends DashboardTestCase
{
/**
* Test that the modal can be opened and closed.
*/
public function testModalOpenAndClose()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->assertMissing('.modal-mask')
->click('@2fa_button')
->waitFor('@2fa_modal')
->pause(500)// seems to fix fragile test
->clickPosition(100, 100)
->waitUntilMissing('@2fa_modal')
->click('@2fa_button')
->waitFor('@2fa_modal')
->click('svg[role="button"][aria-label="Close modal"]')
->waitUntilMissing('@2fa_modal')
->click('@2fa_button')
->waitFor('@2fa_modal')
->keys('', [WebDriverKeys::ESCAPE])
->waitUntilMissing('@2fa_modal');
});
}
/**
* Test that a user that does not have two-factor enabled can enable it on their account.
*/
public function testTwoFactorCanBeEnabled()
{
$this->browse(function (PterodactylBrowser $browser) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->click('@2fa_button')
->waitForText(trans('dashboard/account.two_factor.setup.title'))
->assertFocused('@2fa_token')
->waitFor('#grid-qr-code')
->assertSee(trans('dashboard/account.two_factor.setup.help'));
// Grab information from the database so we can ensure the correct things are showing up.
// Also because we need to generate a code to send through and activate it with.
$updated = $this->user->fresh();
$secret = Crypt::decrypt($updated->totp_secret);
$code = (new Google2FA())->getCurrentOtp($secret);
$browser->assertSeeIn('code', $secret)
->assertVisible('@2fa_enable[disabled="disabled"]')
->assertMissing('@2fa_token ~ .input-help.error')
->type('@2fa_token', '12')
->assertSeeIn('@2fa_token ~ .input-help.error', 'The token length must be 6.')
->type('@2fa_token', $code)
->assertMissing('@2fa_token ~ .input-help.error')
->click('@2fa_enable')
->waitUntilMissing('@2fa_modal')
->assertSeeIn('@@success', trans('dashboard/account.two_factor.enabled'));
$this->assertDatabaseHas('users', ['id' => $this->user->id, 'use_totp' => 1]);
});
}
/**
* Test that a user can disable two-factor authentication on thier account.
*/
public function testTwoFactorCanBeDisabled()
{
$secret = (new Google2FA())->generateSecretKey(16);
$this->user->update([
'use_totp' => true,
'totp_secret' => Crypt::encrypt($secret),
]);
$this->browse(function (PterodactylBrowser $browser) use ($secret) {
$browser->loginAs($this->user)
->visit(new AccountPage())
->click('@2fa_button')
->waitForText(trans('dashboard/account.two_factor.disable.title'))
->click('@2fa_cancel')
->waitUntilMissing('@2fa_modal')
->click('@2fa_button')
->waitForText(trans('dashboard/account.two_factor.disable.title'))
->assertVisible('@2fa_disable[disabled="disabled"]')
->assertVisible('@2fa_cancel')
->assertFocused('@2fa_token_disable')
->assertMissing('@2fa_token_disable ~ .input-help.error')
->type('@2fa_token_disable', '12')
->assertSeeIn('@2fa_token_disable ~ .input-help.error', 'The token length must be 6.');
$token = (new Google2FA())->getCurrentOtp($secret);
$browser->type('@2fa_token_disable', $token)
->assertMissing('@2fa_token_disable ~ .input-help.error')
->click('@2fa_disable')
->waitUntilMissing('@2fa_modal')
->assertSeeIn('@@success', trans('dashboard/account.two_factor.disabled'));
});
}
}

View file

@ -1,55 +0,0 @@
<?php
namespace Pterodactyl\Tests\Browser;
use Laravel\Dusk\Browser;
use Illuminate\Support\Str;
use PHPUnit\Framework\Assert as PHPUnit;
class PterodactylBrowser extends Browser
{
/**
* Move the mouse to a specific location and then perform a left click action.
*
* @return $this
*/
public function clickPosition(int $x, int $y)
{
$this->driver->getMouse()->mouseMove(null, $x, $y)->click();
return $this;
}
/**
* Perform a case insensitive search for a string in the body.
*
* @param string $text
*
* @return \Pterodactyl\Tests\Browser\PterodactylBrowser
*/
public function assertSee($text)
{
return $this->assertSeeIn('', $text);
}
/**
* Perform a case insensitive search for a string in a given selector.
*
* @param string $selector
* @param string $text
*
* @return \Pterodactyl\Tests\Browser\PterodactylBrowser
*/
public function assertSeeIn($selector, $text)
{
$fullSelector = $this->resolver->format($selector);
$element = $this->resolver->findOrFail($selector);
PHPUnit::assertTrue(
Str::contains(mb_strtolower($element->getText()), mb_strtolower($text)),
"Did not see expected text [{$text}] within element [{$fullSelector}] using case-insensitive search."
);
return $this;
}
}

View file

@ -1,2 +0,0 @@
*
!.gitignore

View file

@ -1,2 +0,0 @@
*
!.gitignore

View file

@ -73,7 +73,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
$this->assertFalse($user->use_totp); $this->assertFalse($user->use_totp);
$this->assertEmpty($user->totp_secret); $this->assertEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at); $this->assertEmpty($user->totp_authenticated_at);
$this->assertNotEmpty($user->webauthnKeys); $this->assertNotEmpty($user->securityKeys);
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/'); $this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null); $this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);
@ -148,7 +148,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
$this->assertFalse($user->use_totp); $this->assertFalse($user->use_totp);
$this->assertEmpty($user->totp_secret); $this->assertEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at); $this->assertEmpty($user->totp_authenticated_at);
$this->assertNotEmpty($user->webauthnKeys); $this->assertNotEmpty($user->securityKeys);
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/'); $this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null); $this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);
@ -262,7 +262,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
$this->assertEmpty($user->totp_secret); $this->assertEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at); $this->assertEmpty($user->totp_authenticated_at);
$this->assertFalse($user->root_admin); $this->assertFalse($user->root_admin);
$this->assertNotEmpty($user->webauthnKeys); $this->assertNotEmpty($user->securityKeys);
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/'); $this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null); $this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);
@ -286,7 +286,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
$this->assertEmpty($user->totp_secret); $this->assertEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at); $this->assertEmpty($user->totp_authenticated_at);
$this->assertTrue($user->root_admin); $this->assertTrue($user->root_admin);
$this->assertNotEmpty($user->webauthnKeys); $this->assertNotEmpty($user->securityKeys);
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/'); $this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null); $this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);