mirror of
https://github.com/pterodactyl/panel.git
synced 2024-11-22 09:02:28 +01:00
Merge branch 'feature/vuejs' into feature/vuejs-serverlist
This commit is contained in:
commit
6b2649ad2c
26
.env.dusk
Normal file
26
.env.dusk
Normal file
@ -0,0 +1,26 @@
|
||||
APP_ENV=local
|
||||
APP_DEBUG=false
|
||||
APP_KEY=NDWgIKKi9ovNK1PXZpzfNVSBdfCXGb5i
|
||||
APP_JWT_KEY=test1234
|
||||
APP_TIMEZONE=America/Los_Angeles
|
||||
APP_URL=http://pterodactyl.local
|
||||
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
|
||||
HASHIDS_SALT=IqRr0g82tCTeuyxGs8RV
|
||||
HASHIDS_LENGTH=8
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_FROM=support@pterodactyl.io
|
||||
QUEUE_DRIVER=array
|
||||
|
||||
APP_SERVICE_AUTHOR=testing@pterodactyl.io
|
||||
MAIL_FROM_NAME="Pterodactyl Panel"
|
||||
RECAPTCHA_ENABLED=false
|
||||
|
||||
DB_CONNECTION=testing
|
||||
TESTING_DB_HOST=services.pterodactyl.local
|
||||
TESTING_DB_DATABASE=panel_test
|
||||
TESTING_DB_USERNAME=panel_test
|
||||
TESTING_DB_PASSWORD=Test1234
|
@ -48,6 +48,7 @@
|
||||
"filp/whoops": "^2.1",
|
||||
"friendsofphp/php-cs-fixer": "^2.11.1",
|
||||
"fzaninotto/faker": "^1.6",
|
||||
"laravel/dusk": "^3.0",
|
||||
"martinlindhe/laravel-vue-i18n-generator": "^0.1.28",
|
||||
"mockery/mockery": "^1.0",
|
||||
"nunomaduro/collision": "^2.0",
|
||||
@ -68,6 +69,7 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Pterodactyl\\Tests\\Browser\\": "tests/Browser",
|
||||
"Pterodactyl\\Tests\\Integration\\": "tests/Integration",
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
|
123
composer.lock
generated
123
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f84af54d009a128472ca7e19a50fccf8",
|
||||
"content-hash": "069ebb3ec35c8b309b129189106ad45a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "appstract/laravel-blade-directives",
|
||||
@ -4692,6 +4692,66 @@
|
||||
],
|
||||
"time": "2017-07-22T11:58:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "facebook/webdriver",
|
||||
"version": "1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/php-webdriver.git",
|
||||
"reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e",
|
||||
"reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-zip": "*",
|
||||
"php": "^5.6 || ~7.0",
|
||||
"symfony/process": "^2.8 || ^3.1 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.0",
|
||||
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"php-mock/php-mock-phpunit": "^1.1",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"sebastian/environment": "^1.3.4 || ^2.0 || ^3.0",
|
||||
"squizlabs/php_codesniffer": "^2.6",
|
||||
"symfony/var-dumper": "^3.3 || ^4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-SimpleXML": "For Firefox profile creation"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-community": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Facebook\\WebDriver\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"description": "A PHP client for Selenium WebDriver",
|
||||
"homepage": "https://github.com/facebook/php-webdriver",
|
||||
"keywords": [
|
||||
"facebook",
|
||||
"php",
|
||||
"selenium",
|
||||
"webdriver"
|
||||
],
|
||||
"time": "2018-05-16T17:37:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.1.14",
|
||||
@ -5002,6 +5062,67 @@
|
||||
],
|
||||
"time": "2016-02-11T16:21:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/dusk",
|
||||
"version": "v3.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/dusk.git",
|
||||
"reference": "c6201427e63b869b0c1ee83d91c1d1958b71968e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/dusk/zipball/c6201427e63b869b0c1ee83d91c1d1958b71968e",
|
||||
"reference": "c6201427e63b869b0c1ee83d91c1d1958b71968e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"facebook/webdriver": "~1.0",
|
||||
"illuminate/console": "~5.6",
|
||||
"illuminate/support": "~5.6",
|
||||
"nesbot/carbon": "~1.20",
|
||||
"php": ">=7.1.0",
|
||||
"symfony/console": "~4.0",
|
||||
"symfony/process": "~4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.0",
|
||||
"phpunit/phpunit": "~7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Dusk\\DuskServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Dusk\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Dusk provides simple end-to-end testing and browser automation.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"testing",
|
||||
"webdriver"
|
||||
],
|
||||
"time": "2018-04-29T19:15:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "martinlindhe/laravel-vue-i18n-generator",
|
||||
"version": "0.1.28",
|
||||
|
@ -10,6 +10,9 @@
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Browser">
|
||||
<directory suffix="Test.php">./tests/Browser/Processes</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration">
|
||||
<directory suffix="Test.php">./tests/Integration</directory>
|
||||
</testsuite>
|
||||
|
2
resources/assets/scripts/bootstrap.js
vendored
2
resources/assets/scripts/bootstrap.js
vendored
@ -17,8 +17,8 @@ try {
|
||||
*/
|
||||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
window.axios.defaults.headers.common['Accept'] = 'application/json';
|
||||
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.token || '';
|
||||
|
||||
if (typeof phpdebugbar !== 'undefined') {
|
||||
|
@ -5,13 +5,14 @@
|
||||
>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email" ref="email" required
|
||||
<input class="input" id="grid-email" type="email" aria-labelledby="grid-email-label" required
|
||||
ref="email"
|
||||
v-bind:class="{ 'has-content': email.length > 0 }"
|
||||
v-bind:readonly="showSpinner"
|
||||
v-bind:value="email"
|
||||
v-on:input="updateEmail($event)"
|
||||
/>
|
||||
<label for="grid-email">{{ $t('strings.email') }}</label>
|
||||
<label for="grid-email" id="grid-email-label">{{ $t('strings.email') }}</label>
|
||||
<p class="text-grey-darker text-xs">{{ $t('auth.forgot_password.label_help') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -25,6 +26,7 @@
|
||||
</div>
|
||||
<div class="pt-6 text-center">
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark"
|
||||
aria-label="Go to login"
|
||||
:to="{ name: 'login' }"
|
||||
>
|
||||
{{ $t('auth.go_to_login') }}
|
||||
@ -68,6 +70,10 @@
|
||||
email: this.$props.email,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this request.');
|
||||
}
|
||||
|
||||
self.$data.submitDisabled = false;
|
||||
self.$data.showSpinner = false;
|
||||
self.success(response.data.status);
|
||||
|
@ -5,29 +5,30 @@
|
||||
>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-username" type="text" name="user" aria-labelledby="grid-username" required
|
||||
<input class="input" id="grid-username" type="text" name="user" aria-labelledby="grid-username-label" required
|
||||
ref="email"
|
||||
:class="{ 'has-content' : user.email.length > 0 }"
|
||||
:readonly="showSpinner"
|
||||
:value="user.email"
|
||||
v-on:input="updateEmail($event)"
|
||||
/>
|
||||
<label for="grid-username">{{ $t('strings.user_identifier') }}</label>
|
||||
<label id="grid-username-label" for="grid-username">{{ $t('strings.user_identifier') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap -mx-3 mb-6">
|
||||
<div class="input-open">
|
||||
<input class="input" id="grid-password" type="password" name="password" aria-labelledby="grid-password" required
|
||||
<input class="input" id="grid-password" type="password" name="password" aria-labelledby="grid-password-label" required
|
||||
ref="password"
|
||||
:class="{ 'has-content' : user.password && user.password.length > 0 }"
|
||||
:readonly="showSpinner"
|
||||
v-model="user.password"
|
||||
/>
|
||||
<label for="grid-password">{{ $t('strings.password') }}</label>
|
||||
<label id="grid-password-label" for="grid-password">{{ $t('strings.password') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-blue btn-jumbo" type="submit" v-bind:disabled="showSpinner">
|
||||
<button id="grid-login-button" class="btn btn-blue btn-jumbo" type="submit" aria-label="Log in"
|
||||
v-bind:disabled="showSpinner">
|
||||
<span class="spinner white" v-bind:class="{ hidden: ! showSpinner }"> </span>
|
||||
<span v-bind:class="{ hidden: showSpinner }">
|
||||
{{ $t('auth.sign_in') }}
|
||||
@ -35,7 +36,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="pt-6 text-center">
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark"
|
||||
<router-link class="text-xs text-grey tracking-wide no-underline uppercase hover:text-grey-dark" aria-label="Forgot password"
|
||||
:to="{ name: 'forgot-password' }">
|
||||
{{ $t('auth.forgot_password.label') }}
|
||||
</router-link>
|
||||
@ -81,6 +82,12 @@
|
||||
password: this.$props.user.password,
|
||||
})
|
||||
.then(function (response) {
|
||||
// If there is a 302 redirect or some other odd behavior (basically, response that isnt
|
||||
// in JSON format) throw an error and don't try to continue with the login.
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this request.');
|
||||
}
|
||||
|
||||
if (response.data.complete) {
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
@ -94,6 +101,7 @@
|
||||
.catch(function (err) {
|
||||
self.$props.user.password = '';
|
||||
self.$data.showSpinner = false;
|
||||
self.$refs.password.focus();
|
||||
self.$store.dispatch('logout');
|
||||
|
||||
if (!err.response) {
|
||||
@ -105,7 +113,6 @@
|
||||
response.data.errors.forEach(function (error) {
|
||||
self.error(error.detail);
|
||||
});
|
||||
self.$refs.password.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -93,6 +93,10 @@
|
||||
token: this.$props.token,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this login.');
|
||||
}
|
||||
|
||||
return window.location = response.data.redirect_to;
|
||||
})
|
||||
.catch(function (err) {
|
||||
|
@ -37,6 +37,10 @@
|
||||
};
|
||||
},
|
||||
mounted: function () {
|
||||
if ((this.$route.query.token || '').length < 1) {
|
||||
return this.$router.push({ name: 'login' });
|
||||
}
|
||||
|
||||
this.$refs.code.focus();
|
||||
},
|
||||
methods: {
|
||||
@ -49,8 +53,13 @@
|
||||
authentication_code: this.$data.code,
|
||||
})
|
||||
.then(function (response) {
|
||||
if (!(response.data instanceof Object)) {
|
||||
throw new Error('An error was encountered while processing this login.');
|
||||
}
|
||||
|
||||
localStorage.setItem('token', response.data.token);
|
||||
self.$store.dispatch('login');
|
||||
|
||||
window.location = response.data.intended;
|
||||
})
|
||||
.catch(function (err) {
|
||||
|
@ -7,7 +7,7 @@ import ResetPassword from './components/auth/ResetPassword';
|
||||
export const routes = [
|
||||
{ name: 'login', path: '/auth/login', component: Login },
|
||||
{ name: 'forgot-password', path: '/auth/password', component: Login },
|
||||
{ name: 'checkpoint', path: '/checkpoint', component: Login },
|
||||
{ name: 'checkpoint', path: '/auth/checkpoint', component: Login },
|
||||
{
|
||||
name: 'reset-password',
|
||||
path: '/auth/password/reset/:token',
|
||||
|
64
tests/Browser/BrowserTestCase.php
Normal file
64
tests/Browser/BrowserTestCase.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Tests\Browser;
|
||||
|
||||
use Laravel\Dusk\TestCase;
|
||||
use BadMethodCallException;
|
||||
use Tests\CreatesApplication;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Facebook\WebDriver\Chrome\ChromeOptions;
|
||||
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
||||
use Facebook\WebDriver\Remote\DesiredCapabilities;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
|
||||
abstract class BrowserTestCase extends TestCase
|
||||
{
|
||||
use CreatesApplication, DatabaseMigrations;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
// 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://services.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);
|
||||
}
|
||||
}
|
16
tests/Browser/Pages/BasePage.php
Normal file
16
tests/Browser/Pages/BasePage.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Tests\Browser\Pages;
|
||||
|
||||
use Laravel\Dusk\Page;
|
||||
|
||||
abstract class BasePage extends Page
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function siteElements()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
29
tests/Browser/Pages/LoginPage.php
Normal file
29
tests/Browser/Pages/LoginPage.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Tests\Browser\Pages;
|
||||
|
||||
class LoginPage extends BasePage
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
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',
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?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');
|
||||
});
|
||||
}
|
||||
}
|
88
tests/Browser/Processes/Authentication/LoginProcessTest.php
Normal file
88
tests/Browser/Processes/Authentication/LoginProcessTest.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Tests\Browser\Processes\Authentication;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
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()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->user = factory(User::class)->create([
|
||||
'email' => 'test@example.com',
|
||||
'password' => Hash::make('Password123'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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', 'test@example.com')
|
||||
->type('@password', 'Password123')
|
||||
->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', 'Password123')
|
||||
->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', 'test@example.com')
|
||||
->type('@password', 'invalid')
|
||||
->click('@loginButton')
|
||||
->waitFor('.alert.error')
|
||||
->assertSeeIn('.alert.error', trans('auth.failed'))
|
||||
->assertValue('@username', 'test@example.com')
|
||||
->assertValue('@password', '')
|
||||
->assertFocused('@password')
|
||||
->type('@password', 'Password123')
|
||||
->keys('@password', [WebDriverKeys::ENTER])
|
||||
->waitForReload()
|
||||
->assertPathIs('/')
|
||||
->assertAuthenticatedAs($this->user);
|
||||
});
|
||||
}
|
||||
}
|
41
tests/Browser/PterodactylBrowser.php
Normal file
41
tests/Browser/PterodactylBrowser.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Tests\Browser;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Illuminate\Support\Str;
|
||||
use PHPUnit\Framework\Assert as PHPUnit;
|
||||
|
||||
class PterodactylBrowser extends Browser
|
||||
{
|
||||
/**
|
||||
* 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
Normal file
2
tests/Browser/console/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
2
tests/Browser/screenshots/.gitignore
vendored
Normal file
2
tests/Browser/screenshots/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
Loading…
Reference in New Issue
Block a user