Cleanup test framework; drop all the unused browser tests
This commit is contained in:
parent
a4359064ca
commit
341ff6e178
19 changed files with 18 additions and 712 deletions
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
|
@ -63,9 +63,9 @@ jobs:
|
|||
run: php artisan test tests/Unit
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
TESTING_DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
- name: Execute Integration Tests
|
||||
run: php artisan test tests/Integration
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
TESTING_DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
DB_PORT: ${{ job.services.database.ports[3306] }}
|
||||
|
|
|
@ -55,7 +55,7 @@ class RequireTwoFactorAuthentication
|
|||
// 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 ($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);
|
||||
} elseif ($level === self::LEVEL_ADMIN && !$user->root_admin) {
|
||||
return $next($request);
|
||||
|
|
|
@ -22,9 +22,12 @@ $kernel->bootstrap();
|
|||
|
||||
$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>Environment is currently pointed at: "' . config('database.default') . '".</error>');
|
||||
$output->writeln(PHP_EOL . "<error>Environment is currently pointed at: [$database]</error>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -34,10 +37,10 @@ if (config('database.default') !== 'testing') {
|
|||
*/
|
||||
if (!env('SKIP_MIGRATIONS')) {
|
||||
$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);
|
||||
$kernel->call('db:seed', ['--database' => 'testing']);
|
||||
$kernel->call('db:seed');
|
||||
} else {
|
||||
$output->writeln(PHP_EOL . '<comment>Skipping database migrations...</comment>' . PHP_EOL);
|
||||
}
|
||||
|
|
|
@ -57,36 +57,6 @@ return [
|
|||
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),
|
||||
]) : [],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
19
phpunit.xml
19
phpunit.xml
|
@ -1,17 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="bootstrap/tests.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||
>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
|
@ -19,21 +12,17 @@
|
|||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Browser">
|
||||
<directory suffix="Test.php">./tests/Browser/Processes</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration">
|
||||
<directory suffix="Test.php">./tests/Integration</directory>
|
||||
<directory>./tests/Integration</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
<directory>./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="DB_CONNECTION" value="testing"/>
|
||||
<env name="TESTING_DB_USERNAME" value="root"/>
|
||||
<env name="DB_DATABASE" value="panel_test"/>
|
||||
<env name="CACHE_DRIVER" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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"]',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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']);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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', '');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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'));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
2
tests/Browser/console/.gitignore
vendored
2
tests/Browser/console/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
2
tests/Browser/screenshots/.gitignore
vendored
2
tests/Browser/screenshots/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -73,7 +73,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
|
|||
$this->assertFalse($user->use_totp);
|
||||
$this->assertEmpty($user->totp_secret);
|
||||
$this->assertEmpty($user->totp_authenticated_at);
|
||||
$this->assertNotEmpty($user->webauthnKeys);
|
||||
$this->assertNotEmpty($user->securityKeys);
|
||||
|
||||
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
|
||||
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);
|
||||
|
@ -148,7 +148,7 @@ class RequireTwoFactorAuthenticationTest extends MiddlewareTestCase
|
|||
$this->assertFalse($user->use_totp);
|
||||
$this->assertEmpty($user->totp_secret);
|
||||
$this->assertEmpty($user->totp_authenticated_at);
|
||||
$this->assertNotEmpty($user->webauthnKeys);
|
||||
$this->assertNotEmpty($user->securityKeys);
|
||||
|
||||
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
|
||||
$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_authenticated_at);
|
||||
$this->assertFalse($user->root_admin);
|
||||
$this->assertNotEmpty($user->webauthnKeys);
|
||||
$this->assertNotEmpty($user->securityKeys);
|
||||
|
||||
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
|
||||
$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_authenticated_at);
|
||||
$this->assertTrue($user->root_admin);
|
||||
$this->assertNotEmpty($user->webauthnKeys);
|
||||
$this->assertNotEmpty($user->securityKeys);
|
||||
|
||||
$this->request->shouldReceive('getRequestUri')->withNoArgs()->andReturn('/');
|
||||
$this->request->shouldReceive('route->getName')->withNoArgs()->andReturn(null);
|
||||
|
|
Loading…
Reference in a new issue