diff --git a/src/tests/Browser/Admin/UserTest.php b/src/tests/Browser/Admin/UserTest.php index 91314627..07f4be89 100644 --- a/src/tests/Browser/Admin/UserTest.php +++ b/src/tests/Browser/Admin/UserTest.php @@ -1,724 +1,724 @@ getTestUser('john@kolab.org'); $john->setSettings([ 'phone' => '+48123123123', 'external_email' => 'john.doe.external@gmail.com', ]); if ($john->isSuspended()) { User::where('email', $john->email)->update(['status' => $john->status - User::STATUS_SUSPENDED]); } $wallet = $john->wallets()->first(); $wallet->discount()->dissociate(); $wallet->save(); Entitlement::where('cost', '>=', 5000)->delete(); Eventlog::query()->delete(); $this->deleteTestGroup('group-test@kolab.org'); $this->deleteTestUser('userstest1@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $john = $this->getTestUser('john@kolab.org'); $john->setSettings([ 'phone' => null, 'external_email' => 'john.doe.external@gmail.com', ]); if ($john->isSuspended()) { User::where('email', $john->email)->update(['status' => $john->status - User::STATUS_SUSPENDED]); } $wallet = $john->wallets()->first(); $wallet->discount()->dissociate(); $wallet->save(); Entitlement::where('cost', '>=', 5000)->delete(); Eventlog::query()->delete(); $this->deleteTestGroup('group-test@kolab.org'); $this->deleteTestUser('userstest1@kolabnow.com'); parent::tearDown(); } /** * Test user info page (unauthenticated) */ public function testUserUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $jack = $this->getTestUser('jack@kolab.org'); $browser->visit('/user/' . $jack->id)->on(new Home()); }); } /** * Test user info page */ public function testUserInfo(): void { $this->browse(function (Browser $browser) { $jack = $this->getTestUser('jack@kolab.org'); $jack->setSettings([ 'limit_geo' => null, 'organization' => null, 'guam_enabled' => null, ]); $event1 = EventLog::createFor($jack, EventLog::TYPE_SUSPENDED, 'Event 1'); $event2 = EventLog::createFor($jack, EventLog::TYPE_UNSUSPENDED, 'Event 2', ['test' => 'test-data']); $event2->refresh(); $event1->created_at = (clone $event2->created_at)->subDay(); $event1->user_email = 'jeroen@jeroen.jeroen'; $event1->save(); $event2->user_email = 'test@test.com'; $event2->save(); $page = new UserPage($jack->id); $browser->visit(new Home()) ->submitLogon('jeroen@jeroen.jeroen', \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->visit($page) ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $jack->email) ->with('@user-info form', function (Browser $browser) use ($jack) { $browser->assertElementsCount('.row', 7) ->assertSeeIn('.row:nth-child(1) label', 'Managed by') ->assertSeeIn('.row:nth-child(1) #manager a', 'john@kolab.org') ->assertSeeIn('.row:nth-child(2) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(2) #userid', "{$jack->id} ({$jack->created_at})") ->assertSeeIn('.row:nth-child(3) label', 'Status') ->assertSeeIn('.row:nth-child(3) #status span.text-success', 'Active') ->assertDontSeeIn('.row:nth-child(3) #status', 'Restricted') ->assertSeeIn('.row:nth-child(4) label', 'First Name') ->assertSeeIn('.row:nth-child(4) #first_name', 'Jack') ->assertSeeIn('.row:nth-child(5) label', 'Last Name') ->assertSeeIn('.row:nth-child(5) #last_name', 'Daniels') ->assertSeeIn('.row:nth-child(6) label', 'External Email') ->assertMissing('.row:nth-child(6) #external_email a') ->assertSeeIn('.row:nth-child(7) label', 'Country') ->assertSeeIn('.row:nth-child(7) #country', 'United States'); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (1)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 1) ->assertSeeIn('table tbody tr:first-child td:first-child', 'jack.daniels@kolab.org') ->assertMissing('table tfoot'); }); // Assert Subscriptions tab $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (3)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 3) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '5,00 CHF') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 5 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,90 CHF') ->assertMissing('table tfoot') ->assertMissing('#reset2fa'); }); // Assert Domains tab $browser->assertSeeIn('@nav #tab-domains', 'Domains (0)') ->click('@nav #tab-domains') ->with('@user-domains', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no domains in this account.'); }); // Assert Users tab $browser->assertSeeIn('@nav #tab-users', 'Users (0)') ->click('@nav #tab-users') ->with('@user-users', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no users in this account.'); }); // Assert Distribution lists tab $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (0)') ->click('@nav #tab-distlists') ->with('@user-distlists', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no distribution lists in this account.'); }); // Assert Resources tab $browser->assertSeeIn('@nav #tab-resources', 'Resources (0)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no resources in this account.'); }); // Assert Shared folders tab $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (0)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no shared folders in this account.'); }); // Assert Settings tab $browser->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->whenAvailable('@user-settings form', function (Browser $browser) { $browser->assertElementsCount('.row', 3) ->assertSeeIn('.row:first-child label', 'Greylisting') ->assertSeeIn('.row:first-child .text-success', 'enabled') ->assertSeeIn('.row:nth-child(2) label', 'IMAP proxy') ->assertSeeIn('.row:nth-child(2) .text-danger', 'disabled') ->assertSeeIn('.row:nth-child(3) label', 'Geo-lockin') ->assertSeeIn('.row:nth-child(3) #limit_geo', 'No restrictions') ->assertMissing('#limit_geo + button'); }); // Assert History tab $browser->assertSeeIn('@nav #tab-history', 'History') ->click('@nav #tab-history') ->whenAvailable('@user-history table', function (Browser $browser) use ($event1, $event2) { $browser->waitFor('tbody tr')->assertElementsCount('tbody tr', 2) // row 1 ->assertSeeIn('tr:nth-child(1) td:nth-child(1)', $event2->created_at->toDateTimeString()) ->assertSeeIn('tr:nth-child(1) td:nth-child(2)', 'Unsuspended') ->assertSeeIn('tr:nth-child(1) td:nth-child(3)', $event2->comment) ->assertMissing('tr:nth-child(1) td:nth-child(3) div') ->assertMissing('tr:nth-child(1) td:nth-child(3) pre') ->assertMissing('tr:nth-child(1) td:nth-child(3) .btn-less') ->click('tr:nth-child(1) td:nth-child(3) .btn-more') ->assertSeeIn('tr:nth-child(1) td:nth-child(3) div.email', $event2->user_email) ->assertSeeIn('tr:nth-child(1) td:nth-child(3) pre', 'test-data') ->assertMissing('tr:nth-child(1) td:nth-child(3) .btn-more') ->click('tr:nth-child(1) td:nth-child(3) .btn-less') ->assertMissing('tr:nth-child(1) td:nth-child(3) div') ->assertMissing('tr:nth-child(1) td:nth-child(3) pre') ->assertMissing('tr:nth-child(1) td:nth-child(3) .btn-less') // row 2 ->assertSeeIn('tr:nth-child(2) td:nth-child(1)', $event1->created_at->toDateTimeString()) ->assertSeeIn('tr:nth-child(2) td:nth-child(2)', 'Suspended') ->assertSeeIn('tr:nth-child(2) td:nth-child(3)', $event1->comment) ->click('tr:nth-child(2) td:nth-child(3) .btn-more') ->assertSeeIn('tr:nth-child(2) td:nth-child(3) div.email', $event1->user_email) ->assertMissing('tr:nth-child(2) td:nth-child(3) pre'); }); }); } /** * Test user info page (continue) * * @depends testUserInfo */ public function testUserInfo2(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $page = new UserPage($john->id); $discount = Discount::where('code', 'TEST')->first(); $wallet = $john->wallet(); $wallet->discount()->associate($discount); $wallet->debit(2010); $wallet->save(); $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']); $group->assignToWallet($john->wallets->first()); $john->setSetting('greylist_enabled', null); // Click the managed-by link on Jack's page $browser->click('@user-info #manager a') ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $john->email) ->with('@user-info form', function (Browser $browser) use ($john) { $ext_email = $john->getSetting('external_email'); $browser->assertElementsCount('.row', 9) ->assertSeeIn('.row:nth-child(1) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(1) #userid', "{$john->id} ({$john->created_at})") ->assertSeeIn('.row:nth-child(2) label', 'Status') ->assertSeeIn('.row:nth-child(2) #status span.text-success', 'Active') ->assertSeeIn('.row:nth-child(3) label', 'First Name') ->assertSeeIn('.row:nth-child(3) #first_name', 'John') ->assertSeeIn('.row:nth-child(4) label', 'Last Name') ->assertSeeIn('.row:nth-child(4) #last_name', 'Doe') ->assertSeeIn('.row:nth-child(5) label', 'Organization') ->assertSeeIn('.row:nth-child(5) #organization', 'Kolab Developers') ->assertSeeIn('.row:nth-child(6) label', 'Phone') ->assertSeeIn('.row:nth-child(6) #phone', $john->getSetting('phone')) ->assertSeeIn('.row:nth-child(7) label', 'External Email') ->assertSeeIn('.row:nth-child(7) #external_email a', $ext_email) ->assertAttribute('.row:nth-child(7) #external_email a', 'href', "mailto:$ext_email") ->assertSeeIn('.row:nth-child(8) label', 'Address') ->assertSeeIn('.row:nth-child(8) #billing_address', $john->getSetting('billing_address')) ->assertSeeIn('.row:nth-child(9) label', 'Country') ->assertSeeIn('.row:nth-child(9) #country', 'United States'); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (1)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 1) ->assertSeeIn('table tbody tr:first-child td:first-child', 'john.doe@kolab.org') ->assertMissing('table tfoot'); }); // Assert Subscriptions tab $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (3)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 3) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '4,50 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 5 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,41 CHF/month¹') ->assertMissing('table tfoot') ->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher'); }); // Assert Domains tab $browser->assertSeeIn('@nav #tab-domains', 'Domains (1)') ->click('@nav #tab-domains') ->with('@user-domains table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'kolab.org') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-success') ->assertMissing('tfoot'); }); // Assert Users tab $browser->assertSeeIn('@nav #tab-users', 'Users (4)') ->click('@nav #tab-users') ->with('@user-users table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 4) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'jack@kolab.org') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(2) td:first-child a', 'joe@kolab.org') ->assertVisible('tbody tr:nth-child(2) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(3) td:first-child span', 'john@kolab.org') ->assertVisible('tbody tr:nth-child(3) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(4) td:first-child a', 'ned@kolab.org') ->assertVisible('tbody tr:nth-child(4) td:first-child svg.text-success') ->assertMissing('tfoot'); }); // Assert Distribution lists tab $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (1)') ->click('@nav #tab-distlists') ->with('@user-distlists table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'Test Group') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-danger') ->assertSeeIn('tbody tr:nth-child(1) td:last-child a', 'group-test@kolab.org') ->assertMissing('tfoot'); }); // Assert Resources tab $browser->assertSeeIn('@nav #tab-resources', 'Resources (2)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 2) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'Conference Room #1') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', 'resource-test1@kolab.org') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Conference Room #2') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', 'resource-test2@kolab.org') ->assertMissing('table tfoot'); }); // Assert Shared folders tab $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (2)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 2) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'Calendar') ->assertSeeIn('table tbody tr:nth-child(1) td:nth-child(2)', 'Calendar') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', 'folder-event@kolab.org') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Contacts') ->assertSeeIn('table tbody tr:nth-child(2) td:nth-child(2)', 'Address Book') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', 'folder-contact@kolab.org') ->assertMissing('table tfoot'); }); // Assert History tab $browser->assertSeeIn('@nav #tab-history', 'History') ->click('@nav #tab-history') ->whenAvailable('@user-history table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 0) ->assertSeeIn('tfoot tr td', "There's no events in the log"); }); }); // Now we go to Ned's info page, he's a controller on John's wallet $this->browse(function (Browser $browser) { $ned = $this->getTestUser('ned@kolab.org'); $beta_sku = Sku::withEnvTenantContext()->where('title', 'beta')->first(); $storage_sku = Sku::withEnvTenantContext()->where('title', 'storage')->first(); $wallet = $ned->wallet(); // Add an extra storage and beta entitlement with different prices Entitlement::create([ 'wallet_id' => $wallet->id, 'sku_id' => $beta_sku->id, 'cost' => 5010, 'entitleable_id' => $ned->id, 'entitleable_type' => User::class ]); Entitlement::create([ 'wallet_id' => $wallet->id, 'sku_id' => $storage_sku->id, 'cost' => 5000, 'entitleable_id' => $ned->id, 'entitleable_type' => User::class ]); $page = new UserPage($ned->id); $ned->setSetting('greylist_enabled', 'false'); $browser->click('@nav #tab-users') ->click('@user-users tbody tr:nth-child(4) td:first-child a') ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $ned->email) ->with('@user-info form', function (Browser $browser) use ($ned) { $browser->assertSeeIn('.row:nth-child(2) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(2) #userid', "{$ned->id} ({$ned->created_at})"); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (0)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'This user has no email aliases.'); }); // Assert Subscriptions tab, we expect John's discount here $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (6)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 6) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '4,50 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 6 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '45,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,41 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(4) td:first-child', 'Activesync') ->assertSeeIn('table tbody tr:nth-child(4) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(5) td:first-child', '2-Factor Authentication') ->assertSeeIn('table tbody tr:nth-child(5) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(6) td:first-child', 'Private Beta (invitation only)') ->assertSeeIn('table tbody tr:nth-child(6) td:last-child', '45,09 CHF/month¹') ->assertMissing('table tfoot') ->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher') ->assertSeeIn('#reset2fa', 'Reset 2-Factor Auth') ->assertMissing('#addbetasku'); }); // We don't expect John's domains here $browser->assertSeeIn('@nav #tab-domains', 'Domains (0)') ->click('@nav #tab-domains') ->with('@user-domains', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no domains in this account.'); }); // We don't expect John's users here $browser->assertSeeIn('@nav #tab-users', 'Users (0)') ->click('@nav #tab-users') ->with('@user-users', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no users in this account.'); }); // We don't expect John's distribution lists here $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (0)') ->click('@nav #tab-distlists') ->with('@user-distlists', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no distribution lists in this account.'); }); // We don't expect John's resources here $browser->assertSeeIn('@nav #tab-resources', 'Resources (0)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no resources in this account.'); }); // We don't expect John's folders here $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (0)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no shared folders in this account.'); }); // Assert Settings tab $browser->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->whenAvailable('@user-settings form', function (Browser $browser) { $browser->assertElementsCount('.row', 3) ->assertSeeIn('.row:first-child label', 'Greylisting') ->assertSeeIn('.row:first-child .text-danger', 'disabled'); }); }); } /** * Test editing an external email * * @depends testUserInfo2 */ public function testExternalEmail(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->waitFor('@user-info #external_email button') ->click('@user-info #external_email button') // Test dialog content, and closing it with Cancel button ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'External Email') ->assertFocused('@body input') ->assertValue('@body input', 'john.doe.external@gmail.com') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-cancel'); }) ->assertMissing('#email-dialog') ->click('@user-info #external_email button') // Test email validation error handling, and email update ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->type('@body input', 'test') ->click('@button-action') ->waitFor('@body input.is-invalid') ->assertSeeIn( '@body input + .invalid-feedback', 'The external email must be a valid email address.' ) ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->type('@body input', 'test@test.com') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User data updated successfully.') ->assertSeeIn('@user-info #external_email a', 'test@test.com') ->click('@user-info #external_email button') ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->assertValue('@body input', 'test@test.com') ->assertMissing('@body input.is-invalid') ->assertMissing('@body input + .invalid-feedback') ->click('@button-cancel'); }) ->assertSeeIn('@user-info #external_email a', 'test@test.com'); // $john->getSetting() may not work here as it uses internal cache // read the value form database $current_ext_email = $john->settings()->where('key', 'external_email')->first()->value; $this->assertSame('test@test.com', $current_ext_email); }); } /** * Test suspending/unsuspending the user */ public function testSuspendAndUnsuspend(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->assertVisible('@user-info #button-suspend') ->assertMissing('@user-info #button-unsuspend') ->click('@user-info #button-suspend') ->with(new Dialog('#suspend-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Suspend') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->type('textarea', 'test suspend') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User suspended successfully.') ->assertSeeIn('@user-info #status span.text-warning', 'Suspended') ->assertMissing('@user-info #button-suspend'); $event = EventLog::where('type', EventLog::TYPE_SUSPENDED)->first(); $this->assertSame('test suspend', $event->comment); $browser->click('@user-info #button-unsuspend') ->with(new Dialog('#suspend-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Unsuspend') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User unsuspended successfully.') ->assertSeeIn('@user-info #status span.text-success', 'Active') ->assertVisible('@user-info #button-suspend') ->assertMissing('@user-info #button-unsuspend'); $event = EventLog::where('type', EventLog::TYPE_UNSUSPENDED)->first(); $this->assertSame(null, $event->comment); }); } /** * Test the Resync button */ public function testResync(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->assertSeeIn('@user-info #button-resync', 'Resync') ->click('@user-info #button-resync') ->assertToast(Toast::TYPE_SUCCESS, "User synchronization has been started."); }); } /** * Test resetting 2FA for the user */ public function testReset2FA(): void { $this->browse(function (Browser $browser) { $user = $this->getTestUser('userstest1@kolabnow.com'); $sku2fa = Sku::withEnvTenantContext()->where('title', '2fa')->first(); $user->assignSku($sku2fa); $user->restrict(); SecondFactor::seed('userstest1@kolabnow.com'); $browser->visit(new UserPage($user->id)) ->assertSeeIn('@user-info #status', 'Restricted') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) use ($sku2fa) { $browser->waitFor('#reset2fa') ->assertVisible('#sku' . $sku2fa->id); }) ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)') ->click('#reset2fa') ->with(new Dialog('#reset-2fa-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', '2-Factor Authentication Reset') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Reset') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, '2-Factor authentication reset successfully.') ->assertMissing('#sku' . $sku2fa->id) ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)'); }); } /** * Test resetting Geo-Lock for the user */ public function testResetGeoLock(): void { $this->browse(function (Browser $browser) { $user = $this->getTestUser('userstest1@kolabnow.com'); $user->setSetting('limit_geo', '["PL","DE"]'); $browser->visit(new UserPage($user->id)) ->click('@nav #tab-settings') ->whenAvailable('@user-settings form', function (Browser $browser) { $browser->assertSeeIn('.row:nth-child(3) label', 'Geo-lockin') ->assertSeeIn('.row:nth-child(3) #limit_geo', 'Poland, Germany') ->assertSeeIn('#limit_geo + button', 'Reset') ->click('#limit_geo + button'); }) ->assertToast(Toast::TYPE_SUCCESS, 'Geo-lockin setup reset successfully.') ->assertMissing('#limit_geo + button'); }); } /** * Test adding the beta SKU for the user */ public function testAddBetaSku(): void { $this->browse(function (Browser $browser) { $user = $this->getTestUser('userstest1@kolabnow.com'); $sku = Sku::withEnvTenantContext()->where('title', 'beta')->first(); $browser->visit(new UserPage($user->id)) ->click('@nav #tab-subscriptions') ->waitFor('@user-subscriptions #addbetasku') ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)') ->assertSeeIn('#addbetasku', 'Enable beta program') ->click('#addbetasku') ->assertToast(Toast::TYPE_SUCCESS, 'The subscription added successfully.') ->waitFor('#sku' . $sku->id) ->assertSeeIn("#sku{$sku->id} td:first-child", 'Private Beta (invitation only)') ->assertSeeIn("#sku{$sku->id} td:last-child", '0,00 CHF/month') ->assertMissing('#addbetasku') ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)'); }); } } diff --git a/src/tests/Browser/DistlistTest.php b/src/tests/Browser/DistlistTest.php index 6dadf380..b2a52dbe 100644 --- a/src/tests/Browser/DistlistTest.php +++ b/src/tests/Browser/DistlistTest.php @@ -1,310 +1,314 @@ deleteTestGroup('group-test@kolab.org'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestGroup('group-test@kolab.org'); parent::tearDown(); } /** * Test distlist info page (unauthenticated) */ public function testInfoUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/distlist/abc')->on(new Home()); }); } /** * Test distlist list page (unauthenticated) */ public function testListUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/distlists')->on(new Home()); }); } /** * Test distlist list page */ public function testList(): void { // Create a single group $john = $this->getTestUser('john@kolab.org'); $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']); $group->assignToWallet($john->wallets->first()); // Log on the user $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) ->on(new Dashboard()) ->assertSeeIn('@links .link-distlists', 'Distribution lists') ->click('@links .link-distlists') ->on(new DistlistList()) ->whenAvailable('@table', function (Browser $browser) { $browser->waitFor('tbody tr') ->assertSeeIn('thead tr th:nth-child(1)', 'Name') ->assertSeeIn('thead tr th:nth-child(2)', 'Email') ->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(1) a', 'Test Group') ->assertText('tbody tr:nth-child(1) td:nth-child(1) svg.text-danger title', 'Not Ready') ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(2) a', 'group-test@kolab.org') ->assertMissing('tfoot'); }); }); } /** * Test distlist creation/editing/deleting * * @depends testList */ public function testCreateUpdateDelete(): void { $this->browse(function (Browser $browser) { // Create a group $browser->visit(new DistlistList()) ->assertSeeIn('button.distlist-new', 'Create list') ->click('button.distlist-new') ->on(new DistlistInfo()) ->assertSeeIn('#distlist-info .card-title', 'New distribution list') ->assertSeeIn('@nav #tab-general', 'General') ->assertMissing('@nav #tab-settings') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertMissing('#status') ->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Name') ->assertValue('div.row:nth-child(1) input[type=text]', '') ->assertSeeIn('div.row:nth-child(2) label', 'Email') ->assertValue('div.row:nth-child(2) input[type=text]', '') ->assertSeeIn('div.row:nth-child(3) label', 'Recipients') ->assertVisible('div.row:nth-child(3) .list-input') ->with(new ListInput('#members'), function (Browser $browser) { $browser->assertListInputValue([]) ->assertValue('@input', ''); }) ->assertSeeIn('div.row:nth-child(4) label', 'Subscriptions') ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Distribution list') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,00 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'Mail distribution list' ); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error conditions ->type('#name', str_repeat('A', 192)) ->type('#email', 'group-test@kolabnow.com') ->click('@general button[type=submit]') ->waitFor('#members + .invalid-feedback') ->assertSeeIn('#email + .invalid-feedback', 'The specified domain is not available.') ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.') ->assertSeeIn('#members + .invalid-feedback', 'At least one recipient is required.') ->assertFocused('#name') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful group creation ->type('#name', 'Test Group') ->type('#email', 'group-test@kolab.org') ->with(new ListInput('#members'), function (Browser $browser) { $browser->addListEntry('test1@gmail.com') ->addListEntry('test2@gmail.com'); }) ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Distribution list created successfully.') ->on(new DistlistList()) ->assertElementsCount('@table tbody tr', 1); // Test group update $browser->click('@table tr:nth-child(1) td:first-child a') ->on(new DistlistInfo()) ->assertSeeIn('#distlist-info .card-title', 'Distribution list') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Status') ->assertSeeIn('div.row:nth-child(1) span.text-danger', 'Not Ready') ->assertSeeIn('div.row:nth-child(2) label', 'Name') ->assertValue('div.row:nth-child(2) input[type=text]', 'Test Group') ->assertSeeIn('div.row:nth-child(3) label', 'Email') ->assertValue('div.row:nth-child(3) input[type=text]:disabled', 'group-test@kolab.org') ->assertSeeIn('div.row:nth-child(4) label', 'Recipients') ->assertVisible('div.row:nth-child(4) .list-input') ->with(new ListInput('#members'), function (Browser $browser) { $browser->assertListInputValue(['test1@gmail.com', 'test2@gmail.com']) ->assertValue('@input', ''); }) ->assertSeeIn('div.row:nth-child(5) label', 'Subscriptions') ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Distribution list') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,00 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'Mail distribution list' ); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error handling ->with(new ListInput('#members'), function (Browser $browser) { $browser->addListEntry('invalid address'); }) ->click('@general button[type=submit]') ->waitFor('#members + .invalid-feedback') ->assertSeeIn('#members + .invalid-feedback', 'The specified email address is invalid.') ->assertVisible('#members .input-group:nth-child(4) input.is-invalid') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful update ->with(new ListInput('#members'), function (Browser $browser) { $browser->removeListEntry(3)->removeListEntry(2); }) ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Distribution list updated successfully.') ->assertMissing('.invalid-feedback') ->on(new DistlistList()) ->assertElementsCount('@table tbody tr', 1); $group = Group::where('email', 'group-test@kolab.org')->first(); $this->assertSame(['test1@gmail.com'], $group->members); // Test group deletion $browser->click('@table tr:nth-child(1) td:first-child a') ->on(new DistlistInfo()) ->assertSeeIn('button.button-delete', 'Delete list') ->click('button.button-delete') ->assertToast(Toast::TYPE_SUCCESS, 'Distribution list deleted successfully.') ->on(new DistlistList()) ->assertElementsCount('@table tbody tr', 0) ->assertVisible('@table tfoot'); $this->assertNull(Group::where('email', 'group-test@kolab.org')->first()); }); } /** * Test distribution list status * * @depends testList */ public function testStatus(): void { + if (!\config('app.with_ldap')) { + $this->markTestSkipped(); + } + $john = $this->getTestUser('john@kolab.org'); $group = $this->getTestGroup('group-test@kolab.org'); $group->assignToWallet($john->wallets->first()); $group->status = Group::STATUS_NEW | Group::STATUS_ACTIVE; $group->save(); $this->assertFalse($group->isLdapReady()); $this->browse(function ($browser) use ($group) { // Test auto-refresh $browser->visit('/distlist/' . $group->id) ->on(new DistlistInfo()) ->with(new Status(), function ($browser) { $browser->assertSeeIn('@body', 'We are preparing the distribution list') ->assertProgress(83, 'Creating a distribution list...', 'pending') ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-link') ->assertMissing('#status-verify'); }); $group->status |= Group::STATUS_LDAP_READY; $group->save(); // Test Verify button $browser->waitUntilMissing('@status', 10); }); // TODO: Test all group statuses on the list } /** * Test distribution list settings */ public function testSettings(): void { $john = $this->getTestUser('john@kolab.org'); $group = $this->getTestGroup('group-test@kolab.org'); $group->assignToWallet($john->wallets->first()); $group->status = Group::STATUS_NEW | Group::STATUS_ACTIVE; $group->save(); $this->browse(function ($browser) use ($group) { // Test auto-refresh $browser->visit('/distlist/' . $group->id) ->on(new DistlistInfo()) ->assertSeeIn('@nav #tab-general', 'General') ->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->with('@settings form', function (Browser $browser) { // Assert form content $browser->assertSeeIn('div.row:nth-child(1) label', 'Sender Access List') ->assertVisible('div.row:nth-child(1) .list-input') ->with(new ListInput('#sender-policy'), function (Browser $browser) { $browser->assertListInputValue([]) ->assertValue('@input', ''); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error handling ->with(new ListInput('#sender-policy'), function (Browser $browser) { $browser->addListEntry('test.com'); }) ->click('@settings button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Distribution list settings updated successfully.') ->assertMissing('.invalid-feedback') ->refresh() ->on(new DistlistInfo()) ->click('@nav #tab-settings') ->with('@settings form', function (Browser $browser) { $browser->with(new ListInput('#sender-policy'), function (Browser $browser) { $browser->assertListInputValue(['test.com']) ->assertValue('@input', ''); }); }); }); } } diff --git a/src/tests/Browser/Reseller/UserTest.php b/src/tests/Browser/Reseller/UserTest.php index 0f29bce7..489389db 100644 --- a/src/tests/Browser/Reseller/UserTest.php +++ b/src/tests/Browser/Reseller/UserTest.php @@ -1,617 +1,617 @@ getTestUser('john@kolab.org'); $john->setSettings([ 'phone' => '+48123123123', 'external_email' => 'john.doe.external@gmail.com', ]); if ($john->isSuspended()) { User::where('email', $john->email)->update(['status' => $john->status - User::STATUS_SUSPENDED]); } $wallet = $john->wallets()->first(); $wallet->discount()->dissociate(); $wallet->save(); $this->deleteTestGroup('group-test@kolab.org'); $this->deleteTestUser('userstest1@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $john = $this->getTestUser('john@kolab.org'); $john->setSettings([ 'phone' => null, 'external_email' => 'john.doe.external@gmail.com', ]); if ($john->isSuspended()) { User::where('email', $john->email)->update(['status' => $john->status - User::STATUS_SUSPENDED]); } $wallet = $john->wallets()->first(); $wallet->discount()->dissociate(); $wallet->save(); $this->deleteTestGroup('group-test@kolab.org'); $this->deleteTestUser('userstest1@kolabnow.com'); parent::tearDown(); } /** * Test user info page (unauthenticated) */ public function testUserUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $jack = $this->getTestUser('jack@kolab.org'); $browser->visit('/user/' . $jack->id)->on(new Home()); }); } /** * Test user info page */ public function testUserInfo(): void { $this->browse(function (Browser $browser) { $jack = $this->getTestUser('jack@kolab.org'); $jack->setSettings([ 'limit_geo' => null, 'organization' => null, ]); $page = new UserPage($jack->id); $browser->visit(new Home()) ->submitLogon('reseller@' . \config('app.domain'), \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->visit($page) ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $jack->email) ->with('@user-info form', function (Browser $browser) use ($jack) { $browser->assertElementsCount('.row', 7) ->assertSeeIn('.row:nth-child(1) label', 'Managed by') ->assertSeeIn('.row:nth-child(1) #manager a', 'john@kolab.org') ->assertSeeIn('.row:nth-child(2) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(2) #userid', "{$jack->id} ({$jack->created_at})") ->assertSeeIn('.row:nth-child(3) label', 'Status') ->assertSeeIn('.row:nth-child(3) #status span.text-success', 'Active') ->assertSeeIn('.row:nth-child(4) label', 'First Name') ->assertSeeIn('.row:nth-child(4) #first_name', 'Jack') ->assertSeeIn('.row:nth-child(5) label', 'Last Name') ->assertSeeIn('.row:nth-child(5) #last_name', 'Daniels') ->assertSeeIn('.row:nth-child(6) label', 'External Email') ->assertMissing('.row:nth-child(6) #external_email a') ->assertSeeIn('.row:nth-child(7) label', 'Country') ->assertSeeIn('.row:nth-child(7) #country', 'United States'); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (1)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 1) ->assertSeeIn('table tbody tr:first-child td:first-child', 'jack.daniels@kolab.org') ->assertMissing('table tfoot'); }); // Assert Subscriptions tab $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (3)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 3) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '5,00 CHF/month') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 5 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,90 CHF/month') ->assertMissing('table tfoot') ->assertMissing('#reset2fa'); }); // Assert Domains tab $browser->assertSeeIn('@nav #tab-domains', 'Domains (0)') ->click('@nav #tab-domains') ->with('@user-domains', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no domains in this account.'); }); // Assert Users tab $browser->assertSeeIn('@nav #tab-users', 'Users (0)') ->click('@nav #tab-users') ->with('@user-users', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no users in this account.'); }); // Assert Distribution lists tab $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (0)') ->click('@nav #tab-distlists') ->with('@user-distlists', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no distribution lists in this account.'); }); // Assert Resources tab $browser->assertSeeIn('@nav #tab-resources', 'Resources (0)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no resources in this account.'); }); // Assert Shared folders tab $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (0)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no shared folders in this account.'); }); // Assert Settings tab $browser->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->whenAvailable('@user-settings form', function (Browser $browser) { $browser->assertElementsCount('.row', 3) ->assertSeeIn('.row:first-child label', 'Greylisting') ->assertSeeIn('.row:first-child .text-success', 'enabled') ->assertSeeIn('.row:nth-child(2) label', 'IMAP proxy') ->assertSeeIn('.row:nth-child(2) .text-danger', 'disabled') ->assertSeeIn('.row:nth-child(3) label', 'Geo-lockin') ->assertSeeIn('.row:nth-child(3) #limit_geo', 'No restrictions') ->assertMissing('#limit_geo + button'); }); }); } /** * Test user info page (continue) * * @depends testUserInfo */ public function testUserInfo2(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $page = new UserPage($john->id); $discount = Discount::where('code', 'TEST')->first(); $wallet = $john->wallet(); $wallet->discount()->associate($discount); $wallet->debit(2010); $wallet->save(); $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']); $group->assignToWallet($john->wallets->first()); // Click the managed-by link on Jack's page $browser->click('@user-info #manager a') ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $john->email) ->with('@user-info form', function (Browser $browser) use ($john) { $ext_email = $john->getSetting('external_email'); $browser->assertElementsCount('.row', 9) ->assertSeeIn('.row:nth-child(1) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(1) #userid', "{$john->id} ({$john->created_at})") ->assertSeeIn('.row:nth-child(2) label', 'Status') ->assertSeeIn('.row:nth-child(2) #status span.text-success', 'Active') ->assertSeeIn('.row:nth-child(3) label', 'First Name') ->assertSeeIn('.row:nth-child(3) #first_name', 'John') ->assertSeeIn('.row:nth-child(4) label', 'Last Name') ->assertSeeIn('.row:nth-child(4) #last_name', 'Doe') ->assertSeeIn('.row:nth-child(5) label', 'Organization') ->assertSeeIn('.row:nth-child(5) #organization', 'Kolab Developers') ->assertSeeIn('.row:nth-child(6) label', 'Phone') ->assertSeeIn('.row:nth-child(6) #phone', $john->getSetting('phone')) ->assertSeeIn('.row:nth-child(7) label', 'External Email') ->assertSeeIn('.row:nth-child(7) #external_email a', $ext_email) ->assertAttribute('.row:nth-child(7) #external_email a', 'href', "mailto:$ext_email") ->assertSeeIn('.row:nth-child(8) label', 'Address') ->assertSeeIn('.row:nth-child(8) #billing_address', $john->getSetting('billing_address')) ->assertSeeIn('.row:nth-child(9) label', 'Country') ->assertSeeIn('.row:nth-child(9) #country', 'United States'); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (1)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 1) ->assertSeeIn('table tbody tr:first-child td:first-child', 'john.doe@kolab.org') ->assertMissing('table tfoot'); }); // Assert Subscriptions tab $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (3)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 3) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '4,50 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 5 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,41 CHF/month¹') ->assertMissing('table tfoot') ->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher'); }); // Assert Users tab $browser->assertSeeIn('@nav #tab-users', 'Users (4)') ->click('@nav #tab-users') ->with('@user-users table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 4) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'jack@kolab.org') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(2) td:first-child a', 'joe@kolab.org') ->assertVisible('tbody tr:nth-child(2) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(3) td:first-child span', 'john@kolab.org') ->assertVisible('tbody tr:nth-child(3) td:first-child svg.text-success') ->assertSeeIn('tbody tr:nth-child(4) td:first-child a', 'ned@kolab.org') ->assertVisible('tbody tr:nth-child(4) td:first-child svg.text-success') ->assertMissing('tfoot'); }); // Assert Domains tab $browser->assertSeeIn('@nav #tab-domains', 'Domains (1)') ->click('@nav #tab-domains') ->with('@user-domains table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'kolab.org') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-success') ->assertMissing('tfoot'); }); // Assert Distribution lists tab $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (1)') ->click('@nav #tab-distlists') ->with('@user-distlists table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'Test Group') ->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-danger') ->assertSeeIn('tbody tr:nth-child(1) td:last-child a', 'group-test@kolab.org') ->assertMissing('tfoot'); }); // Assert Resources tab $browser->assertSeeIn('@nav #tab-resources', 'Resources (2)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 2) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'Conference Room #1') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', 'resource-test1@kolab.org') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Conference Room #2') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', 'resource-test2@kolab.org') ->assertMissing('table tfoot'); }); // Assert Shared folders tab $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (2)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 2) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'Calendar') ->assertSeeIn('table tbody tr:nth-child(1) td:nth-child(2)', 'Calendar') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', 'folder-event@kolab.org') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Contacts') ->assertSeeIn('table tbody tr:nth-child(2) td:nth-child(2)', 'Address Book') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', 'folder-contact@kolab.org') ->assertMissing('table tfoot'); }); // Assert History tab $browser->assertSeeIn('@nav #tab-history', 'History'); }); // Now we go to Ned's info page, he's a controller on John's wallet $this->browse(function (Browser $browser) { $ned = $this->getTestUser('ned@kolab.org'); $ned->setSetting('greylist_enabled', 'false'); $page = new UserPage($ned->id); $browser->click('@nav #tab-users') ->click('@user-users tbody tr:nth-child(4) td:first-child a') ->on($page); // Assert main info box content $browser->assertSeeIn('@user-info .card-title', $ned->email) ->with('@user-info form', function (Browser $browser) use ($ned) { $browser->assertSeeIn('.row:nth-child(2) label', 'ID (Created)') ->assertSeeIn('.row:nth-child(2) #userid', "{$ned->id} ({$ned->created_at})"); }); // Some tabs are loaded in background, wait a second $browser->pause(500) - ->assertElementsCount('@nav a', 10); + ->assertElementsCount('@nav a', 11); // Note: Finances tab is tested in UserFinancesTest.php $browser->assertSeeIn('@nav #tab-finances', 'Finances'); // Assert Aliases tab $browser->assertSeeIn('@nav #tab-aliases', 'Aliases (0)') ->click('@nav #tab-aliases') ->whenAvailable('@user-aliases', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'This user has no email aliases.'); }); // Assert Subscriptions tab, we expect John's discount here $browser->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (5)') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 5) ->assertSeeIn('table tbody tr:nth-child(1) td:first-child', 'User Mailbox') ->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '4,50 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(2) td:first-child', 'Storage Quota 5 GB') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(3) td:first-child', 'Groupware Features') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,41 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(4) td:first-child', 'Activesync') ->assertSeeIn('table tbody tr:nth-child(4) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(5) td:first-child', '2-Factor Authentication') ->assertSeeIn('table tbody tr:nth-child(5) td:last-child', '0,00 CHF/month¹') ->assertMissing('table tfoot') ->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher') ->assertSeeIn('#reset2fa', 'Reset 2-Factor Auth'); }); // We don't expect John's domains here $browser->assertSeeIn('@nav #tab-domains', 'Domains (0)') ->click('@nav #tab-domains') ->with('@user-domains', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no domains in this account.'); }); // We don't expect John's users here $browser->assertSeeIn('@nav #tab-users', 'Users (0)') ->click('@nav #tab-users') ->with('@user-users', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no users in this account.'); }); // We don't expect John's distribution lists here $browser->assertSeeIn('@nav #tab-distlists', 'Distribution lists (0)') ->click('@nav #tab-distlists') ->with('@user-distlists', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no distribution lists in this account.'); }); // Assert Resources tab $browser->assertSeeIn('@nav #tab-resources', 'Resources (0)') ->click('@nav #tab-resources') ->with('@user-resources', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no resources in this account.'); }); // Assert Shared folders tab $browser->assertSeeIn('@nav #tab-folders', 'Shared folders (0)') ->click('@nav #tab-folders') ->with('@user-folders', function (Browser $browser) { $browser->assertElementsCount('table tbody tr', 0) ->assertSeeIn('table tfoot tr td', 'There are no shared folders in this account.'); }); // Assert Settings tab $browser->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->whenAvailable('@user-settings form', function (Browser $browser) { $browser->assertElementsCount('.row', 3) ->assertSeeIn('.row:nth-child(1) label', 'Greylisting') ->assertSeeIn('.row:nth-child(1) .text-danger', 'disabled') ->assertSeeIn('.row:nth-child(2) label', 'IMAP proxy') ->assertSeeIn('.row:nth-child(2) .text-danger', 'disabled') ->assertSeeIn('.row:nth-child(3) label', 'Geo-lockin') ->assertSeeIn('.row:nth-child(3) #limit_geo', 'No restrictions'); }); // Assert History tab $browser->assertSeeIn('@nav #tab-history', 'History'); }); } /** * Test editing an external email * * @depends testUserInfo2 */ public function testExternalEmail(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->waitFor('@user-info #external_email button') ->click('@user-info #external_email button') // Test dialog content, and closing it with Cancel button ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'External Email') ->assertFocused('@body input') ->assertValue('@body input', 'john.doe.external@gmail.com') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-cancel'); }) ->assertMissing('#email-dialog') ->click('@user-info #external_email button') // Test email validation error handling, and email update ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->type('@body input', 'test') ->click('@button-action') ->waitFor('@body input.is-invalid') ->assertSeeIn( '@body input + .invalid-feedback', 'The external email must be a valid email address.' ) ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->type('@body input', 'test@test.com') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User data updated successfully.') ->assertSeeIn('@user-info #external_email a', 'test@test.com') ->click('@user-info #external_email button') ->with(new Dialog('#email-dialog'), function (Browser $browser) { $browser->assertValue('@body input', 'test@test.com') ->assertMissing('@body input.is-invalid') ->assertMissing('@body input + .invalid-feedback') ->click('@button-cancel'); }) ->assertSeeIn('@user-info #external_email a', 'test@test.com'); // $john->getSetting() may not work here as it uses internal cache // read the value form database $current_ext_email = $john->settings()->where('key', 'external_email')->first()->value; $this->assertSame('test@test.com', $current_ext_email); }); } /** * Test suspending/unsuspending the user */ public function testSuspendAndUnsuspend(): void { EventLog::query()->delete(); $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->assertVisible('@user-info #button-suspend') ->assertMissing('@user-info #button-unsuspend') ->click('@user-info #button-suspend') ->with(new Dialog('#suspend-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Suspend') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->type('textarea', 'test suspend') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User suspended successfully.') ->assertSeeIn('@user-info #status span.text-warning', 'Suspended') ->assertMissing('@user-info #button-suspend'); $event = EventLog::where('type', EventLog::TYPE_SUSPENDED)->first(); $this->assertSame('test suspend', $event->comment); $browser->click('@user-info #button-unsuspend') ->with(new Dialog('#suspend-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Unsuspend') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User unsuspended successfully.') ->assertSeeIn('@user-info #status span.text-success', 'Active') ->assertVisible('@user-info #button-suspend') ->assertMissing('@user-info #button-unsuspend'); $event = EventLog::where('type', EventLog::TYPE_UNSUSPENDED)->first(); $this->assertSame(null, $event->comment); }); } /** * Test resetting 2FA for the user */ public function testReset2FA(): void { $this->browse(function (Browser $browser) { $user = $this->getTestUser('userstest1@kolabnow.com'); $sku2fa = Sku::withEnvTenantContext()->where(['title' => '2fa'])->first(); $user->assignSku($sku2fa); SecondFactor::seed('userstest1@kolabnow.com'); $browser->visit(new UserPage($user->id)) ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) use ($sku2fa) { $browser->waitFor('#reset2fa') ->assertVisible('#sku' . $sku2fa->id); }) ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)') ->click('#reset2fa') ->with(new Dialog('#reset-2fa-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', '2-Factor Authentication Reset') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Reset') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, '2-Factor authentication reset successfully.') ->assertMissing('#sku' . $sku2fa->id) ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)'); }); } /** * Test adding the beta SKU for the user */ public function testAddBetaSku(): void { $this->browse(function (Browser $browser) { $user = $this->getTestUser('userstest1@kolabnow.com'); $sku = Sku::withEnvTenantContext()->where('title', 'beta')->first(); $browser->visit(new UserPage($user->id)) ->click('@nav #tab-subscriptions') ->waitFor('@user-subscriptions #addbetasku') ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (0)') ->assertSeeIn('#addbetasku', 'Enable beta program') ->click('#addbetasku') ->assertToast(Toast::TYPE_SUCCESS, 'The subscription added successfully.') ->waitFor('#sku' . $sku->id) ->assertSeeIn("#sku{$sku->id} td:first-child", 'Private Beta (invitation only)') ->assertSeeIn("#sku{$sku->id} td:last-child", '0,00 CHF/month') ->assertMissing('#addbetasku') ->assertSeeIn('@nav #tab-subscriptions', 'Subscriptions (1)'); }); } } diff --git a/src/tests/Browser/Reseller/WalletTest.php b/src/tests/Browser/Reseller/WalletTest.php index ecf30fd4..fe1326fb 100644 --- a/src/tests/Browser/Reseller/WalletTest.php +++ b/src/tests/Browser/Reseller/WalletTest.php @@ -1,253 +1,249 @@ getTestUser('reseller@' . \config('app.domain')); $wallet = $reseller->wallets()->first(); $wallet->balance = 0; $wallet->save(); $wallet->payments()->delete(); $wallet->transactions()->delete(); parent::tearDown(); } /** * Test wallet page (unauthenticated) */ public function testWalletUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/wallet')->on(new Home()); }); } /** * Test wallet "box" on Dashboard */ public function testDashboard(): void { $reseller = $this->getTestUser('reseller@' . \config('app.domain')); Wallet::where('user_id', $reseller->id)->update(['balance' => 125]); // Positive balance $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('reseller@' . \config('app.domain'), \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->assertSeeIn('@links .link-wallet svg + span', 'Wallet') ->assertSeeIn('@links .link-wallet .badge.bg-success', '1,25 CHF'); }); Wallet::where('user_id', $reseller->id)->update(['balance' => -1234]); // Negative balance $this->browse(function (Browser $browser) { $browser->visit(new Dashboard()) ->assertSeeIn('@links .link-wallet svg + span', 'Wallet') ->assertSeeIn('@links .link-wallet .badge.bg-danger', '-12,34 CHF'); }); } /** * Test wallet page * * @depends testDashboard */ public function testWallet(): void { $reseller = $this->getTestUser('reseller@' . \config('app.domain')); Wallet::where('user_id', $reseller->id)->update(['balance' => -1234]); $this->browse(function (Browser $browser) { $browser->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('#wallet .card-title', 'Account balance -12,34 CHF') ->assertSeeIn('#wallet .card-title .text-danger', '-12,34 CHF') ->assertSeeIn('#wallet .card-text', 'You are out of credit'); }); } /** * Test Receipts tab * * @depends testWallet */ public function testReceipts(): void { $user = $this->getTestUser('reseller@' . \config('app.domain')); $plan = \App\Plan::withObjectTenantContext($user)->where('title', 'individual')->first(); $wallet = $user->wallets()->first(); $wallet->payments()->delete(); $user->assignPlan($plan); $user->created_at = Carbon::now(); $user->save(); // Assert Receipts tab content when there's no receipts available $this->browse(function (Browser $browser) { $browser->visit(new WalletPage()) ->assertSeeIn('#wallet .card-title', 'Account balance 0,00 CHF') ->assertSeeIn('#wallet .card-title .text-success', '0,00 CHF') ->assertSeeIn('#wallet .card-text', 'You are in your free trial period.') // TODO ->assertSeeIn('@nav #tab-receipts', 'Receipts') ->with('@receipts-tab', function (Browser $browser) { $browser->waitUntilMissing('.app-loader') - ->assertSeeIn('p', 'There are no receipts for payments') - ->assertDontSeeIn('p', 'Here you can download') - ->assertMissing('select') - ->assertMissing('button'); + ->assertSeeIn('tfoot td', 'There are no receipts for payments'); }); }); // Create some sample payments $receipts = []; $date = Carbon::create(intval(date('Y')) - 1, 3, 30); $payment = Payment::create([ 'id' => 'AAA1', 'status' => Payment::STATUS_PAID, 'type' => Payment::TYPE_ONEOFF, 'description' => 'Paid in March', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, 'credit_amount' => 1111, 'currency_amount' => 1111, 'currency' => 'CHF', ]); $payment->updated_at = $date; $payment->save(); $receipts[] = $date->format('Y-m'); $date = Carbon::create(intval(date('Y')) - 1, 4, 30); $payment = Payment::create([ 'id' => 'AAA2', 'status' => Payment::STATUS_PAID, 'type' => Payment::TYPE_ONEOFF, 'description' => 'Paid in April', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, 'credit_amount' => 1111, 'currency_amount' => 1111, 'currency' => 'CHF', ]); $payment->updated_at = $date; $payment->save(); $receipts[] = $date->format('Y-m'); // Assert Receipts tab with receipts available $this->browse(function (Browser $browser) use ($receipts) { $browser->refresh() ->on(new WalletPage()) ->assertSeeIn('@nav #tab-receipts', 'Receipts') ->with('@receipts-tab', function (Browser $browser) use ($receipts) { $browser->waitUntilMissing('.app-loader') - ->assertDontSeeIn('p', 'There are no receipts for payments') - ->assertSeeIn('p', 'Here you can download') - ->assertSeeIn('button', 'Download') - ->assertElementsCount('select > option', 2) - ->assertSeeIn('select > option:nth-child(1)', $receipts[1]) - ->assertSeeIn('select > option:nth-child(2)', $receipts[0]); + ->assertMissing('tfoot') + ->assertElementsCount('table tbody tr', 2) + ->assertSeeIn('table tbody tr:nth-child(1) td.datetime', $receipts[1]) + ->assertSeeIn('table tbody tr:nth-child(2) td.datetime', $receipts[0]); // Download a receipt file - $browser->select('select', $receipts[0]) + $browser->click('table tbody tr:nth-child(2) td.selection button') + ->waitUntilMissing('.app-loader') ->click('button') ->pause(2000); $files = glob(__DIR__ . '/../downloads/*.pdf'); $filename = pathinfo($files[0], PATHINFO_BASENAME); $this->assertTrue(strpos($filename, $receipts[0]) !== false); $content = $browser->readDownloadedFile($filename, 0); $this->assertStringStartsWith("%PDF-1.", $content); $browser->removeDownloadedFile($filename); }); }); } /** * Test History tab * * @depends testWallet */ public function testHistory(): void { $user = $this->getTestUser('reseller@' . \config('app.domain')); $wallet = $user->wallets()->first(); $wallet->transactions()->delete(); // Create some sample transactions $transactions = $this->createTestTransactions($wallet); $transactions = array_reverse($transactions); $pages = array_chunk($transactions, 10 /* page size*/); $this->browse(function (Browser $browser) use ($pages) { $browser->on(new WalletPage()) ->assertSeeIn('@nav #tab-history', 'History') ->click('@nav #tab-history') ->with('@history-tab', function (Browser $browser) use ($pages) { $browser->waitUntilMissing('.app-loader') ->assertElementsCount('table tbody tr', 10) ->assertMissing('table td.email') ->assertSeeIn('.more-loader button', 'Load more'); foreach ($pages[0] as $idx => $transaction) { $selector = 'table tbody tr:nth-child(' . ($idx + 1) . ')'; $priceStyle = $transaction->type == Transaction::WALLET_AWARD ? 'text-success' : 'text-danger'; $browser->assertSeeIn("$selector td.description", $transaction->shortDescription()) ->assertMissing("$selector td.selection button") ->assertVisible("$selector td.price.{$priceStyle}"); // TODO: Test more transaction details } // Load the next page $browser->click('.more-loader button') ->waitUntilMissing('.app-loader') ->assertElementsCount('table tbody tr', 12) ->assertMissing('.more-loader button'); $debitEntry = null; foreach ($pages[1] as $idx => $transaction) { $selector = 'table tbody tr:nth-child(' . ($idx + 1 + 10) . ')'; $priceStyle = $transaction->type == Transaction::WALLET_CREDIT ? 'text-success' : 'text-danger'; $browser->assertSeeIn("$selector td.description", $transaction->shortDescription()); if ($transaction->type == Transaction::WALLET_DEBIT) { $debitEntry = $selector; } else { $browser->assertMissing("$selector td.selection button"); } } }); }); } } diff --git a/src/tests/Browser/ResourceTest.php b/src/tests/Browser/ResourceTest.php index ba852663..2c842154 100644 --- a/src/tests/Browser/ResourceTest.php +++ b/src/tests/Browser/ResourceTest.php @@ -1,346 +1,346 @@ clearBetaEntitlements(); Resource::whereNotIn('email', ['resource-test1@kolab.org', 'resource-test2@kolab.org'])->delete(); // Remove leftover entitlements that might interfere with the tests \App\Entitlement::where('entitleable_type', 'App\\Resource') ->whereRaw('entitleable_id not in (select id from resources where deleted_at is null)') ->forceDelete(); } /** * {@inheritDoc} */ public function tearDown(): void { \App\Sku::withEnvTenantContext()->where('title', 'resource')->update(['units_free' => 0]); Resource::whereNotIn('email', ['resource-test1@kolab.org', 'resource-test2@kolab.org'])->delete(); $this->clearBetaEntitlements(); parent::tearDown(); } /** * Test resource info page (unauthenticated) */ public function testInfoUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/resource/abc')->on(new Home()); }); } /** * Test resource list page (unauthenticated) */ public function testListUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/resources')->on(new Home()); }); } /** * Test resources list page */ public function testList(): void { // Log on the user $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) ->on(new Dashboard()) ->assertMissing('@links .link-resources'); }); // Test that Resources lists page is not accessible without the 'beta' entitlement $this->browse(function (Browser $browser) { $browser->visit('/resources') ->assertErrorPage(403); }); // Add beta entitlements $john = $this->getTestUser('john@kolab.org'); $this->addBetaEntitlement($john); // Make sure the first resource is active $resource = $this->getTestResource('resource-test1@kolab.org'); $resource->status = Resource::STATUS_NEW | Resource::STATUS_ACTIVE | Resource::STATUS_LDAP_READY | Resource::STATUS_IMAP_READY; $resource->save(); // Test resources lists page $this->browse(function (Browser $browser) { $browser->visit(new Dashboard()) ->assertSeeIn('@links .link-resources', 'Resources') ->click('@links .link-resources') ->on(new ResourceList()) ->whenAvailable('@table', function (Browser $browser) { $browser->waitFor('tbody tr') ->assertSeeIn('thead tr th:nth-child(1)', 'Name') ->assertSeeIn('thead tr th:nth-child(2)', 'Email Address') ->assertElementsCount('tbody tr', 2) ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(1) a', 'Conference Room #1') ->assertText('tbody tr:nth-child(1) td:nth-child(1) svg.text-success title', 'Active') ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(2) a', 'kolab.org') ->assertMissing('tfoot'); }); }); } /** * Test resource creation/editing/deleting * * @depends testList */ public function testCreateUpdateDelete(): void { // Test that the page is not available accessible without the 'beta' entitlement $this->browse(function (Browser $browser) { $browser->visit('/resource/new') ->assertErrorPage(403); }); // Add beta entitlement $john = $this->getTestUser('john@kolab.org'); $this->addBetaEntitlement($john); \App\Sku::withEnvTenantContext()->where('title', 'resource')->update(['units_free' => 3]); $this->browse(function (Browser $browser) { // Create a resource $browser->visit(new ResourceList()) ->assertSeeIn('button.resource-new', 'Create resource') ->click('button.resource-new') ->on(new ResourceInfo()) ->assertSeeIn('#resource-info .card-title', 'New resource') ->assertSeeIn('@nav #tab-general', 'General') ->assertMissing('@nav #tab-settings') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertMissing('#status') ->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Name') ->assertValue('div.row:nth-child(1) input[type=text]', '') ->assertSeeIn('div.row:nth-child(2) label', 'Domain') ->assertSelectHasOptions('div.row:nth-child(2) select', ['kolab.org']) ->assertValue('div.row:nth-child(2) select', 'kolab.org') ->assertSeeIn('div.row:nth-child(3) label', 'Subscriptions') ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Resource') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,00 CHF/month') // one free unit left ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'Reservation taker' ); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error conditions ->type('#name', str_repeat('A', 192)) ->click('@general button[type=submit]') ->waitFor('#name + .invalid-feedback') ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.') ->assertFocused('#name') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful resource creation ->type('#name', 'Test Resource') ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Resource created successfully.') ->on(new ResourceList()) ->assertElementsCount('@table tbody tr', 3); // Test resource update $browser->click('@table tr:nth-child(3) td:first-child a') ->on(new ResourceInfo()) ->assertSeeIn('#resource-info .card-title', 'Resource') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Status') ->assertSeeIn('div.row:nth-child(1) span.text-danger', 'Not Ready') ->assertSeeIn('div.row:nth-child(2) label', 'Name') ->assertValue('div.row:nth-child(2) input[type=text]', 'Test Resource') ->assertSeeIn('div.row:nth-child(3) label', 'Email') ->assertAttributeRegExp( 'div.row:nth-child(3) input[type=text]:disabled', 'value', '/^resource-[0-9]+@kolab\.org$/' ) ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Resource') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,00 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'Reservation taker' ); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error handling ->type('#name', str_repeat('A', 192)) ->click('@general button[type=submit]') ->waitFor('#name + .invalid-feedback') ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.') ->assertVisible('#name.is-invalid') ->assertFocused('#name') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful update ->type('#name', 'Test Resource Update') ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Resource updated successfully.') ->on(new ResourceList()) ->assertElementsCount('@table tbody tr', 3) ->assertSeeIn('@table tr:nth-child(3) td:first-child a', 'Test Resource Update'); $this->assertSame(1, Resource::where('name', 'Test Resource Update')->count()); // Test resource deletion $browser->click('@table tr:nth-child(3) td:first-child a') ->on(new ResourceInfo()) ->assertSeeIn('button.button-delete', 'Delete resource') ->click('button.button-delete') ->assertToast(Toast::TYPE_SUCCESS, 'Resource deleted successfully.') ->on(new ResourceList()) ->assertElementsCount('@table tbody tr', 2); $this->assertNull(Resource::where('name', 'Test Resource Update')->first()); }); // Assert Subscription price for the case when there's no free units \App\Sku::withEnvTenantContext()->where('title', 'resource')->update(['units_free' => 2]); $this->browse(function (Browser $browser) { $browser->visit('/resource/new') ->on(new ResourceInfo()) ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Resource') ->assertSeeIn('tbody tr:nth-child(1) td.price', '1,01 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input'); }); }); } /** * Test resource status * * @depends testList */ public function testStatus(): void { $john = $this->getTestUser('john@kolab.org'); $this->addBetaEntitlement($john); $resource = $this->getTestResource('resource-test2@kolab.org'); $resource->status = Resource::STATUS_NEW | Resource::STATUS_ACTIVE | Resource::STATUS_LDAP_READY; $resource->created_at = \now(); $resource->save(); $this->assertFalse($resource->isImapReady()); $this->browse(function ($browser) use ($resource) { // Test auto-refresh $browser->visit('/resource/' . $resource->id) ->on(new ResourceInfo()) ->with(new Status(), function ($browser) { $browser->assertSeeIn('@body', 'We are preparing the resource') - ->assertProgress(85, 'Creating a shared folder...', 'pending') + ->assertProgress(\config('app.with_ldap') ? 85 : 80, 'Creating a shared folder...', 'pending') ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-link') ->assertMissing('#status-verify'); }); $resource->status |= Resource::STATUS_IMAP_READY; $resource->save(); // Test Verify button $browser->waitUntilMissing('@status', 10); }); // TODO: Test all resource statuses on the list } /** * Test resource settings */ public function testSettings(): void { $john = $this->getTestUser('john@kolab.org'); $this->addBetaEntitlement($john); $resource = $this->getTestResource('resource-test2@kolab.org'); $resource->setSetting('invitation_policy', null); $this->browse(function ($browser) use ($resource) { // Test auto-refresh $browser->visit('/resource/' . $resource->id) ->on(new ResourceInfo()) ->assertSeeIn('@nav #tab-general', 'General') ->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->with('@settings form', function (Browser $browser) { // Assert form content $browser->assertSeeIn('div.row:nth-child(1) label', 'Invitation policy') ->assertSelectHasOptions('div.row:nth-child(1) select', ['accept', 'manual', 'reject']) ->assertValue('div.row:nth-child(1) select', 'accept') ->assertMissing('div.row:nth-child(1) input') ->assertSeeIn('div.row:nth-child(1) small', 'manual acceptance') ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error handling ->select('#invitation_policy', 'manual') ->waitFor('#invitation_policy + input') ->type('#invitation_policy + input', 'kolab.org') ->click('@settings button[type=submit]') ->waitFor('#invitation_policy + input + .invalid-feedback') ->assertSeeIn( '#invitation_policy + input + .invalid-feedback', 'The specified email address is invalid.' ) ->assertVisible('#invitation_policy + input.is-invalid') ->assertFocused('#invitation_policy + input') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->type('#invitation_policy + input', 'jack@kolab.org') ->click('@settings button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Resource settings updated successfully.') ->assertMissing('.invalid-feedback') ->refresh() ->on(new ResourceInfo()) ->click('@nav #tab-settings') ->with('@settings form', function (Browser $browser) { $browser->assertValue('div.row:nth-child(1) select', 'manual') ->assertVisible('div.row:nth-child(1) input') ->assertValue('div.row:nth-child(1) input', 'jack@kolab.org'); }); }); } } diff --git a/src/tests/Browser/SharedFolderTest.php b/src/tests/Browser/SharedFolderTest.php index 9b7da4c8..91cebe5c 100644 --- a/src/tests/Browser/SharedFolderTest.php +++ b/src/tests/Browser/SharedFolderTest.php @@ -1,389 +1,389 @@ delete(); } /** * {@inheritDoc} */ public function tearDown(): void { SharedFolder::whereNotIn('email', ['folder-event@kolab.org', 'folder-contact@kolab.org'])->delete(); parent::tearDown(); } /** * Test shared folder info page (unauthenticated) */ public function testInfoUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/shared-folder/abc')->on(new Home()); }); } /** * Test shared folder list page (unauthenticated) */ public function testListUnauth(): void { // Test that the page requires authentication $this->browse(function (Browser $browser) { $browser->visit('/shared-folders')->on(new Home()); }); } /** * Test shared folders list page */ public function testList(): void { // Make sure the first folder is active $folder = $this->getTestSharedFolder('folder-event@kolab.org'); $folder->status = SharedFolder::STATUS_NEW | SharedFolder::STATUS_ACTIVE | SharedFolder::STATUS_LDAP_READY | SharedFolder::STATUS_IMAP_READY; $folder->save(); // Log on the user $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) ->on(new Dashboard()) ->assertSeeIn('@links .link-shared-folders', 'Shared folders') ->click('@links .link-shared-folders') ->on(new SharedFolderList()) ->whenAvailable('@table', function (Browser $browser) { $browser->waitFor('tbody tr') ->assertElementsCount('thead th', 2) ->assertSeeIn('thead tr th:nth-child(1)', 'Name') ->assertSeeIn('thead tr th:nth-child(2)', 'Type') ->assertElementsCount('tbody tr', 2) ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(1) a', 'Calendar') ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(2)', 'Calendar') ->assertText('tbody tr:nth-child(1) td:nth-child(1) svg.text-success title', 'Active') ->assertSeeIn('tbody tr:nth-child(2) td:nth-child(1) a', 'Contacts') ->assertSeeIn('tbody tr:nth-child(2) td:nth-child(2)', 'Address Book') ->assertMissing('tfoot'); }); }); } /** * Test shared folder creation/editing/deleting * * @depends testList */ public function testCreateUpdateDelete(): void { $this->browse(function (Browser $browser) { // Create a folder $browser->visit(new SharedFolderList()) ->assertSeeIn('button.shared-folder-new', 'Create folder') ->click('button.shared-folder-new') ->on(new SharedFolderInfo()) ->assertSeeIn('#folder-info .card-title', 'New shared folder') ->assertSeeIn('@nav #tab-general', 'General') ->assertMissing('@nav #tab-settings') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertMissing('#status') ->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Name') ->assertValue('div.row:nth-child(1) input[type=text]', '') ->assertSeeIn('div.row:nth-child(2) label', 'Type') ->assertSelectHasOptions( 'div.row:nth-child(2) select', ['mail', 'event', 'task', 'contact', 'note', 'file'] ) ->assertValue('div.row:nth-child(2) select', 'mail') ->assertSeeIn('div.row:nth-child(3) label', 'Domain') ->assertSelectHasOptions('div.row:nth-child(3) select', ['kolab.org']) ->assertValue('div.row:nth-child(3) select', 'kolab.org') ->assertSeeIn('div.row:nth-child(4) label', 'Email Addresses') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->assertListInputValue([]) ->assertValue('@input', ''); }) ->assertSeeIn('div.row:nth-child(5) label', 'Subscriptions') ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Shared Folder') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,89 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'A shared folder' ); }) ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error conditions ->type('#name', str_repeat('A', 192)) ->select('#type', 'event') ->assertMissing('#aliases') ->click('@general button[type=submit]') ->waitFor('#name + .invalid-feedback') ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.') ->assertFocused('#name') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test error handling on aliases input ->type('#name', 'Test Folder') ->select('#type', 'mail') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->addListEntry('folder-alias@unknown'); }) ->click('@general button[type=submit]') ->assertMissing('#name + .invalid-feedback') ->waitFor('#aliases + .invalid-feedback') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->assertFormError(1, "The specified domain is invalid.", true); }) ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful folder creation ->select('#type', 'event') ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder created successfully.') ->on(new SharedFolderList()) ->assertElementsCount('@table tbody tr', 3); $this->assertSame(1, SharedFolder::where('name', 'Test Folder')->count()); $this->assertSame(0, SharedFolder::where('name', 'Test Folder')->first()->aliases()->count()); // Test folder update $browser->click('@table tr:nth-child(3) td:first-child a') ->on(new SharedFolderInfo()) ->assertSeeIn('#folder-info .card-title', 'Shared folder') ->with('@general', function (Browser $browser) { // Assert form content $browser->assertFocused('#name') ->assertSeeIn('div.row:nth-child(1) label', 'Status') ->assertSeeIn('div.row:nth-child(1) span.text-danger', 'Not Ready') ->assertSeeIn('div.row:nth-child(2) label', 'Name') ->assertValue('div.row:nth-child(2) input[type=text]', 'Test Folder') ->assertSeeIn('div.row:nth-child(3) label', 'Type') ->assertSelected('div.row:nth-child(3) select:disabled', 'event') ->assertMissing('#aliases') ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test error handling ->type('#name', str_repeat('A', 192)) ->click('@general button[type=submit]') ->waitFor('#name + .invalid-feedback') ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.') ->assertVisible('#name.is-invalid') ->assertFocused('#name') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful update ->type('#name', 'Test Folder Update') ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder updated successfully.') ->on(new SharedFolderList()) ->assertElementsCount('@table tbody tr', 3) ->assertSeeIn('@table tr:nth-child(3) td:first-child a', 'Test Folder Update'); $this->assertSame(1, SharedFolder::where('name', 'Test Folder Update')->count()); // Test folder deletion $browser->click('@table tr:nth-child(3) td:first-child a') ->on(new SharedFolderInfo()) ->assertSeeIn('button.button-delete', 'Delete folder') ->click('button.button-delete') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder deleted successfully.') ->on(new SharedFolderList()) ->assertElementsCount('@table tbody tr', 2); $this->assertNull(SharedFolder::where('name', 'Test Folder Update')->first()); }); // Test creation/updating a mail folder with mail aliases $this->browse(function (Browser $browser) { $browser->on(new SharedFolderList()) ->click('button.shared-folder-new') ->on(new SharedFolderInfo()) ->type('#name', 'Test Folder2') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->addListEntry('folder-alias1@kolab.org') ->addListEntry('folder-alias2@kolab.org'); }) ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder created successfully.') ->on(new SharedFolderList()) ->assertElementsCount('@table tbody tr', 3); $folder = SharedFolder::where('name', 'Test Folder2')->first(); $this->assertSame( ['folder-alias1@kolab.org', 'folder-alias2@kolab.org'], $folder->aliases()->pluck('alias')->all() ); // Test folder update $browser->click('@table tr:nth-child(3) td:first-child a') ->on(new SharedFolderInfo()) ->with('@general', function (Browser $browser) { // Assert form content $browser->assertFocused('#name') ->assertValue('div.row:nth-child(2) input[type=text]', 'Test Folder2') ->assertSelected('div.row:nth-child(3) select:disabled', 'mail') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->assertListInputValue(['folder-alias1@kolab.org', 'folder-alias2@kolab.org']) ->assertValue('@input', ''); }) ->with('@skus', function ($browser) { $browser->assertElementsCount('tbody tr', 1) ->assertSeeIn('tbody tr:nth-child(1) td.name', 'Shared Folder') ->assertSeeIn('tbody tr:nth-child(1) td.price', '0,89 CHF/month') ->assertChecked('tbody tr:nth-child(1) td.selection input') ->assertDisabled('tbody tr:nth-child(1) td.selection input') ->assertTip( 'tbody tr:nth-child(1) td.buttons button', 'A shared folder' ); }); }) // change folder name, and remove one alias ->type('#name', 'Test Folder Update2') ->with(new ListInput('#aliases'), function (Browser $browser) { $browser->removeListEntry(2); }) ->click('@general button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder updated successfully.'); $folder->refresh(); $this->assertSame('Test Folder Update2', $folder->name); $this->assertSame(['folder-alias1@kolab.org'], $folder->aliases()->pluck('alias')->all()); }); } /** * Test shared folder status * * @depends testList */ public function testStatus(): void { $folder = $this->getTestSharedFolder('folder-event@kolab.org'); $folder->status = SharedFolder::STATUS_NEW | SharedFolder::STATUS_ACTIVE | SharedFolder::STATUS_LDAP_READY; $folder->created_at = \now(); $folder->save(); $this->assertFalse($folder->isImapReady()); $this->browse(function ($browser) use ($folder) { // Test auto-refresh $browser->visit('/shared-folder/' . $folder->id) ->on(new SharedFolderInfo()) ->with(new Status(), function ($browser) { $browser->assertSeeIn('@body', 'We are preparing the shared folder') - ->assertProgress(85, 'Creating a shared folder...', 'pending') + ->assertProgress(\config('app.with_ldap') ? 85 : 80, 'Creating a shared folder...', 'pending') ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-link') ->assertMissing('#status-verify'); }); $folder->status |= SharedFolder::STATUS_IMAP_READY; $folder->save(); // Test Verify button $browser->waitUntilMissing('@status', 10); }); // TODO: Test all shared folder statuses on the list } /** * Test shared folder settings */ public function testSettings(): void { $folder = $this->getTestSharedFolder('folder-event@kolab.org'); $folder->setSetting('acl', null); $this->browse(function ($browser) use ($folder) { $aclInput = new AclInput('@settings #acl'); // Test auto-refresh $browser->visit('/shared-folder/' . $folder->id) ->on(new SharedFolderInfo()) ->assertSeeIn('@nav #tab-general', 'General') ->assertSeeIn('@nav #tab-settings', 'Settings') ->click('@nav #tab-settings') ->with('@settings form', function (Browser $browser) { // Assert form content $browser->assertSeeIn('div.row:nth-child(1) label', 'Access rights') ->assertSeeIn('div.row:nth-child(1) #acl-hint', 'permissions') ->assertSeeIn('button[type=submit]', 'Submit'); }) // Test the AclInput widget ->with($aclInput, function (Browser $browser) { $browser->assertAclValue([]) ->addAclEntry('anyone, read-only') ->addAclEntry('test, read-write') ->addAclEntry('john@kolab.org, full') ->assertAclValue([ 'anyone, read-only', 'test, read-write', 'john@kolab.org, full', ]); }) // Test error handling ->click('@settings button[type=submit]') ->with($aclInput, function (Browser $browser) { $browser->assertFormError(2, 'The specified email address is invalid.'); }) ->assertToast(Toast::TYPE_ERROR, 'Form validation error') // Test successful update ->with($aclInput, function (Browser $browser) { $browser->removeAclEntry(2) ->assertAclValue([ 'anyone, read-only', 'john@kolab.org, full', ]) ->updateAclEntry(2, 'jack@kolab.org, read-write') ->assertAclValue([ 'anyone, read-only', 'jack@kolab.org, read-write', ]); }) ->click('@settings button[type=submit]') ->assertToast(Toast::TYPE_SUCCESS, 'Shared folder settings updated successfully.') ->assertMissing('.invalid-feedback') // Refresh the page and check if everything was saved ->refresh() ->on(new SharedFolderInfo()) ->click('@nav #tab-settings') ->with($aclInput, function (Browser $browser) { $browser->assertAclValue([ 'anyone, read-only', 'jack@kolab.org, read-write', ]); }); }); } } diff --git a/src/tests/Browser/StatusTest.php b/src/tests/Browser/StatusTest.php index 660367e7..02636935 100644 --- a/src/tests/Browser/StatusTest.php +++ b/src/tests/Browser/StatusTest.php @@ -1,287 +1,299 @@ 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(71, 'Creating a mailbox...', 'pending') + ->assertProgress(\config('app.with_ldap') ? 71 : 60, 'Creating a mailbox...', 'pending') ->assertMissing('#status-confirm') ->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, 'Confirming an ownership of a custom domain...', 'failed') + ->assertProgress( + \config('app.with_ldap') ? 85 : 80, + 'Confirming an ownership of a custom domain...', + 'failed' + ) ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-confirm') ->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, 'Confirming an ownership of a custom domain...', 'failed'); + ->assertProgress( + \config('app.with_ldap') ? 85 : 80, + 'Confirming 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(71, 'Creating a mailbox...', 'failed') + ->assertProgress(\config('app.with_ldap') ? 71 : 60, 'Creating a mailbox...', 'failed') ->assertVisible('@refresh-button') ->assertVisible('@refresh-text'); $browser->click('@refresh-button') ->assertToast(Toast::TYPE_SUCCESS, 'Setup process has been pushed. Please wait.'); $john->status |= User::STATUS_IMAP_READY; $john->save(); $domain->status |= Domain::STATUS_CONFIRMED; $domain->save(); }) ->waitUntilMissing('@status', 10); }); } /** * 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(); // side-step $this->assertFalse($domain->isNew()); $this->assertTrue($domain->isActive()); $this->assertTrue($domain->isLdapReady()); $this->assertTrue($domain->isExternal()); $this->assertFalse($domain->isHosted()); $this->assertFalse($domain->isConfirmed()); $this->assertFalse($domain->isVerified()); $this->assertFalse($domain->isSuspended()); $this->assertFalse($domain->isDeleted()); $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') + ->assertProgress(\config('app.with_ldap') ? 50 : 33, 'Verifying a custom domain...', 'pending') ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-link') ->assertMissing('#status-confirm'); }); $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, 'Confirming an ownership of a custom domain...', 'failed') + ->assertProgress(\config('app.with_ldap') ? 75 : 66, 'Confirming an ownership of a custom domain...', 'failed') ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-link') ->assertVisible('#status-confirm'); }); $domain->status |= Domain::STATUS_CONFIRMED; $domain->save(); // Test Verify button $browser->click('@status #status-confirm') ->assertToast(Toast::TYPE_SUCCESS, 'Domain ownership confirmed successfully.') ->waitUntilMissing('@status') ->waitUntilMissing('#status-confirm') ->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('@general', 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(71, 'Creating a mailbox...', 'pending') + ->assertProgress(\config('app.with_ldap') ? 71 : 60, 'Creating a mailbox...', 'pending') ->assertMissing('#status-confirm') ->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, 'Confirming an ownership of a custom domain...', 'failed') + ->assertProgress( + \config('app.with_ldap') ? 85 : 80, + 'Confirming an ownership of a custom domain...', + 'failed' + ) ->assertMissing('@refresh-button') ->assertMissing('@refresh-text') ->assertMissing('#status-confirm') ->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); }); } }