Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
19 KB
Referenced Files
None
Subscribers
None
diff --git a/src/resources/vue/Widgets/Status.vue b/src/resources/vue/Widgets/Status.vue
index 2549569f..241f1794 100644
--- a/src/resources/vue/Widgets/Status.vue
+++ b/src/resources/vue/Widgets/Status.vue
@@ -1,177 +1,185 @@
<template>
<div v-if="!state.isReady" id="status-box" :class="'p-4 mb-3 rounded process-' + className">
<div v-if="state.step != 'domain-confirmed'" class="d-flex align-items-start">
<p id="status-body" class="flex-grow-1">
<span v-if="scope == 'dashboard'">We are preparing your account.</span>
<span v-else-if="scope == 'domain'">We are preparing the domain.</span>
<span v-else>We are preparing the user account.</span>
<br>
Some features may be missing or readonly at the moment.<br>
<span id="refresh-text" v-if="refresh">The process never ends? Press the "Refresh" button, please.</span>
</p>
<button v-if="refresh" id="status-refresh" href="#" class="btn btn-secondary" @click="statusRefresh">
<svg-icon icon="sync-alt"></svg-icon> Refresh
</button>
</div>
<div v-else class="d-flex align-items-start">
<p id="status-body" class="flex-grow-1">
<span v-if="scope == 'dashboard'">Your account is almost ready.</span>
<span v-else-if="scope == 'domain'">The domain is almost ready.</span>
<span v-else>The user account is almost ready.</span>
<br>
Verify your domain to finish the setup process.
</p>
<div v-if="scope == 'domain'">
<button id="status-verify" class="btn btn-secondary text-nowrap" @click="confirmDomain">
<svg-icon icon="sync-alt"></svg-icon> Verify
</button>
</div>
<div v-else-if="state.link && scope != 'domain'">
<router-link id="status-link" class="btn btn-secondary" :to="{ path: state.link }">Verify domain</router-link>
</div>
</div>
<div class="status-progress text-center">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<span class="progress-label">{{ state.title || 'Initializing...' }}</span>
</div>
</div>
</template>
<script>
export default {
props: {
status: { type: Object, default: () => {} }
},
data() {
return {
className: 'pending',
refresh: false,
delay: 5000,
scope: 'user',
state: { isReady: true }
}
},
watch: {
// We use property watcher because parent component
// might set the property with a delay and we need to parse it
status: function (val, oldVal) {
this.parseStatusInfo(val)
}
},
destroyed() {
clearTimeout(window.infoRequest)
},
mounted() {
this.scope = this.$route.name
},
methods: {
// Displays account status information
parseStatusInfo(info) {
if (info) {
if (!info.isReady) {
+ let failedCount = 0
+ let allCount = info.process.length
+
info.process.forEach((step, idx) => {
- if (!step.state && !('percent' in info)) {
- info.title = step.title
- info.step = step.label
- info.percent = Math.floor(idx / info.process.length * 100);
- info.link = step.link
+ if (!step.state) {
+ failedCount++
+
+ if (!info.title) {
+ info.title = step.title
+ info.step = step.label
+ info.link = step.link
+ }
}
})
+
+ info.percent = Math.floor((allCount - failedCount) / allCount * 100);
}
this.state = info || {}
this.$nextTick(function() {
$(this.$el).find('.progress-bar')
.css('width', info.percent + '%')
.attr('aria-valuenow', info.percent)
})
// Unhide the Refresh button, the process is in failure state
this.refresh = info.processState == 'failed'
if (this.refresh || info.step == 'domain-confirmed') {
this.className = 'failed'
}
}
// Update status process info every 10 seconds
// FIXME: This probably should have some limit, or the interval
// should grow (well, until it could be done with websocket notifications)
clearTimeout(window.infoRequest)
if (!this.refresh && (!info || !info.isReady)) {
window.infoRequest = setTimeout(() => {
delete window.infoRequest
// Stop updates after user logged out
if (!this.$store.state.isLoggedIn) {
return;
}
axios.get(this.getUrl())
.then(response => {
this.parseStatusInfo(response.data)
this.emitEvent(response.data)
})
.catch(error => {
this.parseStatusInfo(info)
})
}, this.delay);
this.delay += 1000;
}
},
statusRefresh() {
clearTimeout(window.infoRequest)
axios.get(this.getUrl() + '?refresh=1')
.then(response => {
this.$toast[response.data.status](response.data.message)
this.parseStatusInfo(response.data)
this.emitEvent(response.data)
})
.catch(error => {
this.parseStatusInfo(this.state)
})
},
confirmDomain() {
axios.get('/api/v4/domains/' + this.$route.params.domain + '/confirm')
.then(response => {
if (response.data.message) {
this.$toast[response.data.status](response.data.message)
}
if (response.data.status == 'success') {
this.parseStatusInfo(response.data.statusInfo)
response.data.isConfirmed = true
this.emitEvent(response.data)
}
})
},
emitEvent(data) {
// Remove useless data and emit the event (to parent components)
delete data.status
delete data.message
this.$emit('status-update', data)
},
getUrl() {
let url
switch (this.scope) {
case 'dashboard':
url = '/api/v4/users/' + this.$store.state.authInfo.id + '/status'
break
case 'domain':
url = '/api/v4/domains/' + this.$route.params.domain + '/status'
break
default:
url = '/api/v4/users/' + this.$route.params.user + '/status'
}
return url
}
}
}
</script>
diff --git a/src/tests/Browser/StatusTest.php b/src/tests/Browser/StatusTest.php
index b55e4f32..0a312d6c 100644
--- a/src/tests/Browser/StatusTest.php
+++ b/src/tests/Browser/StatusTest.php
@@ -1,270 +1,270 @@
<?php
namespace Tests\Browser;
use App\Domain;
use App\User;
use Carbon\Carbon;
use Tests\Browser;
use Tests\Browser\Components\Status;
use Tests\Browser\Components\Toast;
use Tests\Browser\Pages\Dashboard;
use Tests\Browser\Pages\DomainInfo;
use Tests\Browser\Pages\DomainList;
use Tests\Browser\Pages\Home;
use Tests\Browser\Pages\UserInfo;
use Tests\Browser\Pages\UserList;
use Tests\TestCaseDusk;
use Illuminate\Support\Facades\DB;
class StatusTest extends TestCaseDusk
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$domain_status = Domain::STATUS_CONFIRMED | Domain::STATUS_VERIFIED;
DB::statement("UPDATE domains SET status = (status | {$domain_status})"
. " WHERE namespace = 'kolab.org'");
DB::statement("UPDATE users SET status = (status | " . User::STATUS_IMAP_READY . ")"
. " WHERE email = 'john@kolab.org'");
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$domain_status = Domain::STATUS_CONFIRMED | Domain::STATUS_VERIFIED;
DB::statement("UPDATE domains SET status = (status | {$domain_status})"
. " WHERE namespace = 'kolab.org'");
DB::statement("UPDATE users SET status = (status | " . User::STATUS_IMAP_READY . ")"
. " WHERE email = 'john@kolab.org'");
parent::tearDown();
}
/**
* Test account status in the Dashboard
*/
public function testDashboard(): void
{
// Unconfirmed domain and user
$domain = Domain::where('namespace', 'kolab.org')->first();
if ($domain->isConfirmed()) {
$domain->status ^= Domain::STATUS_CONFIRMED;
$domain->save();
}
$john = $this->getTestUser('john@kolab.org');
$john->created_at = Carbon::now();
if ($john->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
}
$john->save();
$this->browse(function ($browser) use ($john, $domain) {
$browser->visit(new Home())
->submitLogon('john@kolab.org', 'simple123', true)
->on(new Dashboard())
->with(new Status(), function ($browser) use ($john) {
$browser->assertSeeIn('@body', 'We are preparing your account')
- ->assertProgress(28, 'Creating a mailbox...', 'pending')
+ ->assertProgress(71, 'Creating a mailbox...', 'pending')
->assertMissing('#status-verify')
->assertMissing('#status-link')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text');
$john->status |= User::STATUS_IMAP_READY;
$john->save();
// Wait for auto-refresh, expect domain-confirmed step
$browser->pause(6000)
->assertSeeIn('@body', 'Your account is almost ready')
->assertProgress(85, 'Verifying an ownership of a custom domain...', 'failed')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text')
->assertMissing('#status-verify')
->assertVisible('#status-link');
})
// check if the link to domain info page works
->click('#status-link')
->on(new DomainInfo())
->back()
->on(new Dashboard())
->with(new Status(), function ($browser) {
$browser->assertMissing('@refresh-button')
->assertProgress(85, 'Verifying an ownership of a custom domain...', 'failed');
});
// Confirm the domain and wait until the whole status box disappears
$domain->status |= Domain::STATUS_CONFIRMED;
$domain->save();
// This should take less than 10 seconds
$browser->waitUntilMissing('@status', 10);
});
// Test the Refresh button
if ($domain->isConfirmed()) {
$domain->status ^= Domain::STATUS_CONFIRMED;
$domain->save();
}
$john->created_at = Carbon::now()->subSeconds(3600);
if ($john->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
}
$john->save();
$this->browse(function ($browser) use ($john, $domain) {
$browser->visit(new Dashboard())
->with(new Status(), function ($browser) use ($john, $domain) {
$browser->assertSeeIn('@body', 'We are preparing your account')
- ->assertProgress(28, 'Creating a mailbox...', 'failed')
+ ->assertProgress(71, 'Creating a mailbox...', 'failed')
->assertVisible('@refresh-button')
->assertVisible('@refresh-text');
if ($john->refresh()->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
$john->save();
}
$domain->status |= Domain::STATUS_CONFIRMED;
$domain->save();
$browser->click('@refresh-button')
->assertToast(Toast::TYPE_SUCCESS, 'Setup process finished successfully.');
})
->assertMissing('@status');
});
}
/**
* Test domain status on domains list and domain info page
*
* @depends testDashboard
*/
public function testDomainStatus(): void
{
$domain = Domain::where('namespace', 'kolab.org')->first();
$domain->created_at = Carbon::now();
$domain->status = Domain::STATUS_NEW | Domain::STATUS_ACTIVE | Domain::STATUS_LDAP_READY;
$domain->save();
$this->browse(function ($browser) use ($domain) {
// Test auto-refresh
$browser->on(new Dashboard())
->click('@links a.link-domains')
->on(new DomainList())
->waitFor('@table tbody tr')
// Assert domain status icon
->assertVisible('@table tbody tr:first-child td:first-child svg.fa-globe.text-danger')
->assertText('@table tbody tr:first-child td:first-child svg title', 'Not Ready')
->click('@table tbody tr:first-child td:first-child a')
->on(new DomainInfo())
->with(new Status(), function ($browser) {
$browser->assertSeeIn('@body', 'We are preparing the domain')
->assertProgress(50, 'Verifying a custom domain...', 'pending')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text')
->assertMissing('#status-link')
->assertMissing('#status-verify');
});
$domain->status |= Domain::STATUS_VERIFIED;
$domain->save();
// This should take less than 10 seconds
$browser->waitFor('@status.process-failed')
->with(new Status(), function ($browser) {
$browser->assertSeeIn('@body', 'The domain is almost ready')
->assertProgress(75, 'Verifying an ownership of a custom domain...', 'failed')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text')
->assertMissing('#status-link')
->assertVisible('#status-verify');
});
$domain->status |= Domain::STATUS_CONFIRMED;
$domain->save();
// Test Verify button
$browser->click('@status #status-verify')
->assertToast(Toast::TYPE_SUCCESS, 'Domain verified successfully.')
->waitUntilMissing('@status')
->assertMissing('@verify')
->assertVisible('@config');
});
}
/**
* Test user status on users list and user info page
*
* @depends testDashboard
*/
public function testUserStatus(): void
{
$john = $this->getTestUser('john@kolab.org');
$john->created_at = Carbon::now();
if ($john->isImapReady()) {
$john->status ^= User::STATUS_IMAP_READY;
}
$john->save();
$domain = Domain::where('namespace', 'kolab.org')->first();
if ($domain->isConfirmed()) {
$domain->status ^= Domain::STATUS_CONFIRMED;
$domain->save();
}
$this->browse(function ($browser) use ($john, $domain) {
$browser->visit(new Dashboard())
->click('@links a.link-users')
->on(new UserList())
->waitFor('@table tbody tr')
// Assert user status icons
->assertVisible('@table tbody tr:first-child td:first-child svg.fa-user.text-success')
->assertText('@table tbody tr:first-child td:first-child svg title', 'Active')
->assertVisible('@table tbody tr:nth-child(3) td:first-child svg.fa-user.text-danger')
->assertText('@table tbody tr:nth-child(3) td:first-child svg title', 'Not Ready')
->click('@table tbody tr:nth-child(3) td:first-child a')
->on(new UserInfo())
->with('@form', function (Browser $browser) {
// Assert state in the user edit form
$browser->assertSeeIn('div.row:nth-child(1) label', 'Status')
->assertSeeIn('div.row:nth-child(1) #status', 'Not Ready');
})
->with(new Status(), function ($browser) use ($john) {
$browser->assertSeeIn('@body', 'We are preparing the user account')
- ->assertProgress(28, 'Creating a mailbox...', 'pending')
+ ->assertProgress(71, 'Creating a mailbox...', 'pending')
->assertMissing('#status-verify')
->assertMissing('#status-link')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text');
$john->status |= User::STATUS_IMAP_READY;
$john->save();
// Wait for auto-refresh, expect domain-confirmed step
$browser->pause(6000)
->assertSeeIn('@body', 'The user account is almost ready')
->assertProgress(85, 'Verifying an ownership of a custom domain...', 'failed')
->assertMissing('@refresh-button')
->assertMissing('@refresh-text')
->assertMissing('#status-verify')
->assertVisible('#status-link');
})
->assertSeeIn('#status', 'Active');
// Confirm the domain and wait until the whole status box disappears
$domain->status |= Domain::STATUS_CONFIRMED;
$domain->save();
// This should take less than 10 seconds
$browser->waitUntilMissing('@status', 10);
});
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Apr 5, 9:45 PM (3 w, 10 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831296
Default Alt Text
(19 KB)

Event Timeline