diff --git a/src/resources/vue/components/Menu.vue b/src/resources/vue/components/Menu.vue index 0f07db17..026581cf 100644 --- a/src/resources/vue/components/Menu.vue +++ b/src/resources/vue/components/Menu.vue @@ -1,58 +1,58 @@ diff --git a/src/tests/Browser/Components/Menu.php b/src/tests/Browser/Components/Menu.php new file mode 100644 index 00000000..c83d4651 --- /dev/null +++ b/src/tests/Browser/Components/Menu.php @@ -0,0 +1,84 @@ +assertVisible($this->selector()); + $browser->assertVisible('@brand'); + } + + /** + * Assert that menu contains only specified menu items. + * + * @param Browser $browser + * @param array $items List of menu items + * + * @return void + */ + public function assertMenuItems(Browser $browser, array $items) + { + // TODO: On mobile the links will not be visible + + foreach ($items as $item) { + $browser->assertVisible('.link-' . $item); + } + + // Check number of items, to make sure there's no extra items + PHPUnit::assertCount(count($items), $browser->elements('li')); + } + + /** + * Assert that specified menu item is active + * + * @param Browser $browser + * @param string $item Menu item name + * + * @return void + */ + public function assertActiveItem(Browser $browser, string $item) + { + // TODO: On mobile the links will not be visible + + $browser->assertVisible(".link-{$item}.active"); + } + + /** + * Get the element shortcuts for the component. + * + * @return array + */ + public function elements() + { + $selector = $this->selector(); + + return [ + '@list' => "$selector .navbar-nav", + '@brand' => "$selector .navbar-brand", + '@toggler' => "$selector .navbar-toggler", + ]; + } +} diff --git a/src/tests/Browser/LogonTest.php b/src/tests/Browser/LogonTest.php index c21601ef..0f03b1d9 100644 --- a/src/tests/Browser/LogonTest.php +++ b/src/tests/Browser/LogonTest.php @@ -1,63 +1,83 @@ browse(function (Browser $browser) { + $browser->visit(new Home()); + $browser->within(new Menu(), function ($browser) { + $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'webmail']); + }); + }); + } + /** * Test redirect to /login if user is unauthenticated * * @return void */ public function testLogonRedirect() { $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 * * @return void */ public function testLogonWrongCredentials() { $this->browse(function (Browser $browser) { $browser->visit(new Home()) ->submitLogon('john@kolab.org', 'wrong'); // Checks if we're still on the logon page // FIXME: This assertion might be prone to timing issues // I guess we should wait until some error message appears $browser->on(new Home()); }); } /** * Successful logon test * * @return void */ public function testLogonSuccessful() { $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()); + + $browser->within(new Menu(), function ($browser) { + $browser->assertMenuItems(['support', 'contact', 'webmail', 'logout']); + }); }); } } diff --git a/src/tests/Browser/SignupTest.php b/src/tests/Browser/SignupTest.php index 9521692d..6cce96e2 100644 --- a/src/tests/Browser/SignupTest.php +++ b/src/tests/Browser/SignupTest.php @@ -1,313 +1,319 @@ delete(); parent::tearDown(); } /** * Test signup code verification with a link * * @return void */ public function testSignupCodeByLink() { // Test invalid code (invalid format) $this->browse(function (Browser $browser) { // Register Signup page element selectors we'll be using $browser->onWithoutAssert(new Signup()); // TODO: Test what happens if user is logged in $browser->visit('/signup/invalid-code'); // TODO: According to https://github.com/vuejs/vue-router/issues/977 // it is not yet easily possible to display error page component (route) // without changing the URL // TODO: Instead of css selector we should probably define page/component // and use it instead $browser->waitFor('#error-page'); }); // Test invalid code (valid format) $this->browse(function (Browser $browser) { $browser->visit('/signup/XXXXX-code'); // FIXME: User will not be able to continue anyway, so we should // either display 1st step or 404 error page $browser->waitFor('@step1'); $browser->waitFor('.toast-error'); $browser->click('.toast-error'); // remove the toast }); // Test valid code $this->browse(function (Browser $browser) { $code = SignupCode::create([ 'data' => [ 'email' => 'User@example.org', 'name' => 'User Name', ] ]); $browser->visit('/signup/' . $code->short_code . '-' . $code->code); $browser->waitFor('@step3'); $browser->assertMissing('@step1'); $browser->assertMissing('@step2'); // FIXME: Find a nice way to read javascript data without using hidden inputs $this->assertSame($code->code, $browser->value('@step2 #signup_code')); // TODO: Test if the signup process can be completed }); } /** * Test 1st step of the signup process * * @return void */ public function testSignupStep1() { $this->browse(function (Browser $browser) { $browser->visit(new Signup()); $browser->assertVisible('@step1'); + $browser->within(new Menu(), function ($browser) { + $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login']); + $browser->assertActiveItem('signup'); + }); + // Here we expect two text inputs and Continue $browser->with('@step1', function ($step) { $step->assertVisible('#signup_name'); $step->assertFocused('#signup_name'); $step->assertVisible('#signup_email'); $step->assertVisible('[type=submit]'); }); // Submit empty form // Both Step 1 inputs are required, so after pressing Submit // we expect focus to be moved to the first input $browser->with('@step1', function ($step) { $step->click('[type=submit]'); $step->assertFocused('#signup_name'); }); // Submit invalid email // We expect email input to have is-invalid class added, with .invalid-feedback element $browser->with('@step1', function ($step) use ($browser) { $step->type('#signup_name', 'Test User'); $step->type('#signup_email', '@test'); $step->click('[type=submit]'); $step->waitFor('#signup_email.is-invalid'); $step->waitFor('#signup_email + .invalid-feedback'); $browser->waitFor('.toast-error'); $browser->click('.toast-error'); // remove the toast }); // Submit valid data // We expect error state on email input to be removed, and Step 2 form visible $browser->with('@step1', function ($step) { $step->type('#signup_name', 'Test User'); $step->type('#signup_email', 'BrowserSignupTestUser1@kolab.org'); $step->click('[type=submit]'); $step->assertMissing('#signup_email.is-invalid'); $step->assertMissing('#signup_email + .invalid-feedback'); }); $browser->waitUntilMissing('@step2 #signup_code[value=""]'); $browser->waitFor('@step2'); $browser->assertMissing('@step1'); }); } /** * Test 2nd Step of the signup process * * @depends testSignupStep1 * @return void */ public function testSignupStep2() { $this->browse(function (Browser $browser) { $browser->assertVisible('@step2'); // Here we expect one text input, Back and Continue buttons $browser->with('@step2', function ($step) { $step->assertVisible('#signup_short_code'); $step->assertFocused('#signup_short_code'); $step->assertVisible('[type=button]'); $step->assertVisible('[type=submit]'); }); // Test Back button functionality $browser->click('@step2 [type=button]'); $browser->waitFor('@step1'); $browser->assertFocused('@step1 #signup_name'); $browser->assertMissing('@step2'); // Submit valid Step 1 data (again) $browser->with('@step1', function ($step) { $step->type('#signup_name', 'Test User'); $step->type('#signup_email', 'BrowserSignupTestUser1@kolab.org'); $step->click('[type=submit]'); }); $browser->waitFor('@step2'); $browser->assertMissing('@step1'); // Submit invalid code // We expect code input to have is-invalid class added, with .invalid-feedback element $browser->with('@step2', function ($step) use ($browser) { $step->type('#signup_short_code', 'XXXXX'); $step->click('[type=submit]'); $browser->waitFor('.toast-error'); $step->assertVisible('#signup_short_code.is-invalid'); $step->assertVisible('#signup_short_code + .invalid-feedback'); $step->assertFocused('#signup_short_code'); $browser->click('.toast-error'); // remove the toast }); // Submit valid code // We expect error state on code input to be removed, and Step 3 form visible $browser->with('@step2', function ($step) { // Get the code and short_code from database // FIXME: Find a nice way to read javascript data without using hidden inputs $code = $step->value('#signup_code'); $this->assertNotEmpty($code); $code = SignupCode::find($code); $step->type('#signup_short_code', $code->short_code); $step->click('[type=submit]'); $step->assertMissing('#signup_short_code.is-invalid'); $step->assertMissing('#signup_short_code + .invalid-feedback'); }); $browser->waitFor('@step3'); $browser->assertMissing('@step2'); }); } /** * Test 3rd Step of the signup process * * @depends testSignupStep2 * @return void */ public function testSignupStep3() { $this->browse(function (Browser $browser) { $browser->assertVisible('@step3'); // Here we expect 3 text inputs, Back and Continue buttons $browser->with('@step3', function ($step) { $step->assertVisible('#signup_login'); $step->assertVisible('#signup_password'); $step->assertVisible('#signup_confirm'); $step->assertVisible('[type=button]'); $step->assertVisible('[type=submit]'); $step->assertFocused('#signup_login'); $step->assertSeeIn('#signup_login + span', '@' . \config('app.domain')); }); // Test Back button $browser->click('@step3 [type=button]'); $browser->waitFor('@step2'); $browser->assertFocused('@step2 #signup_short_code'); $browser->assertMissing('@step3'); // TODO: Test form reset when going back // Submit valid code again $browser->with('@step2', function ($step) { $code = $step->value('#signup_code'); $this->assertNotEmpty($code); $code = SignupCode::find($code); $step->type('#signup_short_code', $code->short_code); $step->click('[type=submit]'); }); $browser->waitFor('@step3'); // Submit invalid data $browser->with('@step3', function ($step) use ($browser) { $step->assertFocused('#signup_login'); $step->type('#signup_login', '*'); $step->type('#signup_password', '12345678'); $step->type('#signup_confirm', '123456789'); $step->click('[type=submit]'); $browser->waitFor('.toast-error'); $step->assertVisible('#signup_login.is-invalid'); $step->assertVisible('#signup_login + span + .invalid-feedback'); $step->assertVisible('#signup_password.is-invalid'); $step->assertVisible('#signup_password + .invalid-feedback'); $step->assertFocused('#signup_login'); $browser->click('.toast-error'); // remove the toast }); // Submit invalid data (valid login, invalid password) $browser->with('@step3', function ($step) use ($browser) { $step->type('#signup_login', 'SignupTestDusk'); $step->click('[type=submit]'); $browser->waitFor('.toast-error'); $step->assertVisible('#signup_password.is-invalid'); $step->assertVisible('#signup_password + .invalid-feedback'); $step->assertMissing('#signup_login.is-invalid'); $step->assertMissing('#signup_login + span + .invalid-feedback'); $step->assertFocused('#signup_password'); $browser->click('.toast-error'); // remove the toast }); // Submit valid data $browser->with('@step3', function ($step) { $step->type('#signup_confirm', '12345678'); $step->click('[type=submit]'); }); $browser->waitUntilMissing('@step3'); // At this point we should be auto-logged-in to dashboard $dashboard = new Dashboard(); $dashboard->assert($browser); // FIXME: Is it enough to be sure user is logged in? }); } }