convert timesheets to UTC with support for user timezone (#372)
This commit is contained in:
97
tests/Command/RunCodeSnifferCommandTest.php
Normal file
97
tests/Command/RunCodeSnifferCommandTest.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\BashResult;
|
||||
use App\Command\RunCodeSnifferCommand;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\Command\RunCodeSnifferCommand
|
||||
* @group integration
|
||||
*/
|
||||
class RunCodeSnifferCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
/**
|
||||
* @var TestBashExecutor
|
||||
*/
|
||||
protected $executor;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$this->directory = realpath(__DIR__ . '/../../');
|
||||
$this->executor = new TestBashExecutor($this->directory);
|
||||
|
||||
$this->application->add(new RunCodeSnifferCommand($this->executor, $this->directory));
|
||||
}
|
||||
|
||||
public function testSuccessCommandNoOptions()
|
||||
{
|
||||
$command = $this->assertSuccessCommand([]);
|
||||
$this->assertStringStartsWith('/vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none --format=txt', $command);
|
||||
}
|
||||
|
||||
public function testSuccessCommandFix()
|
||||
{
|
||||
$command = $this->assertSuccessCommand(['--fix' => true]);
|
||||
$this->assertStringStartsWith('/vendor/bin/php-cs-fixer fix', $command);
|
||||
}
|
||||
|
||||
public function testSuccessCommandCheckstyle()
|
||||
{
|
||||
$command = $this->assertSuccessCommand(['--checkstyle' => 'phpcs.txt']);
|
||||
$this->assertStringStartsWith('/vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none > ', $command);
|
||||
$this->assertStringEndsWith('phpcs.txt', $command);
|
||||
}
|
||||
|
||||
protected function assertSuccessCommand(array $options)
|
||||
{
|
||||
$result = new BashResult(0, 'FooBar');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:phpcs');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], $options);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('FooBar', $output);
|
||||
$this->assertContains('[OK] All source files have proper code styles', $output);
|
||||
|
||||
return $this->executor->getCommand();
|
||||
}
|
||||
|
||||
public function testFailureCommand()
|
||||
{
|
||||
$result = new BashResult(1, 'BarFoo');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:phpcs');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], ['--fix' => true]);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('BarFoo', $output);
|
||||
$this->assertContains('[ERROR] Found problems while checking your code styles', $output);
|
||||
}
|
||||
}
|
||||
81
tests/Command/RunIntegrationTestsCommandTest.php
Normal file
81
tests/Command/RunIntegrationTestsCommandTest.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\BashResult;
|
||||
use App\Command\RunIntegrationTestsCommand;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\Command\RunIntegrationTestsCommand
|
||||
* @group integration
|
||||
*/
|
||||
class RunIntegrationTestsCommandTest extends RunUnitTestsCommandTest
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
/**
|
||||
* @var TestBashExecutor
|
||||
*/
|
||||
protected $executor;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$this->directory = realpath(__DIR__ . '/../../');
|
||||
$this->executor = new TestBashExecutor($this->directory);
|
||||
|
||||
$this->application->add(new RunIntegrationTestsCommand($this->executor, $this->directory));
|
||||
}
|
||||
|
||||
public function testSuccessCommand()
|
||||
{
|
||||
$result = new BashResult(0, 'FooBar');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:test-integration');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], []);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('FooBar', $output);
|
||||
$this->assertContains('[OK] All tests were successful', $output);
|
||||
|
||||
$this->assertStringStartsWith('/bin/phpunit --group integration', $this->executor->getCommand());
|
||||
$this->assertContains($this->directory, $this->executor->getCommand());
|
||||
}
|
||||
|
||||
public function testFailureCommand()
|
||||
{
|
||||
$result = new BashResult(1, 'BarFoo');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:test-integration');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], []);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('BarFoo', $output);
|
||||
$this->assertContains('[ERROR] Found problems while running tests', $output);
|
||||
|
||||
$this->assertStringStartsWith('/bin/phpunit --group integration', $this->executor->getCommand());
|
||||
$this->assertContains($this->directory, $this->executor->getCommand());
|
||||
}
|
||||
}
|
||||
82
tests/Command/RunUnitTestsCommandTest.php
Normal file
82
tests/Command/RunUnitTestsCommandTest.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\BashResult;
|
||||
use App\Command\RunUnitTestsCommand;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\Command\RunUnitTestsCommand
|
||||
* @group integration
|
||||
*/
|
||||
class RunUnitTestsCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
/**
|
||||
* @var TestBashExecutor
|
||||
*/
|
||||
protected $executor;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$this->directory = realpath(__DIR__ . '/../../');
|
||||
$this->executor = new TestBashExecutor($this->directory);
|
||||
|
||||
$this->application->add(new RunUnitTestsCommand($this->executor, $this->directory));
|
||||
}
|
||||
|
||||
public function testSuccessCommand()
|
||||
{
|
||||
$result = new BashResult(0, 'FooBar');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:test-unit');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], []);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('FooBar', $output);
|
||||
$this->assertContains('[OK] All tests were successful', $output);
|
||||
|
||||
$this->assertStringStartsWith('/bin/phpunit --exclude-group integration', $this->executor->getCommand());
|
||||
$this->assertContains($this->directory, $this->executor->getCommand());
|
||||
}
|
||||
|
||||
public function testFailureCommand()
|
||||
{
|
||||
$result = new BashResult(1, 'BarFoo');
|
||||
$this->executor->setResult($result);
|
||||
|
||||
$command = $this->application->find('kimai:test-unit');
|
||||
$commandTester = new CommandTester($command);
|
||||
$inputs = array_merge(['command' => $command->getName()], []);
|
||||
$commandTester->execute($inputs);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertContains('BarFoo', $output);
|
||||
$this->assertContains('[ERROR] Found problems while running tests', $output);
|
||||
|
||||
$this->assertStringStartsWith('/bin/phpunit --exclude-group integration', $this->executor->getCommand());
|
||||
$this->assertContains($this->directory, $this->executor->getCommand());
|
||||
}
|
||||
}
|
||||
55
tests/Command/TestBashExecutor.php
Normal file
55
tests/Command/TestBashExecutor.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\BashExecutor;
|
||||
use App\Command\BashResult;
|
||||
|
||||
class TestBashExecutor extends BashExecutor
|
||||
{
|
||||
/**
|
||||
* @var BashResult
|
||||
*/
|
||||
protected $result;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $command;
|
||||
|
||||
/**
|
||||
* @param BashResult $result
|
||||
* @return $this
|
||||
*/
|
||||
public function setResult(BashResult $result)
|
||||
{
|
||||
$this->result = $result;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCommand(): string
|
||||
{
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $command
|
||||
* @return BashResult
|
||||
*/
|
||||
public function execute(string $command)
|
||||
{
|
||||
$this->command = $command;
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
@@ -256,7 +256,7 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
'user_preferences_form' => [
|
||||
'preferences' => [
|
||||
['name' => UserPreference::HOURLY_RATE, 'value' => 37.5],
|
||||
// ['name' => 'timezone', 'value' => 'America/Creston'],
|
||||
['name' => 'timezone', 'value' => 'America/Creston'],
|
||||
['name' => 'language', 'value' => 'ar'],
|
||||
['name' => UserPreference::SKIN, 'value' => 'blue'],
|
||||
['name' => 'theme.fixed_layout', 'value' => false],
|
||||
@@ -280,7 +280,7 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
$user = $this->getUserByName($em, $username);
|
||||
|
||||
$this->assertEquals($hourlyRate, $user->getPreferenceValue(UserPreference::HOURLY_RATE));
|
||||
//$this->assertEquals('', $user->getPreferenceValue('America/Creston'));
|
||||
$this->assertEquals('', $user->getPreferenceValue('America/Creston'));
|
||||
$this->assertEquals('ar', $user->getPreferenceValue('language'));
|
||||
$this->assertEquals('blue', $user->getPreferenceValue(UserPreference::SKIN));
|
||||
$this->assertEquals(false, $user->getPreferenceValue('theme.fixed_layout'));
|
||||
|
||||
@@ -116,6 +116,11 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$client->submit($form, [
|
||||
'timesheet_edit_form' => [
|
||||
'description' => 'Testing is fun!',
|
||||
// begin is always pre-filled with the current datetime
|
||||
// 'begin' => null,
|
||||
// end must be allowed to be null, to start a record
|
||||
// there was a bug with end begin required, so we manually set this field to be empty
|
||||
'end' => null,
|
||||
'project' => 1,
|
||||
'activity' => 1,
|
||||
]
|
||||
|
||||
105
tests/Doctrine/UTCDateTimeTypeTest.php
Normal file
105
tests/Doctrine/UTCDateTimeTypeTest.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?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\Doctrine;
|
||||
|
||||
use App\Doctrine\UTCDateTimeType;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Doctrine\UTCDateTimeType
|
||||
*/
|
||||
class UTCDateTimeTypeTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var AbstractPlatform
|
||||
*/
|
||||
private $platform;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
|
||||
$registry = $kernel->getContainer()->get('doctrine');
|
||||
/** @var \Doctrine\DBAL\Connection $connection */
|
||||
$connection = $registry->getConnection();
|
||||
$this->platform = $connection->getDatabasePlatform();
|
||||
}
|
||||
|
||||
public function testGetUtc()
|
||||
{
|
||||
Type::overrideType(Type::DATETIME, UTCDateTimeType::class);
|
||||
/** @var UTCDateTimeType $type */
|
||||
$type = Type::getType(Type::DATETIME);
|
||||
|
||||
$this->assertInstanceOf(UTCDateTimeType::class, $type);
|
||||
$utc = $type::getUtc();
|
||||
$this->assertSame($utc, $type::getUtc());
|
||||
$this->assertEquals('UTC', $type::getUtc()->getName());
|
||||
}
|
||||
|
||||
public function testConvertToDatabaseValue()
|
||||
{
|
||||
Type::overrideType(Type::DATETIME, UTCDateTimeType::class);
|
||||
/** @var UTCDateTimeType $type */
|
||||
$type = Type::getType(Type::DATETIME);
|
||||
|
||||
$result = $type->convertToDatabaseValue(null, $this->platform);
|
||||
$this->assertNull($result);
|
||||
|
||||
$berlinTz = new \DateTimeZone('Europe/Berlin');
|
||||
$date = new \DateTime('2019-01-17 13:30:00');
|
||||
$date->setTimezone($berlinTz);
|
||||
|
||||
$this->assertEquals('Europe/Berlin', $date->getTimezone()->getName());
|
||||
|
||||
$expected = clone $date;
|
||||
$expected->setTimezone($type::getUtc());
|
||||
$bla = $expected->format($this->platform->getDateTimeFormatString());
|
||||
|
||||
/** @var \DateTime $result */
|
||||
$result = $type->convertToDatabaseValue($date, $this->platform);
|
||||
|
||||
$this->assertEquals($bla, $result);
|
||||
}
|
||||
|
||||
public function testConvertToPHPValue()
|
||||
{
|
||||
Type::overrideType(Type::DATETIME, UTCDateTimeType::class);
|
||||
/** @var UTCDateTimeType $type */
|
||||
$type = Type::getType(Type::DATETIME);
|
||||
|
||||
$result = $type->convertToPHPValue(null, $this->platform);
|
||||
$this->assertNull($result);
|
||||
|
||||
$result = $type->convertToPHPValue('2019-01-17 13:30:00', $this->platform);
|
||||
$this->assertInstanceOf(\DateTime::class, $result);
|
||||
$this->assertEquals('UTC', $result->getTimezone()->getName());
|
||||
|
||||
$result = $result->format($this->platform->getDateTimeFormatString());
|
||||
$this->assertEquals('2019-01-17 13:30:00', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Doctrine\DBAL\Types\ConversionException
|
||||
*/
|
||||
public function testConvertToPHPValueWithInvalidValue()
|
||||
{
|
||||
Type::overrideType(Type::DATETIME, UTCDateTimeType::class);
|
||||
/** @var UTCDateTimeType $type */
|
||||
$type = Type::getType(Type::DATETIME);
|
||||
|
||||
$type->convertToPHPValue('201xx01-17 13:30:00', $this->platform);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ abstract class AbstractCalculatorTest extends TestCase
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
->setDescription('timesheet description')
|
||||
->setBegin(new \DateTime())
|
||||
->setDuration(3600)
|
||||
->setRate(293.27)
|
||||
->setUser(new User())
|
||||
|
||||
@@ -44,6 +44,15 @@ class DebugRenderer implements RendererInterface
|
||||
return $date->format('d.m.Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $date
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFormattedTime(\DateTime $date)
|
||||
{
|
||||
return $date->format('H:i');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @return mixed
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace App\Tests\Repository;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
@@ -93,4 +95,28 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
$this->assertTrue($result);
|
||||
$this->assertInstanceOf(\DateTime::class, $timesheet->getEnd());
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$activityRepository = $em->getRepository(Activity::class);
|
||||
$activity = $activityRepository->find(1);
|
||||
$projectRepository = $em->getRepository(Project::class);
|
||||
$project = $projectRepository->find(1);
|
||||
|
||||
$user = $this->getUserByRole($em, User::ROLE_USER);
|
||||
$repository = $em->getRepository(Timesheet::class);
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
->setDescription('foo')
|
||||
->setUser($user)
|
||||
->setActivity($activity)
|
||||
->setProject($project);
|
||||
|
||||
$this->assertNull($timesheet->getId());
|
||||
$repository->save($timesheet);
|
||||
$this->assertEquals(1, $timesheet->getId());
|
||||
}
|
||||
}
|
||||
|
||||
57
tests/Security/UserCheckerTest.php
Normal file
57
tests/Security/UserCheckerTest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Security\UserChecker;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\Security\UserChecker
|
||||
*/
|
||||
class UserCheckerTest extends TestCase
|
||||
{
|
||||
public function testCheckPreAuth()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
$user = new User();
|
||||
|
||||
try {
|
||||
$sut->checkPreAuth($user);
|
||||
} catch (\Exception $ex) {
|
||||
$this->fail('UserChecker should not throw exception in checkPreAuth()');
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Security\Core\Exception\LockedException
|
||||
*/
|
||||
public function testDisabledCannotLogin()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
$user = new User();
|
||||
$user->setEnabled(false);
|
||||
|
||||
$sut->checkPostAuth($user);
|
||||
}
|
||||
|
||||
public function testCheckPostAuth()
|
||||
{
|
||||
$sut = new UserChecker();
|
||||
|
||||
$mock = $this->getMockBuilder(UserInterface::class)->setMethods(['isEnabled'])->getMockForAbstractClass();
|
||||
$mock->expects($this->never())->method('isEnabled')->willReturn(false);
|
||||
|
||||
$sut->checkPostAuth($mock);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -163,10 +163,13 @@ class RateCalculatorTest extends TestCase
|
||||
*/
|
||||
public function testCalculateWithRules($rules, $expectedFactor)
|
||||
{
|
||||
$seconds = 41837;
|
||||
$seconds = 31837;
|
||||
|
||||
$end = new \DateTime();
|
||||
$end->setTimezone(new \DateTimeZone('UTC'));
|
||||
$end->setTime(12, 0, 0);
|
||||
$start = clone $end;
|
||||
$start->setTimezone(new \DateTimeZone('UTC'));
|
||||
$start->setTimestamp($end->getTimestamp() - $seconds);
|
||||
|
||||
$record = new Timesheet();
|
||||
@@ -191,6 +194,7 @@ class RateCalculatorTest extends TestCase
|
||||
public function getRuleDefinitions()
|
||||
{
|
||||
$start = new \DateTime();
|
||||
$start->setTimezone(new \DateTimeZone('UTC'));
|
||||
$start->setTime(12, 0, 0);
|
||||
$day = $start->format('l');
|
||||
|
||||
@@ -219,7 +223,7 @@ class RateCalculatorTest extends TestCase
|
||||
'factor' => 2.0
|
||||
],
|
||||
'weekdays' => [
|
||||
'days' => ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'],
|
||||
'days' => ['MonDay', 'tUEsdAy', 'WEdnesday', 'THursday', 'friDay', 'SATURday', 'sunDAY'],
|
||||
'factor' => 1.5
|
||||
],
|
||||
],
|
||||
|
||||
@@ -40,7 +40,7 @@ class DateExtensionsTest extends TestCase
|
||||
|
||||
public function testGetFilters()
|
||||
{
|
||||
$filters = ['month_name', 'date_short', 'date_time'];
|
||||
$filters = ['month_name', 'date_short', 'date_time', 'time'];
|
||||
$sut = $this->getSut('de', []);
|
||||
$twigFilters = $sut->getFilters();
|
||||
$this->assertCount(count($filters), $twigFilters);
|
||||
@@ -119,4 +119,13 @@ class DateExtensionsTest extends TestCase
|
||||
[new \DateTime('2016-12-23'), 'month.12'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testTime()
|
||||
{
|
||||
$time = new \DateTime('2016-06-23');
|
||||
$time->setTime(17, 53, 23);
|
||||
|
||||
$sut = $this->getSut('en', []);
|
||||
$this->assertEquals('17:53', $sut->time($time));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,5 +22,36 @@ class MarkdownTest extends TestCase
|
||||
$sut = new Markdown();
|
||||
$this->assertEquals('<p><em>test</em></p>', $sut->toHtml('*test*'));
|
||||
$this->assertEquals('<h1 id="foobar">foobar</h1>', $sut->toHtml('# foobar'));
|
||||
$html = <<<'EOT'
|
||||
<p>foo bar</p>
|
||||
<ul>
|
||||
<li>sdfasdfasdf</li>
|
||||
<li>asdfasdfasdf</li>
|
||||
</ul>
|
||||
<h1 id="test">test</h1>
|
||||
<p>asdfasdfa</p>
|
||||
<pre><code>ssdfsdf</code></pre>
|
||||
<p>sdfsdf <a href="#test-1">asdfasdf</a> asdfasdf</p>
|
||||
<h1 id="test-1">test</h1>
|
||||
<p>aasdfasdf</p>
|
||||
EOT;
|
||||
|
||||
$markdown = <<<EOT
|
||||
foo bar
|
||||
|
||||
- sdfasdfasdf
|
||||
- asdfasdfasdf
|
||||
|
||||
# test
|
||||
asdfasdfa
|
||||
|
||||
ssdfsdf
|
||||
|
||||
sdfsdf [asdfasdf](#test-1) asdfasdf
|
||||
|
||||
# test
|
||||
aasdfasdf
|
||||
EOT;
|
||||
$this->assertEquals($html, $sut->toHtml($markdown));
|
||||
}
|
||||
}
|
||||
|
||||
159
tests/Validator/Constraints/TimesheetValidatorTest.php
Normal file
159
tests/Validator/Constraints/TimesheetValidatorTest.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?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\Validator\Constraints;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Validator\Constraints\Timesheet as TimesheetConstraint;
|
||||
use App\Validator\Constraints\TimesheetValidator;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Validator\Constraints\TimesheetValidator
|
||||
*/
|
||||
class TimesheetValidatorTest extends ConstraintValidatorTestCase
|
||||
{
|
||||
protected function createValidator()
|
||||
{
|
||||
$options = [
|
||||
'allow_future_times' => false
|
||||
];
|
||||
|
||||
return new TimesheetValidator($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
|
||||
*/
|
||||
public function testConstraintIsInvalid()
|
||||
{
|
||||
$this->validator->validate('foo', new NotBlank());
|
||||
}
|
||||
|
||||
public function testEmptyTimesheet()
|
||||
{
|
||||
$timesheet = new Timesheet();
|
||||
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
|
||||
|
||||
$this->buildViolation('You must submit a begin date.')
|
||||
->atPath('property.path.begin')
|
||||
->setCode(TimesheetConstraint::MISSING_BEGIN_ERROR)
|
||||
->buildNextViolation('A timesheet must have an activity.')
|
||||
->atPath('property.path.activity')
|
||||
->setCode(TimesheetConstraint::MISSING_ACTIVITY_ERROR)
|
||||
->buildNextViolation('A timesheet must have a project.')
|
||||
->atPath('property.path.project')
|
||||
->setCode(TimesheetConstraint::MISSING_PROJECT_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testFutureBegin()
|
||||
{
|
||||
$begin = new \DateTime('+10 hour');
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setBegin($begin);
|
||||
|
||||
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
|
||||
|
||||
$this->buildViolation('The begin date cannot be in the future.')
|
||||
->atPath('property.path.begin')
|
||||
->setCode(TimesheetConstraint::BEGIN_IN_FUTURE_ERROR)
|
||||
->buildNextViolation('A timesheet must have an activity.')
|
||||
->atPath('property.path.activity')
|
||||
->setCode(TimesheetConstraint::MISSING_ACTIVITY_ERROR)
|
||||
->buildNextViolation('A timesheet must have a project.')
|
||||
->atPath('property.path.project')
|
||||
->setCode(TimesheetConstraint::MISSING_PROJECT_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testEndBeforeBegin()
|
||||
{
|
||||
$end = new \DateTime('-10 hour');
|
||||
$begin = new \DateTime('-1 hour');
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setBegin($begin);
|
||||
$timesheet->setEnd($end);
|
||||
|
||||
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
|
||||
|
||||
$this->buildViolation('End date must not be earlier then start date.')
|
||||
->atPath('property.path.end')
|
||||
->setCode(TimesheetConstraint::END_BEFORE_BEGIN_ERROR)
|
||||
->buildNextViolation('A timesheet must have an activity.')
|
||||
->atPath('property.path.activity')
|
||||
->setCode(TimesheetConstraint::MISSING_ACTIVITY_ERROR)
|
||||
->buildNextViolation('A timesheet must have a project.')
|
||||
->atPath('property.path.project')
|
||||
->setCode(TimesheetConstraint::MISSING_PROJECT_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testProjectMismatch()
|
||||
{
|
||||
$end = new \DateTime('-1 hour');
|
||||
$begin = new \DateTime('-10 hour');
|
||||
$activity = new Activity();
|
||||
$project1 = new Project();
|
||||
$project2 = new Project();
|
||||
$activity->setProject($project1);
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
->setBegin($begin)
|
||||
->setEnd($end)
|
||||
->setActivity($activity)
|
||||
->setProject($project2)
|
||||
;
|
||||
|
||||
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
|
||||
|
||||
$this->buildViolation('Project mismatch, project specific activity and timesheet project are different.')
|
||||
->atPath('property.path.project')
|
||||
->setCode(TimesheetConstraint::ACTIVITY_PROJECT_MISMATCH_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testDisabledValuesDuringStart()
|
||||
{
|
||||
$begin = new \DateTime('-10 hour');
|
||||
$customer = new Customer();
|
||||
$customer->setVisible(false);
|
||||
$activity = new Activity();
|
||||
$activity->setVisible(false);
|
||||
$project = new Project();
|
||||
$project->setVisible(false);
|
||||
$project->setCustomer($customer);
|
||||
$activity->setProject($project);
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
->setBegin($begin)
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
;
|
||||
|
||||
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
|
||||
|
||||
$this->buildViolation('Cannot start a disabled activity.')
|
||||
->atPath('property.path.activity')
|
||||
->setCode(TimesheetConstraint::DISABLED_ACTIVITY_ERROR)
|
||||
->buildNextViolation('Cannot start a disabled project.')
|
||||
->atPath('property.path.project')
|
||||
->setCode(TimesheetConstraint::DISABLED_PROJECT_ERROR)
|
||||
->buildNextViolation('Cannot start a disabled customer.')
|
||||
->atPath('property.path.customer')
|
||||
->setCode(TimesheetConstraint::DISABLED_CUSTOMER_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ class UserVoterTest extends AbstractVoterTest
|
||||
yield [$user, $user4, 'edit', $result];
|
||||
yield [$user, $user4, 'delete', $result];
|
||||
yield [$user, $user4, 'hourly-rate', $result];
|
||||
yield [$user, $user4, 'hourly-rate', $result];
|
||||
}
|
||||
|
||||
$result = VoterInterface::ACCESS_ABSTAIN;
|
||||
|
||||
Reference in New Issue
Block a user