diff --git a/src/tests/Browser/Admin/DashboardTest.php b/src/tests/Browser/Admin/DashboardTest.php index 1d52a591..9510d351 100644 --- a/src/tests/Browser/Admin/DashboardTest.php +++ b/src/tests/Browser/Admin/DashboardTest.php @@ -1,140 +1,155 @@ getTestUser('jack@kolab.org'); $jack->setSetting('external_email', null); $this->deleteTestUser('test@testsearch.com'); $this->deleteTestDomain('testsearch.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $jack = $this->getTestUser('jack@kolab.org'); $jack->setSetting('external_email', null); $this->deleteTestUser('test@testsearch.com'); $this->deleteTestDomain('testsearch.com'); parent::tearDown(); } /** * Test user search */ public function testSearch(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('jeroen@jeroen.jeroen', \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->assertFocused('@search input') ->assertMissing('@search table'); // Test search with no results $browser->type('@search input', 'unknown') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '0 user accounts have been found.') ->assertMissing('@search table'); $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $jack->setSetting('external_email', 'john.doe.external@gmail.com'); // Test search with multiple results $browser->type('@search input', 'john.doe.external@gmail.com') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '2 user accounts have been found.') ->whenAvailable('@search table', function (Browser $browser) use ($john, $jack) { $browser->assertElementsCount('tbody tr', 2) ->with('tbody tr:first-child', function (Browser $browser) use ($jack) { $browser->assertSeeIn('td:nth-child(1) a', $jack->email) - ->assertSeeIn('td:nth-child(2) a', $jack->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertText('td:nth-child(4)', ''); + ->assertSeeIn('td:nth-child(2) a', $jack->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertText('td:nth-child(4)', ''); + } }) ->with('tbody tr:last-child', function (Browser $browser) use ($john) { $browser->assertSeeIn('td:nth-child(1) a', $john->email) - ->assertSeeIn('td:nth-child(2) a', $john->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertText('td:nth-child(4)', ''); + ->assertSeeIn('td:nth-child(2) a', $john->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertText('td:nth-child(4)', ''); + } }); }); // Test search with single record result -> redirect to user page $browser->type('@search input', 'kolab.org') ->click('@search form button') ->assertMissing('@search table') ->waitForLocation('/user/' . $john->id) ->waitUntilMissing('.app-loader') ->whenAvailable('#user-info', function (Browser $browser) use ($john) { $browser->assertSeeIn('.card-title', $john->email); }); }); } /** * Test user search deleted user/domain */ public function testSearchDeleted(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('jeroen@jeroen.jeroen', \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->assertFocused('@search input') ->assertMissing('@search table'); // Deleted users/domains $domain = $this->getTestDomain('testsearch.com', ['type' => \App\Domain::TYPE_EXTERNAL]); $user = $this->getTestUser('test@testsearch.com'); $plan = \App\Plan::where('title', 'group')->first(); $user->assignPlan($plan, $domain); $user->setAliases(['alias@testsearch.com']); Queue::fake(); $user->delete(); // Test search with multiple results $browser->type('@search input', 'testsearch.com') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '1 user accounts have been found.') ->whenAvailable('@search table', function (Browser $browser) use ($user) { $browser->assertElementsCount('tbody tr', 1) ->assertVisible('tbody tr:first-child.text-secondary') ->with('tbody tr:first-child', function (Browser $browser) use ($user) { $browser->assertSeeIn('td:nth-child(1) span', $user->email) - ->assertSeeIn('td:nth-child(2) span', $user->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertTextRegExp('td:nth-child(4)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/'); + ->assertSeeIn('td:nth-child(2) span', $user->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertTextRegExp('td:nth-child(4)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/'); + } }); }); }); } } diff --git a/src/tests/Browser/LogonTest.php b/src/tests/Browser/LogonTest.php index 4b569a3e..6f7b6aef 100644 --- a/src/tests/Browser/LogonTest.php +++ b/src/tests/Browser/LogonTest.php @@ -1,285 +1,289 @@ browse(function (Browser $browser) { $browser->visit(new Home()) ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login', 'lang']) ->assertSeeIn('#footer-copyright', '@ Apheleia IT AG, ' . date('Y')); }); if ($browser->isDesktop()) { $browser->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'tos', 'login']); }); } else { $browser->assertMissing('#footer-menu .navbar-nav'); } $browser->assertSeeLink('Forgot password?') ->assertSeeLink('Webmail'); }); } /** * Test language menu, and language change */ public function testLocales(): void { $this->browse(function (Browser $browser) { + if (!$browser->isDesktop()) { + $this->markTestIncomplete(); + } + $browser->visit(new Home()) // ->plainCookie('language', '') ->within(new Menu(), function ($browser) { $browser->assertSeeIn('@lang', 'EN') ->click('@lang'); }) // Switch English -> German ->whenAvailable('nav .dropdown-menu', function (Browser $browser) { $browser->assertElementsCount('a', 2) ->assertSeeIn('a:nth-child(1)', 'EN - English') ->assertSeeIn('a:nth-child(2)', 'DE - German') ->click('a:nth-child(2)'); }) ->waitUntilMissing('nav .dropdown-menu') ->within(new Menu(), function ($browser) { $browser->assertSeeIn('@lang', 'DE'); }) ->waitForTextIn('#header-menu .link-login', 'EINLOGGEN') ->assertSeeIn('#footer-menu .link-login', 'Einloggen') ->assertSeeIn('@logon-button', 'Anmelden') // refresh the page to see if it uses the lang previously set ->refresh() ->waitForTextIn('#header-menu .link-login', 'EINLOGGEN') ->assertSeeIn('#footer-menu .link-login', 'Einloggen') ->assertSeeIn('@logon-button', 'Anmelden') ->within(new Menu(), function ($browser) { $browser->assertSeeIn('@lang', 'DE') ->click('@lang'); }) // Switch German -> English ->whenAvailable('nav .dropdown-menu', function (Browser $browser) { $browser->click('a:nth-child(1)'); }) ->waitUntilMissing('nav .dropdown-menu') ->within(new Menu(), function ($browser) { $browser->assertSeeIn('@lang', 'EN'); }) ->waitForTextIn('#header-menu .link-login', 'LOGIN'); }); } /** * Test redirect to /login if user is unauthenticated */ public function testRequiredAuth(): void { $this->browse(function (Browser $browser) { $browser->visit('/dashboard'); // Checks if we're really on the login page $browser->waitForLocation('/login') ->on(new Home()); }); } /** * Logon with wrong password/user test */ public function testLogonWrongCredentials(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'wrong'); // Error message $browser->assertToast(Toast::TYPE_ERROR, 'Invalid username or password.'); // Checks if we're still on the logon page $browser->on(new Home()); }); } /** * Successful logon test */ public function testLogonSuccessful(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true) // Checks if we're really on Dashboard page ->on(new Dashboard()) ->assertVisible('@links a.link-profile') ->assertVisible('@links a.link-domains') ->assertVisible('@links a.link-users') ->assertVisible('@links a.link-wallet') ->assertVisible('@links a.link-webmail') ->within(new Menu(), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'dashboard', 'logout', 'lang']); }); if ($browser->isDesktop()) { $browser->within(new Menu('footer'), function ($browser) { $browser->assertMenuItems(['explore', 'blog', 'support', 'tos', 'dashboard', 'logout']); }); } else { $browser->assertMissing('#footer-menu .navbar-nav'); } $browser->assertUser('john@kolab.org'); // Assert no "Account status" for this account $browser->assertMissing('@status'); // Goto /domains and assert that the link on logo element // leads to the dashboard $browser->visit('/domains') ->waitForText('Domains') ->click('a.navbar-brand') ->on(new Dashboard()); // Test that visiting '/' with logged in user does not open logon form // but "redirects" to the dashboard $browser->visit('/') ->waitForLocation('/dashboard') ->on(new Dashboard()); }); } /** * Logout test * * @depends testLogonSuccessful */ public function testLogout(): void { $this->browse(function (Browser $browser) { $browser->on(new Dashboard()); // Click the Logout button $browser->within(new Menu(), function ($browser) { $browser->clickMenuItem('logout'); }); // We expect the logon page $browser->waitForLocation('/login') ->on(new Home()); // with default menu $browser->within(new Menu(), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login', 'lang']); }); // Success toast message $browser->assertToast(Toast::TYPE_SUCCESS, 'Successfully logged out'); }); } /** * Logout by URL test */ public function testLogoutByURL(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'simple123', true); // Checks if we're really on Dashboard page $browser->on(new Dashboard()); // Use /logout url, and expect the logon page $browser->visit('/logout') ->waitForLocation('/login') ->on(new Home()); // with default menu $browser->within(new Menu(), function ($browser) { $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login', 'lang']); }); // Success toast message $browser->assertToast(Toast::TYPE_SUCCESS, 'Successfully logged out'); }); } /** * Test 2-Factor Authentication * * @depends testLogoutByURL */ public function test2FA(): void { $this->browse(function (Browser $browser) { // Test missing 2fa code $browser->on(new Home()) ->type('@email-input', 'ned@kolab.org') ->type('@password-input', 'simple123') ->press('form button') ->waitFor('@second-factor-input.is-invalid + .invalid-feedback') ->assertSeeIn( '@second-factor-input.is-invalid + .invalid-feedback', 'Second factor code is required.' ) ->assertFocused('@second-factor-input') ->assertToast(Toast::TYPE_ERROR, 'Form validation error'); // Test invalid code $browser->type('@second-factor-input', '123456') ->press('form button') ->waitUntilMissing('@second-factor-input.is-invalid') ->waitFor('@second-factor-input.is-invalid + .invalid-feedback') ->assertSeeIn( '@second-factor-input.is-invalid + .invalid-feedback', 'Second factor code is invalid.' ) ->assertFocused('@second-factor-input') ->assertToast(Toast::TYPE_ERROR, 'Form validation error'); $code = \App\Auth\SecondFactor::code('ned@kolab.org'); // Test valid (TOTP) code $browser->type('@second-factor-input', $code) ->press('form button') ->waitUntilMissing('@second-factor-input.is-invalid') ->waitForLocation('/dashboard') ->on(new Dashboard()); }); } /** * Test redirect to the requested page after logon * * @depends test2FA */ public function testAfterLogonRedirect(): void { $this->browse(function (Browser $browser) { // User is logged in $browser->visit(new UserProfile()); // Test redirect if the token is invalid $browser->script("localStorage.setItem('token', '123')"); $browser->refresh() ->on(new Home()) ->submitLogon('john@kolab.org', 'simple123', false) ->waitForLocation('/profile'); }); } } diff --git a/src/tests/Browser/Reseller/DashboardTest.php b/src/tests/Browser/Reseller/DashboardTest.php index c4e77a1e..cb27ebcd 100644 --- a/src/tests/Browser/Reseller/DashboardTest.php +++ b/src/tests/Browser/Reseller/DashboardTest.php @@ -1,140 +1,155 @@ getTestUser('jack@kolab.org'); $jack->setSetting('external_email', null); $this->deleteTestUser('test@testsearch.com'); $this->deleteTestDomain('testsearch.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $jack = $this->getTestUser('jack@kolab.org'); $jack->setSetting('external_email', null); $this->deleteTestUser('test@testsearch.com'); $this->deleteTestDomain('testsearch.com'); parent::tearDown(); } /** * Test user search */ public function testSearch(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('reseller@' . \config('app.domain'), \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->assertFocused('@search input') ->assertMissing('@search table'); // Test search with no results $browser->type('@search input', 'unknown') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '0 user accounts have been found.') ->assertMissing('@search table'); $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $jack->setSetting('external_email', 'john.doe.external@gmail.com'); // Test search with multiple results $browser->type('@search input', 'john.doe.external@gmail.com') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '2 user accounts have been found.') ->whenAvailable('@search table', function (Browser $browser) use ($john, $jack) { $browser->assertElementsCount('tbody tr', 2) ->with('tbody tr:first-child', function (Browser $browser) use ($jack) { $browser->assertSeeIn('td:nth-child(1) a', $jack->email) - ->assertSeeIn('td:nth-child(2) a', $jack->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertText('td:nth-child(4)', ''); + ->assertSeeIn('td:nth-child(2) a', $jack->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertText('td:nth-child(4)', ''); + } }) ->with('tbody tr:last-child', function (Browser $browser) use ($john) { $browser->assertSeeIn('td:nth-child(1) a', $john->email) - ->assertSeeIn('td:nth-child(2) a', $john->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertText('td:nth-child(4)', ''); + ->assertSeeIn('td:nth-child(2) a', $john->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertText('td:nth-child(4)', ''); + } }); }); // Test search with single record result -> redirect to user page $browser->type('@search input', 'kolab.org') ->click('@search form button') ->assertMissing('@search table') ->waitForLocation('/user/' . $john->id) ->waitUntilMissing('.app-loader') ->whenAvailable('#user-info', function (Browser $browser) use ($john) { $browser->assertSeeIn('.card-title', $john->email); }); }); } /** * Test user search deleted user/domain */ public function testSearchDeleted(): void { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('reseller@' . \config('app.domain'), \App\Utils::generatePassphrase(), true) ->on(new Dashboard()) ->assertFocused('@search input') ->assertMissing('@search table'); // Deleted users/domains $domain = $this->getTestDomain('testsearch.com', ['type' => \App\Domain::TYPE_EXTERNAL]); $user = $this->getTestUser('test@testsearch.com'); $plan = \App\Plan::where('title', 'group')->first(); $user->assignPlan($plan, $domain); $user->setAliases(['alias@testsearch.com']); Queue::fake(); $user->delete(); // Test search with multiple results $browser->type('@search input', 'testsearch.com') ->click('@search form button') ->assertToast(Toast::TYPE_INFO, '1 user accounts have been found.') ->whenAvailable('@search table', function (Browser $browser) use ($user) { $browser->assertElementsCount('tbody tr', 1) ->assertVisible('tbody tr:first-child.text-secondary') ->with('tbody tr:first-child', function (Browser $browser) use ($user) { $browser->assertSeeIn('td:nth-child(1) span', $user->email) - ->assertSeeIn('td:nth-child(2) span', $user->id) - ->assertVisible('td:nth-child(3)') - ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') - ->assertVisible('td:nth-child(4)') - ->assertTextRegExp('td:nth-child(4)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/'); + ->assertSeeIn('td:nth-child(2) span', $user->id); + + if ($browser->isPhone()) { + $browser->assertMissing('td:nth-child(3)'); + } else { + $browser->assertVisible('td:nth-child(3)') + ->assertTextRegExp('td:nth-child(3)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/') + ->assertVisible('td:nth-child(4)') + ->assertTextRegExp('td:nth-child(4)', '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/'); + } }); }); }); } }