Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117870913
D1123.1775328006.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
29 KB
Referenced Files
None
Subscribers
None
D1123.1775328006.diff
View Options
diff --git a/src/.gitignore b/src/.gitignore
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -3,10 +3,11 @@
package-lock.json
public/css/app.css
public/hot
-public/js/app.js
+public/js/*.js
public/storage/
storage/*.key
storage/export/
+tests/report/
vendor
.env
.env.backup
diff --git a/src/app/Auth/SecondFactor.php b/src/app/Auth/SecondFactor.php
--- a/src/app/Auth/SecondFactor.php
+++ b/src/app/Auth/SecondFactor.php
@@ -227,8 +227,6 @@
*/
public function read($key)
{
- \Log::debug(__METHOD__ . ' ' . $key);
-
if (!isset($this->cache[$key])) {
$factors = $this->getFactors();
$this->cache[$key] = isset($factors[$key]) ? $factors[$key] : null;
diff --git a/src/app/Backends/LDAP.php b/src/app/Backends/LDAP.php
--- a/src/app/Backends/LDAP.php
+++ b/src/app/Backends/LDAP.php
@@ -430,6 +430,10 @@
if (!in_array("groupware", $roles)) {
$entry['nsroledn'][] = "cn=imap-user,{$hostedRootDN}";
}
+
+ if (empty($entry['nsroledn'])) {
+ unset($entry['nsroledn']);
+ }
}
/**
diff --git a/src/app/Domain.php b/src/app/Domain.php
--- a/src/app/Domain.php
+++ b/src/app/Domain.php
@@ -356,16 +356,9 @@
throw new \Exception("Failed to get DNS record for {$this->namespace}");
}
- // It may happen that result contains other domains depending on the host
- // DNS setup
- $hosts = array_map(
- function ($record) {
- return $record['host'];
- },
- $records
- );
-
- if (in_array($this->namespace, $hosts)) {
+ // It may happen that result contains other domains depending on the host DNS setup
+ // that's why in_array() and not just !empty()
+ if (in_array($this->namespace, array_column($records, 'host'))) {
$this->status |= Domain::STATUS_VERIFIED;
$this->save();
@@ -380,8 +373,11 @@
*
* @return \App\Wallet A wallet object
*/
- public function wallet(): Wallet
+ public function wallet(): ?Wallet
{
- return $this->entitlement()->first()->wallet;
+ // Note: Not all domains have a entitlement/wallet
+ $entitlement = $this->entitlement()->first();
+
+ return $entitlement ? $entitlement->wallet : null;
}
}
diff --git a/src/app/Handlers/Activesync.php b/src/app/Handlers/Activesync.php
--- a/src/app/Handlers/Activesync.php
+++ b/src/app/Handlers/Activesync.php
@@ -18,4 +18,9 @@
return true;
}
+
+ public static function priority(): int
+ {
+ return 70;
+ }
}
diff --git a/src/app/Handlers/Auth2F.php b/src/app/Handlers/Auth2F.php
--- a/src/app/Handlers/Auth2F.php
+++ b/src/app/Handlers/Auth2F.php
@@ -18,4 +18,9 @@
return true;
}
+
+ public static function priority(): int
+ {
+ return 60;
+ }
}
diff --git a/src/app/Http/Controllers/API/V4/Admin/UsersController.php b/src/app/Http/Controllers/API/V4/Admin/UsersController.php
--- a/src/app/Http/Controllers/API/V4/Admin/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/Admin/UsersController.php
@@ -2,16 +2,62 @@
namespace App\Http\Controllers\API\V4\Admin;
+use App\Domain;
+use App\User;
+use App\UserSetting;
+
class UsersController extends \App\Http\Controllers\API\V4\UsersController
{
+ /**
+ * Searching of user accounts.
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
public function index()
{
- $result = \App\User::orderBy('email')->get()->map(function ($user) {
+ $search = trim(request()->input('search'));
+ $result = collect([]);
+
+ if (strpos($search, '@')) {
+ // Search by email
+ if ($user = User::findByEmail($search, false)) {
+ $result->push($user);
+ } else {
+ // Search by an external email
+ // TODO: This is not optimal (external email should be in users table)
+ $user_ids = UserSetting::where('key', 'external_email')->where('value', $search)
+ ->get()->pluck('user_id');
+
+ // TODO: Sort order
+ $result = User::find($user_ids);
+ }
+ } elseif (is_numeric($search)) {
+ // Search by user ID
+ if ($user = User::find($search)) {
+ $result->push($user);
+ }
+ } elseif (!empty($search)) {
+ // Search by domain
+ if ($domain = Domain::where('namespace', $search)->first()) {
+ if ($wallet = $domain->wallet()) {
+ $result->push($wallet->owner);
+ }
+ }
+ }
+
+ // Process the result
+ $result = $result->map(function ($user) {
$data = $user->toArray();
$data = array_merge($data, self::userStatuses($user));
return $data;
});
+ $result = [
+ 'list' => $result,
+ 'count' => count($result),
+ 'message' => \trans('app.search-foundxusers', ['x' => count($result)]),
+ ];
+
return response()->json($result);
}
}
diff --git a/src/app/User.php b/src/app/User.php
--- a/src/app/User.php
+++ b/src/app/User.php
@@ -355,11 +355,12 @@
* Helper to find user by email address, whether it is
* main email address, alias or external email
*
- * @param string $email Email address
+ * @param string $email Email address
+ * @param bool $external Search also by an external email
*
* @return \App\User User model object if found
*/
- public static function findByEmail(string $email): ?User
+ public static function findByEmail(string $email, bool $external = false): ?User
{
if (strpos($email, '@') === false) {
return null;
diff --git a/src/resources/js/fontawesome.js b/src/resources/js/fontawesome.js
--- a/src/resources/js/fontawesome.js
+++ b/src/resources/js/fontawesome.js
@@ -14,6 +14,7 @@
faLock,
faKey,
faPlus,
+ faSearch,
faSignInAlt,
faSyncAlt,
faTrashAlt,
@@ -32,6 +33,7 @@
faLock,
faKey,
faPlus,
+ faSearch,
faSignInAlt,
faSquare,
faSyncAlt,
diff --git a/src/resources/js/routes-admin.js b/src/resources/js/routes-admin.js
--- a/src/resources/js/routes-admin.js
+++ b/src/resources/js/routes-admin.js
@@ -8,6 +8,7 @@
import LoginComponent from '../vue/Login'
import LogoutComponent from '../vue/Logout'
import PasswordResetComponent from '../vue/PasswordReset'
+import UserComponent from '../vue/Admin/User'
import store from './store'
@@ -37,6 +38,12 @@
name: 'password-reset',
component: PasswordResetComponent
},
+ {
+ path: '/user/:user',
+ name: 'user',
+ component: UserComponent,
+ meta: { requiresAuth: true }
+ },
{
name: '404',
path: '*',
diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php
--- a/src/resources/lang/en/app.php
+++ b/src/resources/lang/en/app.php
@@ -24,4 +24,6 @@
'user-update-success' => 'User data updated successfully.',
'user-create-success' => 'User created successfully.',
'user-delete-success' => 'User deleted successfully.',
+
+ 'search-foundxusers' => ':x user accounts have been found.',
];
diff --git a/src/resources/vue/Admin/Dashboard.vue b/src/resources/vue/Admin/Dashboard.vue
--- a/src/resources/vue/Admin/Dashboard.vue
+++ b/src/resources/vue/Admin/Dashboard.vue
@@ -1,5 +1,36 @@
<template>
<div v-if="!$root.isLoading" class="container" dusk="dashboard-component">
+ <div id="search-box" class="card">
+ <div class="card-body">
+ <form @submit.prevent="searchUser" class="row justify-content-center">
+ <div class="input-group col-sm-8">
+ <input class="form-control" type="text" placeholder="User ID, email or domain" v-model="search">
+ <div class="input-group-append">
+ <button type="submit" class="btn btn-primary"><svg-icon icon="search"></svg-icon> Search</button>
+ </div>
+ </div>
+ </form>
+ <table v-if="users.length" class="table table-sm table-hover mt-4">
+ <thead class="thead-light">
+ <tr>
+ <th scope="col">Primary Email</th>
+ <th scope="col">ID</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr v-for="user in users" :id="'user' + user.id" :key="user.id">
+ <td>
+ <svg-icon icon="user" :class="$root.userStatusClass(user)" :title="$root.userStatusText(user)"></svg-icon>
+ <router-link :to="{ path: 'user/' + user.id }">{{ user.email }}</router-link>
+ </td>
+ <td>
+ <router-link :to="{ path: 'user/' + user.id }">{{ user.id }}</router-link>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
<div id="dashboard-nav"></div>
</div>
</template>
@@ -8,25 +39,45 @@
export default {
data() {
return {
- isReady: true
+ search: '',
+ users: []
}
},
mounted() {
const authInfo = this.$store.state.isLoggedIn ? this.$store.state.authInfo : null
if (authInfo) {
-
+ $('#search-box input').focus()
} else {
this.$root.startLoading()
axios.get('/api/auth/info')
.then(response => {
this.$store.state.authInfo = response.data
this.$root.stopLoading()
+ setTimeout(() => { $('#search-box input').focus() }, 10)
})
.catch(this.$root.errorHandler)
}
},
methods: {
+ searchUser() {
+ this.users = []
+
+ axios.get('/api/v4/users', { params: { search: this.search } })
+ .then(response => {
+ if (response.data.count == 1) {
+ this.$router.push({ name: 'user', params: { user: response.data.list[0].id } })
+ return
+ }
+
+ if (response.data.message) {
+ this.$toastr('info', response.data.message)
+ }
+
+ this.users = response.data.list
+ })
+ .catch(this.$root.errorHandler)
+ }
}
}
</script>
diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue
new file mode 100644
--- /dev/null
+++ b/src/resources/vue/Admin/User.vue
@@ -0,0 +1,107 @@
+<template>
+ <div class="container">
+ <div class="card" id="user-info">
+ <div class="card-body">
+ <div class="card-title">{{ user.email }}</div>
+ <div class="card-text">
+ <form @submit.prevent="submit">
+ <div class="form-group row mb-0">
+ <label for="first_name" class="col-sm-4 col-form-label">Status</label>
+ <div class="col-sm-8">
+ <span :class="$root.userStatusClass(user) + ' form-control-plaintext'" id="status">{{ $root.userStatusText(user) }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0" v-if="user.first_name">
+ <label for="first_name" class="col-sm-4 col-form-label">First name</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="first_name">{{ user.first_name }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0" v-if="user.last_name">
+ <label for="last_name" class="col-sm-4 col-form-label">Last name</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="last_name">{{ user.last_name }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0" v-if="user.phone">
+ <label for="phone" class="col-sm-4 col-form-label">Phone</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="phone">{{ user.phone }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0">
+ <label for="external_email" class="col-sm-4 col-form-label">External email</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="external_email">{{ user.external_email }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0" v-if="user.billing_address">
+ <label for="billing_address" class="col-sm-4 col-form-label">Address</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" style="white-space:pre" id="billing_address">{{ user.billing_address }}</span>
+ </div>
+ </div>
+ <div class="form-group row mb-0">
+ <label for="country" class="col-sm-4 col-form-label">Country</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="country">{{ user.country }}</span>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ export default {
+ data() {
+ return {
+ discount: 0,
+ discount_description: '',
+ user: {},
+ skus: []
+ }
+ },
+ created() {
+ let user_id = this.$route.params.user
+
+ this.$root.startLoading()
+
+ axios.get('/api/v4/users/' + user_id)
+ .then(response => {
+ this.user = response.data
+
+ let keys = ['first_name', 'last_name', 'external_email', 'billing_address']
+ let country = this.user.settings.country
+
+ if (country) {
+ this.user.country = window.config.countries[country][1]
+ }
+
+ keys.forEach(key => { this.user[key] = this.user.settings[key] })
+
+ this.discount = this.user.wallet.discount
+ this.discount_description = this.user.wallet.discount_description
+
+ this.$root.stopLoading()
+ })
+ .catch(this.$root.errorHandler)
+ },
+ mounted() {
+ },
+ methods: {
+ price(cost, units = 1) {
+ let index = ''
+
+ if (this.discount) {
+ cost = Math.floor(cost * ((100 - this.discount) / 100))
+ index = '\u00B9'
+ }
+
+ return this.$root.price(cost * units) + '/month' + index
+ }
+ }
+ }
+</script>
diff --git a/src/tests/Browser/Admin/DashboardTest.php b/src/tests/Browser/Admin/DashboardTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Browser/Admin/DashboardTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Tests\Browser\Admin;
+
+use Tests\Browser;
+use Tests\Browser\Components\Toast;
+use Tests\Browser\Pages\Dashboard;
+use Tests\Browser\Pages\Home;
+use Tests\TestCaseDusk;
+use Illuminate\Foundation\Testing\DatabaseMigrations;
+
+class DashboardTest extends TestCaseDusk
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ self::useAdminUrl();
+
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test user search
+ */
+ public function testSearch(): void
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit(new Home())
+ ->submitLogon('jeroen@jeroen.jeroen', 'jeroen', true)
+ ->on(new Dashboard())
+ ->assertFocused('@search input')
+ ->assertMissing('@search table');
+
+ // Test search with no results
+ $browser->type('@search input', 'unknown')
+ ->click('@search form button')
+ ->assertToast(Toast::TYPE_INFO, '', '0 user accounts have been found.')
+ ->assertMissing('@search table');
+
+ $john = $this->getTestUser('john@kolab.org');
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', 'john.doe.external@gmail.com');
+
+ // Test search with multiple results
+ $browser->type('@search input', 'john.doe.external@gmail.com')
+ ->click('@search form button')
+ ->assertToast(Toast::TYPE_INFO, '', '2 user accounts have been found.')
+ ->whenAvailable('@search table', function (Browser $browser) {
+ $browser->assertElementsCount('tbody tr', 2);
+ // TODO: Assert table content
+ });
+
+ // Test search with single record result -> redirect to user page
+ $browser->type('@search input', 'kolab.org')
+ ->click('@search form button')
+ ->assertMissing('@search table')
+ ->waitForLocation('/user/' . $john->id)
+ ->waitFor('#user-info')
+ ->assertVisible('#user-info .card-title', $john->email);
+ });
+ }
+}
diff --git a/src/tests/Browser/Admin/LogonTest.php b/src/tests/Browser/Admin/LogonTest.php
--- a/src/tests/Browser/Admin/LogonTest.php
+++ b/src/tests/Browser/Admin/LogonTest.php
@@ -18,11 +18,7 @@
public function setUp(): void
{
parent::setUp();
-
- // This will set baseURL for all tests in this file
- // If we wanted to visit both user and admin in one test
- // we can also just call visit() with full url
- Browser::$baseUrl = str_replace('//', '//admin.', \config('app.url'));
+ self::useAdminUrl();
}
/**
diff --git a/src/tests/Browser/Pages/Dashboard.php b/src/tests/Browser/Pages/Dashboard.php
--- a/src/tests/Browser/Pages/Dashboard.php
+++ b/src/tests/Browser/Pages/Dashboard.php
@@ -52,6 +52,7 @@
'@app' => '#app',
'@links' => '#dashboard-nav',
'@status' => '#status-box',
+ '@search' => '#search-box',
];
}
}
diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php
--- a/src/tests/Browser/UsersTest.php
+++ b/src/tests/Browser/UsersTest.php
@@ -261,25 +261,25 @@
'tbody tr:nth-child(3) td.buttons button',
'Groupware functions like Calendar, Tasks, Notes, etc.'
)
- // 2FA SKU
- ->assertSeeIn('tbody tr:nth-child(4) td.name', '2-Factor Authentication')
- ->assertSeeIn('tbody tr:nth-child(4) td.price', '0,00 CHF/month')
+ // ActiveSync SKU
+ ->assertSeeIn('tbody tr:nth-child(4) td.name', 'Activesync')
+ ->assertSeeIn('tbody tr:nth-child(4) td.price', '1,00 CHF/month')
->assertNotChecked('tbody tr:nth-child(4) td.selection input')
->assertEnabled('tbody tr:nth-child(4) td.selection input')
->assertTip(
'tbody tr:nth-child(4) td.buttons button',
- 'Two factor authentication for webmail and administration panel'
+ 'Mobile synchronization'
)
- // ActiveSync SKU
- ->assertSeeIn('tbody tr:nth-child(5) td.name', 'Activesync')
- ->assertSeeIn('tbody tr:nth-child(5) td.price', '1,00 CHF/month')
+ // 2FA SKU
+ ->assertSeeIn('tbody tr:nth-child(5) td.name', '2-Factor Authentication')
+ ->assertSeeIn('tbody tr:nth-child(5) td.price', '0,00 CHF/month')
->assertNotChecked('tbody tr:nth-child(5) td.selection input')
->assertEnabled('tbody tr:nth-child(5) td.selection input')
->assertTip(
'tbody tr:nth-child(5) td.buttons button',
- 'Mobile synchronization'
+ 'Two factor authentication for webmail and administration panel'
)
- ->click('tbody tr:nth-child(5) td.selection input');
+ ->click('tbody tr:nth-child(4) td.selection input');
})
->assertMissing('@skus table + .hint')
->click('button[type=submit]');
@@ -561,10 +561,10 @@
->assertSeeIn('tr:nth-child(2) td.price', '21,56 CHF/month¹')
// groupware SKU
->assertSeeIn('tbody tr:nth-child(3) td.price', '4,99 CHF/month¹')
- // 2FA SKU
- ->assertSeeIn('tbody tr:nth-child(4) td.price', '0,00 CHF/month¹')
// ActiveSync SKU
- ->assertSeeIn('tbody tr:nth-child(5) td.price', '0,90 CHF/month¹');
+ ->assertSeeIn('tbody tr:nth-child(4) td.price', '0,90 CHF/month¹')
+ // 2FA SKU
+ ->assertSeeIn('tbody tr:nth-child(5) td.price', '0,00 CHF/month¹');
})
->assertSeeIn('@skus table + .hint', '¹ applied discount: 10% - Test voucher');
});
diff --git a/src/tests/Feature/Controller/Admin/UsersTest.php b/src/tests/Feature/Controller/Admin/UsersTest.php
--- a/src/tests/Feature/Controller/Admin/UsersTest.php
+++ b/src/tests/Feature/Controller/Admin/UsersTest.php
@@ -2,8 +2,6 @@
namespace Tests\Feature\Controller\Admin;
-use App\Domain;
-use App\User;
use Tests\TestCase;
class UsersTest extends TestCase
@@ -14,11 +12,10 @@
public function setUp(): void
{
parent::setUp();
+ self::useAdminUrl();
- // This will set base URL for all tests in this file
- // If we wanted to access both user and admin in one test
- // we can also just call post/get/whatever with full url
- \config(['app.url' => str_replace('//', '//admin.', \config('app.url'))]);
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', null);
}
/**
@@ -26,24 +23,101 @@
*/
public function tearDown(): void
{
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', null);
+
parent::tearDown();
}
/**
- * Test (/api/v4/index)
+ * Test users searching (/api/v4/users)
*/
public function testIndex(): void
{
$user = $this->getTestUser('john@kolab.org');
$admin = $this->getTestUser('jeroen@jeroen.jeroen');
+ // Non-admin user
$response = $this->actingAs($user)->get("api/v4/users");
$response->assertStatus(403);
+ // Search with no search criteria
$response = $this->actingAs($admin)->get("api/v4/users");
$response->assertStatus(200);
- // TODO: Test the response
- $this->markTestIncomplete();
+ $json = $response->json();
+
+ $this->assertSame(0, $json['count']);
+ $this->assertSame([], $json['list']);
+
+ // Search with no matches expected
+ $response = $this->actingAs($admin)->get("api/v4/users?search=abcd1234efgh5678");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(0, $json['count']);
+ $this->assertSame([], $json['list']);
+
+ // Search by domain
+ $response = $this->actingAs($admin)->get("api/v4/users?search=kolab.org");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(1, $json['count']);
+ $this->assertCount(1, $json['list']);
+ $this->assertSame($user->id, $json['list'][0]['id']);
+ $this->assertSame($user->email, $json['list'][0]['email']);
+
+ // Search by user ID
+ $response = $this->actingAs($admin)->get("api/v4/users?search={$user->id}");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(1, $json['count']);
+ $this->assertCount(1, $json['list']);
+ $this->assertSame($user->id, $json['list'][0]['id']);
+ $this->assertSame($user->email, $json['list'][0]['email']);
+
+ // Search by email (primary)
+ $response = $this->actingAs($admin)->get("api/v4/users?search=john@kolab.org");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(1, $json['count']);
+ $this->assertCount(1, $json['list']);
+ $this->assertSame($user->id, $json['list'][0]['id']);
+ $this->assertSame($user->email, $json['list'][0]['email']);
+
+ // Search by email (alias)
+ $response = $this->actingAs($admin)->get("api/v4/users?search=john.doe@kolab.org");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(1, $json['count']);
+ $this->assertCount(1, $json['list']);
+ $this->assertSame($user->id, $json['list'][0]['id']);
+ $this->assertSame($user->email, $json['list'][0]['email']);
+
+ // Search by email (external), expect two users in a result
+ $jack = $this->getTestUser('jack@kolab.org');
+ $jack->setSetting('external_email', 'john.doe.external@gmail.com');
+
+ $response = $this->actingAs($admin)->get("api/v4/users?search=john.doe.external@gmail.com");
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame(2, $json['count']);
+ $this->assertCount(2, $json['list']);
+
+ $emails = array_column($json['list'], 'email');
+
+ $this->assertContains($user->email, $emails);
+ $this->assertContains($jack->email, $emails);
}
}
diff --git a/src/tests/TestCase.php b/src/tests/TestCase.php
--- a/src/tests/TestCase.php
+++ b/src/tests/TestCase.php
@@ -16,4 +16,15 @@
$entitlement->save();
}
}
+
+ /**
+ * Set baseURL to the admin UI location
+ */
+ protected static function useAdminUrl(): void
+ {
+ // This will set base URL for all tests in a file.
+ // If we wanted to access both user and admin in one test
+ // we can also just call post/get/whatever with full url
+ \config(['app.url' => str_replace('//', '//admin.', \config('app.url'))]);
+ }
}
diff --git a/src/tests/TestCaseDusk.php b/src/tests/TestCaseDusk.php
--- a/src/tests/TestCaseDusk.php
+++ b/src/tests/TestCaseDusk.php
@@ -33,7 +33,6 @@
'--lang=en_US',
'--disable-gpu',
'--headless',
- '--window-size=1280,720',
]);
// For file download handling
@@ -57,7 +56,7 @@
$options->setExperimentalOption('mobileEmulation', ['userAgent' => $ua]);
$options->addArguments(['--window-size=800,640']);
} else {
- $options->addArguments(['--window-size=1280,720']);
+ $options->addArguments(['--window-size=2560,1440']);
}
// Make sure downloads dir exists and is empty
@@ -85,4 +84,15 @@
{
return new Browser($driver);
}
+
+ /**
+ * Set baseURL to the admin UI location
+ */
+ protected static function useAdminUrl(): void
+ {
+ // This will set baseURL for all tests in this file
+ // If we wanted to visit both user and admin in one test
+ // we can also just call visit() with full url
+ Browser::$baseUrl = str_replace('//', '//admin.', \config('app.url'));
+ }
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 4, 6:40 PM (19 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18830779
Default Alt Text
D1123.1775328006.diff (29 KB)
Attached To
Mode
D1123: Support Dashboard
Attached
Detach File
Event Timeline