Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F118311807
D976.1775750516.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
13 KB
Referenced Files
None
Subscribers
None
D976.1775750516.diff
View Options
diff --git a/src/app/Http/Controllers/API/UsersController.php b/src/app/Http/Controllers/API/UsersController.php
--- a/src/app/Http/Controllers/API/UsersController.php
+++ b/src/app/Http/Controllers/API/UsersController.php
@@ -75,18 +75,7 @@
public function info()
{
$user = $this->guard()->user();
- $response = $user->toArray();
-
- // Settings
- // TODO: It might be reasonable to limit the list of settings here to these
- // that are safe and are used in the UI
- $response['settings'] = [];
- foreach ($user->settings as $item) {
- $response['settings'][$item->key] = $item->value;
- }
-
- // Status info
- $response['statusInfo'] = self::statusInfo($user);
+ $response = $this->userResponse($user);
return response()->json($response);
}
@@ -112,7 +101,6 @@
return response()->json(['status' => 'error', 'errors' => $v->errors()], 422);
}
-
$credentials = $request->only('email', 'password');
if ($token = $this->guard()->attempt($credentials)) {
@@ -181,10 +169,12 @@
$user = User::find($id);
if (empty($user)) {
- return $this->errorResponse(404);
+ return $this->errorResponse(404);
}
- return response()->json($user);
+ $response = $this->userResponse($user);
+
+ return response()->json($response);
}
/**
@@ -345,4 +335,29 @@
return $current_user->id == $user_id;
}
+
+ /**
+ * Create a response data array for specified user.
+ *
+ * @param \App\User $user User object
+ *
+ * @return array Response data
+ */
+ protected function userResponse(User $user): array
+ {
+ $response = $user->toArray();
+
+ // Settings
+ // TODO: It might be reasonable to limit the list of settings here to these
+ // that are safe and are used in the UI
+ $response['settings'] = [];
+ foreach ($user->settings as $item) {
+ $response['settings'][$item->key] = $item->value;
+ }
+
+ // Status info
+ $response['statusInfo'] = self::statusInfo($user);
+
+ return $response;
+ }
}
diff --git a/src/resources/vue/components/User/Info.vue b/src/resources/vue/components/User/Info.vue
--- a/src/resources/vue/components/User/Info.vue
+++ b/src/resources/vue/components/User/Info.vue
@@ -4,6 +4,47 @@
<div class="card-body">
<div class="card-title">User account</div>
<div class="card-text">
+ <form @submit.prevent="submit">
+ <div class="form-group row">
+ <label for="first_name" class="col-sm-4 col-form-label">First name</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" id="first_name" v-model="user.first_name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label for="last_name" class="col-sm-4 col-form-label">Last name</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" id="last_name" v-model="user.last_name">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label for="email" class="col-sm-4 col-form-label">Email</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" id="email" disabled v-model="user.email">
+ </div>
+ </div>
+ <!--
+ <div class="form-group row">
+ <label for="alias" class="col-sm-4 col-form-label">Email aliases</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" id="alias" v-model="user.alias">
+ </div>
+ </div>
+ -->
+ <div class="form-group row">
+ <label for="password" class="col-sm-4 col-form-label">Password</label>
+ <div class="col-sm-8">
+ <input type="password" class="form-control" id="password" v-model="user.password">
+ </div>
+ </div>
+ <div class="form-group row">
+ <label for="password_confirmaton" class="col-sm-4 col-form-label">Confirm password</label>
+ <div class="col-sm-8">
+ <input type="password" class="form-control" id="password_confirmation" v-model="user.password_confirmation">
+ </div>
+ </div>
+ <button class="btn btn-primary" type="submit">Submit</button>
+ </form>
</div>
</div>
</div>
@@ -15,7 +56,7 @@
data() {
return {
user_id: null,
- user: null
+ user: {}
}
},
created() {
@@ -23,11 +64,31 @@
axios.get('/api/v4/users/' + this.user_id)
.then(response => {
this.user = response.data
+ this.user.first_name = response.data.settings.first_name
+ this.user.last_name = response.data.settings.last_name
})
.catch(this.$root.errorHandler)
} else {
this.$root.errorPage(404)
}
+ },
+ mounted() {
+ $('#first_name').focus()
+ },
+ methods: {
+ submit() {
+ this.$root.clearFormValidation($('#user-info form'))
+
+ axios.put('/api/v4/users/' + this.user_id, this.user)
+ .then(response => {
+ delete this.user.password
+ delete this.user.password_confirm
+
+ if (response.data.status == 'success') {
+ this.$toastr('success', response.data.message)
+ }
+ })
+ }
}
}
</script>
diff --git a/src/tests/Browser/Pages/UserInfo.php b/src/tests/Browser/Pages/UserInfo.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Browser/Pages/UserInfo.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Tests\Browser\Pages;
+
+use Laravel\Dusk\Browser;
+use Laravel\Dusk\Page;
+
+class UserInfo extends Page
+{
+ /**
+ * Get the URL for the page.
+ *
+ * @return string
+ */
+ public function url(): string
+ {
+ return '';
+ }
+
+ /**
+ * Assert that the browser is on the page.
+ *
+ * @param \Laravel\Dusk\Browser $browser
+ *
+ * @return void
+ */
+ public function assert(Browser $browser)
+ {
+ $browser->waitFor('@form')
+ ->assertSeeIn('#user-info .card-title', 'User account');
+ }
+
+ /**
+ * Get the element shortcuts for the page.
+ *
+ * @return array
+ */
+ public function elements(): array
+ {
+ return [
+ '@app' => '#app',
+ '@form' => '#user-info form',
+ ];
+ }
+}
diff --git a/src/tests/Browser/Pages/UserList.php b/src/tests/Browser/Pages/UserList.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Browser/Pages/UserList.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Tests\Browser\Pages;
+
+use Laravel\Dusk\Browser;
+use Laravel\Dusk\Page;
+
+class UserList extends Page
+{
+ /**
+ * Get the URL for the page.
+ *
+ * @return string
+ */
+ public function url(): string
+ {
+ return '/users';
+ }
+
+ /**
+ * Assert that the browser is on the page.
+ *
+ * @param \Laravel\Dusk\Browser $browser
+ *
+ * @return void
+ */
+ public function assert(Browser $browser)
+ {
+ $browser->assertPathIs($this->url())
+ ->waitUntilMissing('@app .app-loader')
+ ->assertSeeIn('#user-list .card-title', 'User Accounts');
+ }
+
+ /**
+ * Get the element shortcuts for the page.
+ *
+ * @return array
+ */
+ public function elements(): array
+ {
+ return [
+ '@app' => '#app',
+ '@table' => '#user-list table',
+ ];
+ }
+}
diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Browser/UsersTest.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Tests\Browser;
+
+use App\User;
+use Tests\Browser\Components\Toast;
+use Tests\Browser\Pages\Dashboard;
+use Tests\Browser\Pages\Home;
+use Tests\Browser\Pages\UserInfo;
+use Tests\Browser\Pages\UserList;
+use Tests\DuskTestCase;
+use Laravel\Dusk\Browser;
+use Illuminate\Foundation\Testing\DatabaseMigrations;
+
+class UsersTest extends DuskTestCase
+{
+ private $profile = [
+ 'first_name' => 'John',
+ 'last_name' => 'Doe',
+ ];
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ User::where('email', 'john@kolab.org')->first()->setSettings($this->profile);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ User::where('email', 'john@kolab.org')->first()->setSettings($this->profile);
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test user info page (unauthenticated)
+ */
+ public function testInfoUnauth(): void
+ {
+ // Test that the page requires authentication
+ $this->browse(function (Browser $browser) {
+ $user = User::where('email', 'john@kolab.org')->first();
+
+ $browser->visit('/user/' . $user->id)->on(new Home());
+ });
+ }
+
+ /**
+ * Test users list page (unauthenticated)
+ */
+ public function testListUnauth(): void
+ {
+ // Test that the page requires authentication
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/users')->on(new Home());
+ });
+ }
+
+ /**
+ * Test users list page
+ */
+ public function testList(): void
+ {
+ // Test that the page requires authentication
+ $this->browse(function (Browser $browser) {
+ $browser->visit(new Home())
+ ->submitLogon('john@kolab.org', 'simple123', true)
+ ->on(new Dashboard())
+ ->assertSeeIn('@links .link-users', 'User accounts')
+ ->click('@links .link-users')
+ ->on(new UserList())
+ ->whenAvailable('@table', function ($browser) {
+ $this->assertCount(1, $browser->elements('tbody tr'));
+ $browser->assertSeeIn('tbody tr td a', 'john@kolab.org');
+ });
+ });
+ }
+
+ /**
+ * Test profile page
+ *
+ * @depends testList
+ */
+ public function testInfo(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->on(new UserList())
+ ->click('@table tr:first-child a')
+ ->on(new UserInfo())
+ ->whenAvailable('@form', function (Browser $browser) {
+ // Assert form content
+ $browser->assertFocused('div.row:nth-child(1) input')
+ ->assertSeeIn('div.row:nth-child(1) label', 'First name')
+ ->assertValue('div.row:nth-child(1) input[type=text]', $this->profile['first_name'])
+ ->assertSeeIn('div.row:nth-child(2) label', 'Last name')
+ ->assertValue('div.row:nth-child(2) input[type=text]', $this->profile['last_name'])
+ ->assertSeeIn('div.row:nth-child(3) label', 'Email')
+ ->assertValue('div.row:nth-child(3) input[type=text]', 'john@kolab.org')
+// ->assertDisabled('div.row:nth-child(3) input')
+ ->assertSeeIn('div.row:nth-child(4) label', 'Password')
+ ->assertValue('div.row:nth-child(4) input[type=password]', '')
+ ->assertSeeIn('div.row:nth-child(5) label', 'Confirm password')
+ ->assertValue('div.row:nth-child(5) input[type=password]', '')
+ ->assertSeeIn('button[type=submit]', 'Submit');
+
+ // Clear some fields and submit
+ $browser->type('#first_name', '')
+ ->type('#last_name', '')
+ ->click('button[type=submit]');
+ })
+ ->with(new Toast(Toast::TYPE_SUCCESS), function (Browser $browser) {
+ $browser->assertToastTitle('')
+ ->assertToastMessage('User data updated successfully')
+ ->closeToast();
+ });
+
+ // Test error handling
+ $browser->with('@form', function (Browser $browser) {
+ $browser->type('#password', 'aaaaaa')
+ ->type('#password_confirmation', '')
+ ->click('button[type=submit]')
+ ->waitFor('#password + .invalid-feedback')
+ ->assertSeeIn('#password + .invalid-feedback', 'The password confirmation does not match.')
+ ->assertFocused('#password');
+ })
+ ->with(new Toast(Toast::TYPE_ERROR), function (Browser $browser) {
+ $browser->assertToastTitle('Error')
+ ->assertToastMessage('Form validation error')
+ ->closeToast();
+ });
+
+ // TODO: Test password change
+ });
+ }
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 9, 4:01 PM (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18852283
Default Alt Text
D976.1775750516.diff (13 KB)
Attached To
Mode
D976: Group: Additional user (Bifrost#T249344)
Attached
Detach File
Event Timeline