added database driven system configurations with admin screen (#647)
This commit is contained in:
100
tests/Configuration/FormConfigurationTest.php
Normal file
100
tests/Configuration/FormConfigurationTest.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\FormConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\FormConfiguration
|
||||
* @covers \App\Configuration\StringAccessibleConfigTrait
|
||||
*/
|
||||
class FormConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new FormConfiguration($loader, $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'customer' => [
|
||||
'timezone' => 'Europe/London',
|
||||
'currency' => 'GBP',
|
||||
'country' => 'FR',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultLoaderSettings()
|
||||
{
|
||||
return [
|
||||
(new Configuration())->setName('defaults.customer.timezone')->setValue('Russia/Moscov'),
|
||||
(new Configuration())->setName('defaults.customer.currency')->setValue('USD'),
|
||||
(new Configuration())->setName('defaults.customer.country')->setValue('RU'),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('defaults', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('GBP', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('FR', $sut->getCustomerDefaultCountry());
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals('Russia/Moscov', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('USD', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('RU', $sut->getCustomerDefaultCountry());
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('defaults.customer.country')->setValue('RU'),
|
||||
(new Configuration())->setName('defaults.customer.foobar')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('GBP', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('RU', $sut->getCustomerDefaultCountry());
|
||||
}
|
||||
|
||||
public function testFindByKey()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('FR', $sut->find('customer.country'));
|
||||
$this->assertEquals('FR', $sut->find('defaults.customer.country'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Unknown config: foobar
|
||||
*/
|
||||
public function testUnknownConfigAreNotImportedAndFindingThemThrowsException()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('defaults.customer.foobar')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('customer.foobar'));
|
||||
}
|
||||
}
|
||||
114
tests/Configuration/SystemConfigurationTest.php
Normal file
114
tests/Configuration/SystemConfigurationTest.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\SystemConfiguration
|
||||
* @covers \App\Configuration\StringAccessibleConfigTrait
|
||||
*/
|
||||
class SystemConfigurationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array $settings
|
||||
* @param array $loaderSettings
|
||||
* @return SystemConfiguration
|
||||
*/
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new SystemConfiguration($loader, $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'timesheet' => [
|
||||
'rules' => [
|
||||
'allow_future_times' => false,
|
||||
],
|
||||
'duration_only' => true,
|
||||
'markdown_content' => false,
|
||||
'active_entries' => [
|
||||
'hard_limit' => 99,
|
||||
'soft_limit' => 15,
|
||||
],
|
||||
],
|
||||
'defaults' => [
|
||||
'customer' => [
|
||||
'timezone' => 'Europe/London',
|
||||
'currency' => 'GBP',
|
||||
'country' => 'FR',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultLoaderSettings()
|
||||
{
|
||||
return [
|
||||
(new Configuration())->setName('defaults.customer.timezone')->setValue('Russia/Moscov'),
|
||||
(new Configuration())->setName('defaults.customer.currency')->setValue('RUB'),
|
||||
(new Configuration())->setName('timesheet.rules.allow_future_times')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.duration_only')->setValue('0'),
|
||||
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
|
||||
(new Configuration())->setName('timesheet.active_entries.soft_limit')->setValue('3'),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('kimai', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('Europe/London', $sut->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('GBP', $sut->find('defaults.customer.currency'));
|
||||
$this->assertEquals(false, $sut->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(99, $sut->find('timesheet.active_entries.hard_limit'));
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals('Russia/Moscov', $sut->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('RUB', $sut->find('defaults.customer.currency'));
|
||||
$this->assertEquals(true, $sut->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(7, $sut->find('timesheet.active_entries.hard_limit'));
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.duration_only')->setValue(''),
|
||||
]);
|
||||
$this->assertEquals(false, $sut->find('timesheet.duration_only'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Unknown config: foo
|
||||
*/
|
||||
public function testUnknownConfigAreNotImportedAndFindingThemThrowsException()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.foo')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('foo'));
|
||||
}
|
||||
}
|
||||
35
tests/Configuration/TestConfigLoader.php
Normal file
35
tests/Configuration/TestConfigLoader.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\ConfigLoaderInterface;
|
||||
use App\Entity\Configuration;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\FormConfiguration
|
||||
*/
|
||||
class TestConfigLoader implements ConfigLoaderInterface
|
||||
{
|
||||
private $configs = [];
|
||||
|
||||
public function __construct(array $configs)
|
||||
{
|
||||
$this->configs = $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $prefix
|
||||
* @return Configuration[]
|
||||
*/
|
||||
public function getConfiguration(?string $prefix = null): array
|
||||
{
|
||||
return $this->configs;
|
||||
}
|
||||
}
|
||||
112
tests/Configuration/TimesheetConfigurationTest.php
Normal file
112
tests/Configuration/TimesheetConfigurationTest.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\TimesheetConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\TimesheetConfiguration
|
||||
* @covers \App\Configuration\StringAccessibleConfigTrait
|
||||
*/
|
||||
class TimesheetConfigurationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array $settings
|
||||
* @param array $loaderSettings
|
||||
* @return TimesheetConfiguration
|
||||
*/
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new TimesheetConfiguration($loader, $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'rules' => [
|
||||
'allow_future_times' => false,
|
||||
],
|
||||
'duration_only' => true,
|
||||
'markdown_content' => false,
|
||||
'active_entries' => [
|
||||
'hard_limit' => 99,
|
||||
'soft_limit' => 15,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultLoaderSettings()
|
||||
{
|
||||
return [
|
||||
(new Configuration())->setName('timesheet.rules.allow_future_times')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.duration_only')->setValue('0'),
|
||||
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
|
||||
(new Configuration())->setName('timesheet.active_entries.soft_limit')->setValue('3'),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('timesheet', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals(99, $sut->getActiveEntriesHardLimit());
|
||||
$this->assertEquals(15, $sut->getActiveEntriesSoftLimit());
|
||||
$this->assertEquals(false, $sut->isAllowFutureTimes());
|
||||
$this->assertEquals(true, $sut->isDurationOnly());
|
||||
$this->assertEquals(false, $sut->isMarkdownEnabled());
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals(7, $sut->getActiveEntriesHardLimit());
|
||||
$this->assertEquals(3, $sut->getActiveEntriesSoftLimit());
|
||||
$this->assertEquals(true, $sut->isAllowFutureTimes());
|
||||
$this->assertEquals(false, $sut->isDurationOnly());
|
||||
$this->assertEquals(true, $sut->isMarkdownEnabled());
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.duration_only')->setValue(''),
|
||||
]);
|
||||
$this->assertEquals(false, $sut->isDurationOnly());
|
||||
}
|
||||
|
||||
public function testFindByKey()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals(false, $sut->find('rules.allow_future_times'));
|
||||
$this->assertEquals(false, $sut->find('timesheet.rules.allow_future_times'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Unknown config: foo
|
||||
*/
|
||||
public function testUnknownConfigAreNotImportedAndFindingThemThrowsException()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.foo')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('foo'));
|
||||
}
|
||||
}
|
||||
@@ -271,6 +271,15 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
$this->assertHasFlashSuccess($client, 'Entry was deleted successful');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string|null $message
|
||||
*/
|
||||
protected function assertHasFlashSaveSuccess(Client $client)
|
||||
{
|
||||
$this->assertHasFlashSuccess($client, 'Saved changes successful');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string|null $message
|
||||
@@ -284,6 +293,19 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string|null $message
|
||||
*/
|
||||
protected function assertHasFlashError(Client $client, string $message = null)
|
||||
{
|
||||
$node = $client->getCrawler()->filter('div.alert.alert-error.alert-dismissible');
|
||||
$this->assertNotEmpty($node->text());
|
||||
if (null !== $message) {
|
||||
$this->assertContains($message, $node->text());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
|
||||
175
tests/Controller/SystemConfigurationControllerTest.php
Normal file
175
tests/Controller/SystemConfigurationControllerTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\Controller\SystemConfigurationController
|
||||
* @group integration
|
||||
*/
|
||||
class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/admin/system-config/');
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/system-config/');
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$expectedForms = $this->getTestDataForms();
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary');
|
||||
$this->assertEquals(count($expectedForms), count($result));
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary form');
|
||||
$this->assertEquals(count($expectedForms), count($result));
|
||||
|
||||
foreach ($expectedForms as $formConfig) {
|
||||
$result = $client->getCrawler()->filter($formConfig[0]);
|
||||
$this->assertEquals(1, count($result));
|
||||
$form = $result->form();
|
||||
$this->assertStringEndsWith($formConfig[1], $form->getUri());
|
||||
$this->assertEquals('POST', $form->getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTestDataForms()
|
||||
{
|
||||
return [
|
||||
['#system_configuration_form_timesheet', $this->createUrl('/admin/system-config/timesheet')],
|
||||
['#system_configuration_form_form_customer', $this->createUrl('/admin/system-config/customer')],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUpdateTimesheetConfig()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = $client->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals(false, $configService->find('timesheet.markdown_content'));
|
||||
$this->assertEquals(false, $configService->find('timesheet.duration_only'));
|
||||
$this->assertEquals(true, $configService->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(3, $configService->find('timesheet.active_entries.hard_limit'));
|
||||
$this->assertEquals(1, $configService->find('timesheet.active_entries.soft_limit'));
|
||||
|
||||
$form = $client->getCrawler()->filter('#system_configuration_form_timesheet')->form();
|
||||
$client->submit($form, [
|
||||
'system_configuration_form' => [
|
||||
'configuration' => [
|
||||
['name' => 'timesheet.markdown_content', 'value' => 1],
|
||||
['name' => 'timesheet.duration_only', 'value' => 1],
|
||||
['name' => 'timesheet.rules.allow_future_times', 'value' => false],
|
||||
['name' => 'timesheet.active_entries.hard_limit', 'value' => 99],
|
||||
['name' => 'timesheet.active_entries.soft_limit', 'value' => 77],
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/system-config/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = $client->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals(true, $configService->find('timesheet.markdown_content'));
|
||||
$this->assertEquals(true, $configService->find('timesheet.duration_only'));
|
||||
$this->assertEquals(false, $configService->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(99, $configService->find('timesheet.active_entries.hard_limit'));
|
||||
$this->assertEquals(77, $configService->find('timesheet.active_entries.soft_limit'));
|
||||
}
|
||||
|
||||
public function testUpdateTimesheetConfigValidation()
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_SUPER_ADMIN,
|
||||
'/admin/system-config/',
|
||||
'#system_configuration_form_timesheet',
|
||||
[
|
||||
'system_configuration_form' => [
|
||||
'configuration' => [
|
||||
['name' => 'timesheet.markdown_content', 'value' => 1],
|
||||
['name' => 'timesheet.duration_only', 'value' => 1],
|
||||
['name' => 'timesheet.rules.allow_future_times', 'value' => 1],
|
||||
['name' => 'timesheet.active_entries.hard_limit', 'value' => -1],
|
||||
['name' => 'timesheet.active_entries.soft_limit', 'value' => -1],
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'#system_configuration_form_configuration_3_value',
|
||||
'#system_configuration_form_configuration_4_value',
|
||||
],
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public function testUpdateCustomerConfig()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = $client->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals('Europe/Berlin', $configService->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('DE', $configService->find('defaults.customer.country'));
|
||||
$this->assertEquals('EUR', $configService->find('defaults.customer.currency'));
|
||||
|
||||
$form = $client->getCrawler()->filter('#system_configuration_form_form_customer')->form();
|
||||
$client->submit($form, [
|
||||
'system_configuration_form' => [
|
||||
'configuration' => [
|
||||
['name' => 'defaults.customer.timezone', 'value' => 'Atlantic/Canary'],
|
||||
['name' => 'defaults.customer.country', 'value' => 'BB'],
|
||||
['name' => 'defaults.customer.currency', 'value' => 'GBP'],
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/system-config/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = $client->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals('Atlantic/Canary', $configService->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('BB', $configService->find('defaults.customer.country'));
|
||||
$this->assertEquals('GBP', $configService->find('defaults.customer.currency'));
|
||||
}
|
||||
|
||||
public function testUpdateCustomerConfigValidation()
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_SUPER_ADMIN,
|
||||
'/admin/system-config/',
|
||||
'#system_configuration_form_form_customer',
|
||||
[
|
||||
'system_configuration_form' => [
|
||||
'configuration' => [
|
||||
['name' => 'defaults.customer.timezone', 'value' => 'XX'],
|
||||
['name' => 'defaults.customer.country', 'value' => 1],
|
||||
['name' => 'defaults.customer.currency', 'value' => 'XXX'],
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'#system_configuration_form_configuration_0_value',
|
||||
'#system_configuration_form_configuration_1_value',
|
||||
'#system_configuration_form_configuration_2_value',
|
||||
],
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertIsRedirect($client, $this->createUrl('/timesheet/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSuccess($client);
|
||||
$this->assertHasFlashSuccess($client, 'Time recording was started');
|
||||
|
||||
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||
/** @var Timesheet $timesheet */
|
||||
@@ -198,7 +198,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(1, $timesheet->getProject()->getId());
|
||||
}
|
||||
|
||||
public function testStopActionDoesNotShowRateFieldsForUser()
|
||||
public function testCreateActionDoesNotShowRateFieldsForUser()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/timesheet/create');
|
||||
@@ -246,6 +246,40 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertNull($timesheet->getHourlyRate());
|
||||
}
|
||||
|
||||
public function testStopActionFailsOnStoppedEntry()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/timesheet/create');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=timesheet_edit_form]')->form();
|
||||
$client->submit($form, [
|
||||
'timesheet_edit_form' => [
|
||||
'description' => 'Testing is fun!',
|
||||
'fixedRate' => 100,
|
||||
'project' => 1,
|
||||
'activity' => 1,
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, $this->createUrl('/timesheet/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSuccess($client);
|
||||
|
||||
$this->request($client, '/timesheet/1/stop');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/timesheet/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSuccess($client);
|
||||
|
||||
$this->request($client, '/timesheet/1/stop');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/timesheet/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashError($client, 'Time recording could not be stopped: Timesheet entry already stopped');
|
||||
}
|
||||
|
||||
public function testCreateActionWithFromAndToValues()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
@@ -336,6 +370,21 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
'Could not find link to documentation'
|
||||
);
|
||||
|
||||
// TODO more assertions
|
||||
$form = $client->getCrawler()->filter('form[name=timesheet_edit_form]')->form();
|
||||
$client->submit($form, [
|
||||
'timesheet_edit_form' => [
|
||||
'description' => 'foo-bar'
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, $this->createUrl('/timesheet/'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
|
||||
/** @var Timesheet $timesheet */
|
||||
$timesheet = $em->getRepository(Timesheet::class)->find(1);
|
||||
$this->assertEquals('foo-bar', $timesheet->getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
35
tests/Entity/ConfigurationTest.php
Normal file
35
tests/Entity/ConfigurationTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\Entity;
|
||||
|
||||
use App\Entity\Configuration;
|
||||
|
||||
/**
|
||||
* @covers \App\Entity\Configuration
|
||||
*/
|
||||
class ConfigurationTest extends AbstractEntityTest
|
||||
{
|
||||
public function testDefaultValues()
|
||||
{
|
||||
$sut = new Configuration();
|
||||
$this->assertNull($sut->getId());
|
||||
$this->assertNull($sut->getName());
|
||||
$this->assertNull($sut->getValue());
|
||||
}
|
||||
|
||||
public function testSetterAndGetter()
|
||||
{
|
||||
$sut = new Configuration();
|
||||
$this->assertInstanceOf(Configuration::class, $sut->setName('foo-bar'));
|
||||
$this->assertEquals('foo-bar', $sut->getName());
|
||||
$this->assertInstanceOf(Configuration::class, $sut->setValue('hello world'));
|
||||
$this->assertEquals('hello world', $sut->getValue());
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace App\Tests\Twig;
|
||||
|
||||
use App\Configuration\ConfigLoaderInterface;
|
||||
use App\Configuration\TimesheetConfiguration;
|
||||
use App\Twig\MarkdownExtension;
|
||||
use App\Utils\Markdown;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -20,7 +22,9 @@ class MarkdownExtensionTest extends TestCase
|
||||
{
|
||||
public function testGetFilters()
|
||||
{
|
||||
$sut = new MarkdownExtension(new Markdown());
|
||||
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
|
||||
$config = new TimesheetConfiguration($loader, ['markdown_content' => true]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
$filters = $sut->getFilters();
|
||||
$this->assertCount(2, $filters);
|
||||
$this->assertEquals('md2html', $filters[0]->getName());
|
||||
@@ -29,14 +33,18 @@ class MarkdownExtensionTest extends TestCase
|
||||
|
||||
public function testMarkdownToHtml()
|
||||
{
|
||||
$sut = new MarkdownExtension(new Markdown());
|
||||
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
|
||||
$config = new TimesheetConfiguration($loader, ['markdown_content' => true]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
$this->assertEquals('<p><em>test</em></p>', $sut->markdownToHtml('*test*'));
|
||||
$this->assertEquals('<h1 id="foobar">foobar</h1>', $sut->markdownToHtml('# foobar'));
|
||||
}
|
||||
|
||||
public function testTimesheetContent()
|
||||
{
|
||||
$sut = new MarkdownExtension(new Markdown(), false);
|
||||
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
|
||||
$config = new TimesheetConfiguration($loader, ['markdown_content' => false]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
$this->assertEquals(
|
||||
"- test<br />\n- foo",
|
||||
$sut->timesheetContent("- test\n- foo")
|
||||
@@ -44,7 +52,8 @@ class MarkdownExtensionTest extends TestCase
|
||||
$this->assertEquals('', $sut->timesheetContent(null));
|
||||
$this->assertEquals('', $sut->timesheetContent(''));
|
||||
|
||||
$sut = new MarkdownExtension(new Markdown(), true);
|
||||
$config = new TimesheetConfiguration($loader, ['markdown_content' => true]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
$this->assertEquals(
|
||||
"<ul>\n<li>test</li>\n<li>foo</li>\n</ul>\n<p>foo <strong>bar</strong></p>",
|
||||
$sut->timesheetContent("- test\n- foo\n\nfoo __bar__")
|
||||
|
||||
@@ -17,9 +17,9 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
*/
|
||||
class MPdfConverterTest extends KernelTestCase
|
||||
{
|
||||
function unicode_hex($unicode_dec)
|
||||
public function unicode_hex($unicode_dec)
|
||||
{
|
||||
return (sprintf("%05s", strtoupper(dechex($unicode_dec))));
|
||||
return (sprintf('%05s', strtoupper(dechex($unicode_dec))));
|
||||
}
|
||||
|
||||
public function test()
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace App\Tests\Validator\Constraints;
|
||||
|
||||
use App\Configuration\ConfigLoaderInterface;
|
||||
use App\Configuration\TimesheetConfiguration;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
@@ -26,14 +28,18 @@ class TimesheetValidatorTest extends ConstraintValidatorTestCase
|
||||
{
|
||||
protected function createValidator($isGranted = true)
|
||||
{
|
||||
$options = [
|
||||
'allow_future_times' => false
|
||||
];
|
||||
|
||||
$authMock = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
|
||||
$authMock->method('isGranted')->willReturn($isGranted);
|
||||
|
||||
return new TimesheetValidator($authMock, $options, false);
|
||||
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
|
||||
$config = new TimesheetConfiguration($loader, [
|
||||
'rules' => [
|
||||
'allow_future_times' => false,
|
||||
],
|
||||
'duration_only' => false,
|
||||
]);
|
||||
|
||||
return new TimesheetValidator($authMock, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user