diff --git a/src/.gitignore b/src/.gitignore
index 29988b9e..e7357141 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,23 +1,24 @@
*.swp
database/database.sqlite
node_modules/
package-lock.json
public/css/*.css
public/hot
public/js/*.js
public/storage/
storage/*.key
storage/export/
tests/report/
vendor
.env
.env.backup
.env.local
.env.testing
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
composer.lock
resources/countries.php
+resources/js/ts.js
diff --git a/src/resources/build/before.php b/src/resources/build/before.php
new file mode 100644
index 00000000..59a95cc4
--- /dev/null
+++ b/src/resources/build/before.php
@@ -0,0 +1,9 @@
+
diff --git a/src/tests/Browser/LogonTest.php b/src/tests/Browser/LogonTest.php
index b1cae78a..def8c463 100644
--- a/src/tests/Browser/LogonTest.php
+++ b/src/tests/Browser/LogonTest.php
@@ -1,238 +1,239 @@
browse(function (Browser $browser) {
$browser->visit(new Home())
->within(new Menu(), function ($browser) {
- $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login']);
+ $browser->assertMenuItems(['signup', 'explore', 'blog', 'support', 'login'])
+ ->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 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']);
});
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']);
});
// 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']);
});
// 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/webpack.mix.js b/src/webpack.mix.js
index 5599af72..056e0f3e 100644
--- a/src/webpack.mix.js
+++ b/src/webpack.mix.js
@@ -1,39 +1,44 @@
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
+const { exec } = require('child_process');
const fs = require('fs');
const glob = require('glob');
const mix = require('laravel-mix');
mix.webpackConfig({
resolve: {
alias: {
'jquery$': 'jquery/dist/jquery.slim.js',
}
}
})
+mix.before(() => {
+ exec('php resources/build/before.php')
+})
+
mix.js('resources/js/user.js', 'public/js').vue()
.js('resources/js/admin.js', 'public/js').vue()
glob.sync('resources/themes/*/', {}).forEach(fromDir => {
const toDir = fromDir.replace('resources/themes/', 'public/themes/')
mix.sass(fromDir + 'app.scss', toDir)
.sass(fromDir + 'document.scss', toDir);
fs.stat(fromDir + 'images', {}, (err, stats) => {
if (stats) {
mix.copyDirectory(fromDir + 'images', toDir + 'images')
}
})
})