Skip to content

Commit d9a09e9

Browse files
committed
Add tests for all of the account actions
1 parent be2c76c commit d9a09e9

File tree

9 files changed

+243
-24
lines changed

9 files changed

+243
-24
lines changed

resources/assets/scripts/components/core/Modal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<transition name="modal">
33
<div class="modal-mask" v-show="show" v-on:click="close">
44
<div class="modal-container" @click.stop>
5-
<x-icon class="absolute pin-r pin-t m-2 text-grey cursor-pointer" aria-label="Close modal"
5+
<x-icon class="absolute pin-r pin-t m-2 text-grey cursor-pointer" aria-label="Close modal" role="button"
66
v-on:click="close"
77
/>
88
<slot/>

resources/assets/scripts/components/dashboard/Account.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
<div class="sm:m-4 md:ml-0">
1212
<update-email class="mb-4 sm:mb-8"/>
1313
<div class="content-box text-center mb-4 sm:mb-0">
14-
<button class="btn btn-green btn-sm" type="submit" v-on:click="openModal">Configure 2-Factor Authentication</button>
14+
<button class="btn btn-green btn-sm" type="submit" id="grid-open-two-factor-modal"
15+
v-on:click="openModal"
16+
>Configure 2-Factor Authentication</button>
1517
</div>
1618
</div>
1719
</div>

resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<template>
2-
<div>
2+
<div id="configure-two-factor">
33
<div class="h-16 text-center" v-show="spinner">
44
<span class="spinner spinner-xl text-blue"></span>
55
</div>
6-
<div v-if="response.enabled" v-show="!spinner">
6+
<div id="container-disable-two-factor" v-if="response.enabled" v-show="!spinner">
77
<h2 class="font-medium text-grey-darkest">{{ $t('dashboard.account.two_factor.disable.title') }}</h2>
88
<div class="mt-6">
99
<label class="input-label" for="grid-two-factor-token-disable">{{ $t('dashboard.account.two_factor.disable.field') }}</label>
@@ -26,12 +26,12 @@
2626
>{{ $t('strings.disable') }}</button>
2727
</div>
2828
</div>
29-
<div v-else v-show="!spinner">
29+
<div id="container-enable-two-factor" v-else v-show="!spinner">
3030
<h2 class="font-medium text-grey-darkest">{{ $t('dashboard.account.two_factor.setup.title') }}</h2>
3131
<div class="flex mt-6">
3232
<div class="flex-none w-full sm:w-1/2 text-center">
3333
<div class="h-48">
34-
<img :src="response.qr_image" alt="Two-factor qr image" class="h-48">
34+
<img :src="response.qr_image" id="grid-qr-code" alt="Two-factor qr image" class="h-48">
3535
</div>
3636
<div>
3737
<p class="text-xs text-grey-darker mb-2">{{ $t('dashboard.account.two_factor.setup.help') }}</p>

tests/Browser/Pages/Dashboard/AccountPage.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@ public function elements()
2121
{
2222
return array_merge(parent::elements(), [
2323
'@email' => '#update-email-container #grid-email',
24-
'@password' => '#update-email-container #grid-password',
24+
'@password' => '#update-email-container #grid-password[type="password"]',
2525
'@submit' => '#update-email-container button[type="submit"]',
26+
27+
'@current_password' => '#change-password-container #grid-password-current[type="password"]',
28+
'@new_password' => '#change-password-container #grid-password-new[type="password"]',
29+
'@confirm_password' => '#change-password-container #grid-password-new-confirm[type="password"]',
30+
'@submit_password' => '#change-password-container button[type="submit"]',
31+
32+
'@2fa_button' => '#grid-open-two-factor-modal',
33+
'@2fa_modal' => '.modal-mask #configure-two-factor',
34+
'@2fa_token' => '#configure-two-factor #container-enable-two-factor #grid-two-factor-token[type="number"]',
35+
'@2fa_token_disable' => '#configure-two-factor #container-disable-two-factor #grid-two-factor-token-disable',
36+
'@2fa_enable' => '#configure-two-factor #container-enable-two-factor button[type="submit"]',
37+
'@2fa_disable' => '#configure-two-factor #container-disable-two-factor button.btn-red[type="submit"]',
38+
'@2fa_cancel' => '#configure-two-factor #container-disable-two-factor button.btn-secondary',
2639
]);
2740
}
2841
}

tests/Browser/Processes/Dashboard/AccountEmailProcessTest.php

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,11 @@
22

33
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
44

5-
use Pterodactyl\Tests\Browser\BrowserTestCase;
65
use Pterodactyl\Tests\Browser\PterodactylBrowser;
76
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
87

9-
class AccountEmailProcessTest extends BrowserTestCase
8+
class AccountEmailProcessTest extends DashboardTestCase
109
{
11-
/**
12-
* @var \Pterodactyl\Models\User
13-
*/
14-
private $user;
15-
16-
/**
17-
* Setup tests.
18-
*/
19-
protected function setUp()
20-
{
21-
parent::setUp();
22-
23-
$this->user = $this->user();
24-
}
25-
2610
/**
2711
* Test that an email address can be changed successfully.
2812
*/
@@ -43,6 +27,23 @@ public function testEmailCanBeChanged()
4327
});
4428
}
4529

30+
/**
31+
* Test that the validation error message shows up when an invalid email is entered.
32+
*/
33+
public function testInvalidEmailShowsErrors()
34+
{
35+
$this->browse(function (PterodactylBrowser $browser) {
36+
$browser->loginAs($this->user)
37+
->visit(new AccountPage)
38+
->assertMissing('@email ~ .input-help.error')
39+
->type('@email', 'admin')
40+
->assertVisible('@email ~ .input-help.error')
41+
->assertSeeIn('@email ~ .input-help.error', 'The email field must be a valid email.')
42+
->type('@email', 'admin@example.com')
43+
->assertMissing('@email ~ .input-help.error');
44+
});
45+
}
46+
4647
/**
4748
* Test that entering the wrong password for an account returns an error.
4849
*/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
4+
5+
use Pterodactyl\Tests\Browser\PterodactylBrowser;
6+
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
7+
8+
class AccountPasswordProcessTest extends DashboardTestCase
9+
{
10+
/**
11+
* Test that a user is able to change their password.
12+
*/
13+
public function testPasswordCanBeChanged()
14+
{
15+
$this->browse(function (PterodactylBrowser $browser) {
16+
$browser->loginAs($this->user)
17+
->visit(new AccountPage)
18+
->type('@current_password', self::$userPassword)
19+
->assertMissing('@new_password ~ .input-help.error')
20+
->type('@new_password', 'test')
21+
->assertSeeIn('@new_password ~ .input-help.error', 'The password field must be at least 8 characters.')
22+
->type('@new_password', 'Test1234')
23+
->assertMissing('@new_password ~ .input-help.error')
24+
->assertMissing('@confirm_password ~ .input-help.error')
25+
->type('@confirm_password', 'test')
26+
->assertSeeIn('@confirm_password ~ .input-help.error', 'The password value is not valid.')
27+
->type('@confirm_password', 'Test1234')
28+
->assertMissing('@confirm_password ~ .input-help.error')
29+
->click('@submit_password')
30+
->waitFor('@@success')
31+
->assertSeeIn('@@success', 'Your password has been updated.')
32+
->assertInputValue('@current_password', '')
33+
->assertInputValue('@new_password', '')
34+
->assertInputValue('@confirm_password', '');
35+
});
36+
}
37+
38+
/**
39+
* Test that invalid passwords result in the expected error message.
40+
*/
41+
public function testInvalidPassword()
42+
{
43+
$this->browse(function (PterodactylBrowser $browser) {
44+
$browser->loginAs($this->user)
45+
->visit(new AccountPage)
46+
->type('@current_password', 'badpassword')
47+
->type('@new_password', 'testtest')
48+
->type('@confirm_password', 'testtest')
49+
->click('@submit_password')
50+
->waitFor('@@error')
51+
->assertSeeIn('@@error', trans('validation.internal.invalid_password'))
52+
->assertInputValue('@current_password', '');
53+
});
54+
}
55+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
4+
5+
use Pterodactyl\Tests\Browser\BrowserTestCase;
6+
7+
abstract class DashboardTestCase extends BrowserTestCase
8+
{
9+
/**
10+
* @var \Pterodactyl\Models\User
11+
*/
12+
protected $user;
13+
14+
/**
15+
* Setup tests and provide a default user to calling functions.
16+
*/
17+
protected function setUp()
18+
{
19+
parent::setUp();
20+
21+
$this->user = $this->user();
22+
}
23+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Browser\Processes\Dashboard;
4+
5+
use PragmaRX\Google2FA\Google2FA;
6+
use Facebook\WebDriver\WebDriverKeys;
7+
use Illuminate\Support\Facades\Crypt;
8+
use Pterodactyl\Tests\Browser\PterodactylBrowser;
9+
use Pterodactyl\Tests\Browser\Pages\Dashboard\AccountPage;
10+
11+
class TwoFactorAuthenticationProcessTest extends DashboardTestCase
12+
{
13+
/**
14+
* Test that the modal can be opened and closed.
15+
*/
16+
public function testModalOpenAndClose()
17+
{
18+
$this->browse(function (PterodactylBrowser $browser) {
19+
$browser->loginAs($this->user)
20+
->visit(new AccountPage)
21+
->assertMissing('.modal-mask')
22+
->click('@2fa_button')
23+
->waitFor('@2fa_modal')
24+
->pause(500)// seems to fix fragile test
25+
->clickPosition(100, 100)
26+
->waitUntilMissing('@2fa_modal')
27+
->click('@2fa_button')
28+
->waitFor('@2fa_modal')
29+
->click('svg[role="button"][aria-label="Close modal"]')
30+
->waitUntilMissing('@2fa_modal')
31+
->click('@2fa_button')
32+
->waitFor('@2fa_modal')
33+
->keys('', [WebDriverKeys::ESCAPE])
34+
->waitUntilMissing('@2fa_modal');
35+
});
36+
}
37+
38+
/**
39+
* Test that a user that does not have two-factor enabled can enable it on their account.
40+
*/
41+
public function testTwoFactorCanBeEnabled()
42+
{
43+
$this->browse(function (PterodactylBrowser $browser) {
44+
$browser->loginAs($this->user)
45+
->visit(new AccountPage)
46+
->click('@2fa_button')
47+
->waitForText(trans('dashboard/account.two_factor.setup.title'))
48+
->assertFocused('@2fa_token')
49+
->waitFor('#grid-qr-code')
50+
->assertSee(trans('dashboard/account.two_factor.setup.help'));
51+
52+
// Grab information from the database so we can ensure the correct things are showing up.
53+
// Also because we need to generate a code to send through and activate it with.
54+
$updated = $this->user->fresh();
55+
56+
$secret = Crypt::decrypt($updated->totp_secret);
57+
$code = (new Google2FA())->getCurrentOtp($secret);
58+
59+
$browser->assertSeeIn('code', $secret)
60+
->assertVisible('@2fa_enable[disabled="disabled"]')
61+
->assertMissing('@2fa_token ~ .input-help.error')
62+
->type('@2fa_token', '12')
63+
->assertSeeIn('@2fa_token ~ .input-help.error', 'The token length must be 6.')
64+
->type('@2fa_token', $code)
65+
->assertMissing('@2fa_token ~ .input-help.error')
66+
->click('@2fa_enable')
67+
->waitUntilMissing('@2fa_modal')
68+
->assertSeeIn('@@success', trans('dashboard/account.two_factor.enabled'));
69+
70+
$this->assertDatabaseHas('users', ['id' => $this->user->id, 'use_totp' => 1]);
71+
});
72+
}
73+
74+
/**
75+
* Test that a user can disable two-factor authentication on thier account.
76+
*/
77+
public function testTwoFactorCanBeDisabled()
78+
{
79+
$secret = (new Google2FA)->generateSecretKey(16);
80+
81+
$this->user->update([
82+
'use_totp' => true,
83+
'totp_secret' => Crypt::encrypt($secret),
84+
]);
85+
86+
$this->browse(function (PterodactylBrowser $browser) use ($secret) {
87+
$browser->loginAs($this->user)
88+
->visit(new AccountPage)
89+
->click('@2fa_button')
90+
->waitForText(trans('dashboard/account.two_factor.disable.title'))
91+
->click('@2fa_cancel')
92+
->waitUntilMissing('@2fa_modal')
93+
->click('@2fa_button')
94+
->waitForText(trans('dashboard/account.two_factor.disable.title'))
95+
->assertVisible('@2fa_disable[disabled="disabled"]')
96+
->assertVisible('@2fa_cancel')
97+
->assertFocused('@2fa_token_disable')
98+
->assertMissing('@2fa_token_disable ~ .input-help.error')
99+
->type('@2fa_token_disable', '12')
100+
->assertSeeIn('@2fa_token_disable ~ .input-help.error', 'The token length must be 6.');
101+
102+
$token = (new Google2FA())->getCurrentOtp($secret);
103+
104+
$browser->type('@2fa_token_disable', $token)
105+
->assertMissing('@2fa_token_disable ~ .input-help.error')
106+
->click('@2fa_disable')
107+
->waitUntilMissing('@2fa_modal')
108+
->assertSeeIn('@@success', trans('dashboard/account.two_factor.disabled'));
109+
});
110+
}
111+
}

tests/Browser/PterodactylBrowser.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@
88

99
class PterodactylBrowser extends Browser
1010
{
11+
/**
12+
* Move the mouse to a specific location and then perform a left click action.
13+
*
14+
* @param int $x
15+
* @param int $y
16+
* @return $this
17+
*/
18+
public function clickPosition(int $x, int $y)
19+
{
20+
$this->driver->getMouse()->mouseMove(null, $x, $y)->click();
21+
22+
return $this;
23+
}
24+
1125
/**
1226
* Perform a case insensitive search for a string in the body.
1327
*

0 commit comments

Comments
 (0)