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?
});
}
}