add "duration + fixed start time" tracking mode (#859)

This commit is contained in:
Kevin Papst
2019-06-15 23:04:53 +02:00
committed by GitHub
parent 92f3f4ee57
commit 59d2946b91
36 changed files with 1094 additions and 265 deletions

View File

@@ -43,6 +43,7 @@ class TimesheetConfigurationTest extends TestCase
'hard_limit' => 99,
'soft_limit' => 15,
],
'default_begin' => 'now',
];
}
@@ -52,6 +53,7 @@ class TimesheetConfigurationTest extends TestCase
(new Configuration())->setName('timesheet.rules.allow_future_times')->setValue('1'),
(new Configuration())->setName('timesheet.mode')->setValue('default'),
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
(new Configuration())->setName('timesheet.default_begin')->setValue('07:00'),
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
(new Configuration())->setName('timesheet.active_entries.soft_limit')->setValue('3'),
];
@@ -69,8 +71,9 @@ class TimesheetConfigurationTest extends TestCase
$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());
$this->assertEquals('duration_only', $sut->getTrackingMode());
$this->assertEquals('now', $sut->getDefaultBeginTime());
}
public function testDefaultWithLoader()
@@ -79,8 +82,9 @@ class TimesheetConfigurationTest extends TestCase
$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());
$this->assertEquals('default', $sut->getTrackingMode());
$this->assertEquals('07:00', $sut->getDefaultBeginTime());
}
public function testDefaultWithMixedConfigs()
@@ -88,7 +92,7 @@ class TimesheetConfigurationTest extends TestCase
$sut = $this->getSut($this->getDefaultSettings(), [
(new Configuration())->setName('timesheet.mode')->setValue('sdf'),
]);
$this->assertEquals(false, $sut->isDurationOnly());
$this->assertEquals('sdf', $sut->getTrackingMode());
}
public function testFindByKey()

View File

@@ -129,6 +129,7 @@ class AppExtensionTest extends TestCase
'rules' => [
'allow_future_times' => true,
],
'default_begin' => 'now',
],
'kimai.timesheet.rates' => [],
'kimai.timesheet.rounding' => [],

View File

@@ -0,0 +1,205 @@
<?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\Timesheet\TrackingMode;
use App\Entity\Timesheet;
use App\Timesheet\TrackingMode\AbstractTrackingMode;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* @covers \App\Timesheet\TrackingMode\AbstractTrackingMode
*/
abstract class AbstractTrackingModeTest extends TestCase
{
/**
* @return AbstractTrackingMode
*/
abstract protected function createSut();
public function testCreateDoesNotChangeAnythingOnEmptyRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
self::assertNull($timesheet->getBegin());
self::assertNull($timesheet->getEnd());
$sut->create($timesheet, new Request());
self::assertNull($timesheet->getBegin());
self::assertNull($timesheet->getEnd());
}
public function testCreateUseBeginWithoutEndDateFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'begin' => '2017-07-23',
]);
$sut->create($timesheet, $request);
self::assertEquals('2017-07-23', $timesheet->getBegin()->format('Y-m-d'));
self::assertNotEquals('10:00:00', $timesheet->getBegin()->format('H:i:s'));
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
public function testCreateUseBeginEndDateFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'begin' => '2017-07-23',
'end' => '2017-07-23',
]);
$sut->create($timesheet, $request);
self::assertNotNull($timesheet->getBegin());
self::assertNotNull($timesheet->getEnd());
self::assertEquals('2017-07-23 10:00:00', $timesheet->getBegin()->format('Y-m-d H:i:s'));
self::assertEquals('2017-07-23 18:00:00', $timesheet->getEnd()->format('Y-m-d H:i:s'));
self::assertEquals(28800, $timesheet->getDuration());
}
public function testCreateIgnoresValidEndOnInvalidBeginDateFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'begin' => '10x0-99-99',
'end' => '2017-07-23',
]);
$sut->create($timesheet, $request);
self::assertNull($timesheet->getBegin());
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
public function testCreateUsesBeginAndIgnoresInvalidEndDateFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'begin' => '2017-07-23',
'end' => '20xx-07-23',
]);
$sut->create($timesheet, $request);
self::assertEquals('2017-07-23', $timesheet->getBegin()->format('Y-m-d'));
self::assertNotEquals('10:00:00', $timesheet->getBegin()->format('H:i:s'));
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
public function testCreateUseFromWithoutToDatetimeFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'from' => '2018-05-23 21:47:55',
]);
$sut->create($timesheet, $request);
self::assertEquals('2018-05-23 21:47:55', $timesheet->getBegin()->format('Y-m-d H:i:s'));
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
public function testCreateUseFromToDatetimeFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'from' => '2018-05-23 21:47:55',
'to' => '2018-05-24 01:11:11',
]);
$sut->create($timesheet, $request);
self::assertNotNull($timesheet->getBegin());
self::assertNotNull($timesheet->getEnd());
self::assertEquals('2018-05-23 21:47:55', $timesheet->getBegin()->format('Y-m-d H:i:s'));
self::assertEquals('2018-05-24 01:11:11', $timesheet->getEnd()->format('Y-m-d H:i:s'));
self::assertEquals(12196, $timesheet->getDuration());
}
public function testCreateUseFromToDatetimeOverwritesBeginEndTatesFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'begin' => '2017-07-23',
'end' => '2017-07-23',
'from' => '2018-05-23 21:47:55',
'to' => '2018-05-24 01:11:11',
]);
$sut->create($timesheet, $request);
self::assertNotNull($timesheet->getBegin());
self::assertNotNull($timesheet->getEnd());
self::assertEquals('2018-05-23 21:47:55', $timesheet->getBegin()->format('Y-m-d H:i:s'));
self::assertEquals('2018-05-24 01:11:11', $timesheet->getEnd()->format('Y-m-d H:i:s'));
self::assertEquals(12196, $timesheet->getDuration());
}
public function testCreateIgnoresValidToOnInvalidFromDatetimeFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'from' => '2018-xx-23 21:47:55',
'to' => '2018-05-24 01:11:11',
]);
$sut->create($timesheet, $request);
self::assertNull($timesheet->getBegin());
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
public function testCreateUsesFromAndIgnoresInvalidToDatetimeFromRequest()
{
$sut = $this->createSut();
$timesheet = new Timesheet();
$request = new Request([
'from' => '2018-05-23 21:47:55',
'to' => '2018-xx-24 01:11:11',
]);
$sut->create($timesheet, $request);
self::assertEquals('2018-05-23 21:47:55', $timesheet->getBegin()->format('Y-m-d H:i:s'));
self::assertNull($timesheet->getEnd());
self::assertEquals(0, $timesheet->getDuration());
}
}

View File

@@ -0,0 +1,45 @@
<?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\Timesheet\TrackingMode;
use App\Configuration\TimesheetConfiguration;
use App\Tests\Configuration\TestConfigLoader;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Timesheet\TrackingMode\DefaultMode;
/**
* @covers \App\Timesheet\TrackingMode\DefaultMode
*/
class DefaultModeTest extends AbstractTrackingModeTest
{
/**
* @return DefaultMode
*/
protected function createSut()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, ['default_begin' => '13:47']);
return new DefaultMode($dateTime, $configuration);
}
public function testDefaultValues()
{
$sut = $this->createSut();
self::assertTrue($sut->canEditBegin());
self::assertTrue($sut->canEditEnd());
self::assertFalse($sut->canEditDuration());
self::assertTrue($sut->canUpdateTimesWithAPI());
self::assertTrue($sut->canSeeBeginAndEndTimes());
self::assertEquals('default', $sut->getId());
}
}

View 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\Timesheet\TrackingMode;
use App\Configuration\TimesheetConfiguration;
use App\Entity\Timesheet;
use App\Tests\Configuration\TestConfigLoader;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Timesheet\TrackingMode\DurationFixedStartMode;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* @covers \App\Timesheet\TrackingMode\DurationFixedStartMode
*/
class DurationFixedStartModeTest extends TestCase
{
protected function createSut()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, ['default_begin' => '13:47']);
return new DurationFixedStartMode($dateTime, $configuration);
}
public function testDefaultValues()
{
$sut = $this->createSut();
self::assertFalse($sut->canEditBegin());
self::assertFalse($sut->canEditEnd());
self::assertTrue($sut->canEditDuration());
self::assertFalse($sut->canUpdateTimesWithAPI());
self::assertFalse($sut->canSeeBeginAndEndTimes());
self::assertEquals('duration_fixed_start', $sut->getId());
}
public function testCreate()
{
$timesheet = new Timesheet();
$timesheet->setBegin(new \DateTime('22:54'));
$request = new Request();
$sut = $this->createSut();
self::assertEquals('22:54', $timesheet->getBegin()->format('H:i'));
$sut->create($timesheet, $request);
self::assertEquals('13:47', $timesheet->getBegin()->format('H:i'));
}
}

View File

@@ -0,0 +1,42 @@
<?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\Timesheet\TrackingMode;
use App\Configuration\TimesheetConfiguration;
use App\Tests\Configuration\TestConfigLoader;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Timesheet\TrackingMode\DurationOnlyMode;
/**
* @covers \App\Timesheet\TrackingMode\DurationOnlyMode
*/
class DurationOnlyModeTest extends AbstractTrackingModeTest
{
protected function createSut()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, []);
return new DurationOnlyMode($dateTime, $configuration);
}
public function testDefaultValues()
{
$sut = $this->createSut();
self::assertTrue($sut->canEditBegin());
self::assertFalse($sut->canEditEnd());
self::assertTrue($sut->canEditDuration());
self::assertTrue($sut->canUpdateTimesWithAPI());
self::assertFalse($sut->canSeeBeginAndEndTimes());
self::assertEquals('duration_only', $sut->getId());
}
}

View File

@@ -0,0 +1,45 @@
<?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\Timesheet\TrackingMode;
use App\Entity\Timesheet;
use App\Timesheet\TrackingMode\PunchInOutMode;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* @covers \App\Timesheet\TrackingMode\PunchInOutMode
*/
class PunchInOutModeTest extends TestCase
{
public function testDefaultValues()
{
$sut = new PunchInOutMode();
self::assertFalse($sut->canEditBegin());
self::assertFalse($sut->canEditEnd());
self::assertFalse($sut->canEditDuration());
self::assertFalse($sut->canUpdateTimesWithAPI());
self::assertTrue($sut->canSeeBeginAndEndTimes());
self::assertEquals('punch', $sut->getId());
}
public function testCreate()
{
$timesheet = new Timesheet();
$timesheet->setBegin(new \DateTime('22:54'));
$request = new Request();
$timesheetNew = clone $timesheet;
$sut = new PunchInOutMode();
$sut->create($timesheet, $request);
self::assertEquals($timesheet, $timesheetNew);
}
}

View File

@@ -0,0 +1,71 @@
<?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\Timesheet;
use App\Configuration\TimesheetConfiguration;
use App\Tests\Configuration\TestConfigLoader;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Timesheet\TrackingMode\PunchInOutMode;
use App\Timesheet\TrackingModeService;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Timesheet\TrackingModeService
*/
class TrackingModeServiceTest extends TestCase
{
public function testDefaultTrackingModesAreRegistered()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, ['mode' => 'punch']);
$sut = new TrackingModeService($dateTime, $configuration);
$modes = $sut->getModes();
self::assertGreaterThanOrEqual(4, $modes);
$ids = [];
foreach ($modes as $mode) {
$ids[] = $mode->getId();
}
self::assertContains('default', $ids);
self::assertContains('punch', $ids);
self::assertContains('duration_only', $ids);
self::assertContains('duration_fixed_start', $ids);
}
public function testGetActiveMode()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, ['mode' => 'punch']);
$sut = new TrackingModeService($dateTime, $configuration);
self::assertInstanceOf(PunchInOutMode::class, $sut->getActiveMode());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException
* @expectedExceptionMessage You have requested a non-existent service "xxxxxx"
*/
public function testGetActiveModeThrowsExceptionOnlyInvalidMode()
{
$loader = new TestConfigLoader([]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$configuration = new TimesheetConfiguration($loader, ['mode' => 'xxxxxx']);
$sut = new TrackingModeService($dateTime, $configuration);
$sut->getActiveMode();
}
}

View File

@@ -1,59 +0,0 @@
<?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\Twig;
use App\Configuration\ConfigLoaderInterface;
use App\Configuration\TimesheetConfiguration;
use App\Twig\TimesheetConfigExtension;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Twig\TimesheetConfigExtension
*/
class TimesheetConfigExtensionTest extends TestCase
{
public function testGetFunctions()
{
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
$config = new TimesheetConfiguration($loader, ['mode' => 'duration_only']);
$sut = new TimesheetConfigExtension($config);
$filters = $sut->getFunctions();
$this->assertCount(2, $filters);
$this->assertEquals('is_duration_only', $filters[0]->getName());
$this->assertEquals('is_punch_mode', $filters[1]->getName());
}
public function testIsDurationOnly()
{
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
$config = new TimesheetConfiguration($loader, ['mode' => 'duration_only']);
$sut = new TimesheetConfigExtension($config);
$this->assertTrue($sut->isDurationOnly());
$this->assertFalse($sut->isPunchInOut());
}
public function testIsNotDurationOnly()
{
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
$config = new TimesheetConfiguration($loader, ['mode' => 'default']);
$sut = new TimesheetConfigExtension($config);
$this->assertFalse($sut->isDurationOnly());
$this->assertFalse($sut->isPunchInOut());
}
public function testIsPunchInOut()
{
$loader = $this->getMockBuilder(ConfigLoaderInterface::class)->getMock();
$config = new TimesheetConfiguration($loader, ['mode' => 'punch']);
$sut = new TimesheetConfigExtension($config);
$this->assertFalse($sut->isDurationOnly());
$this->assertTrue($sut->isPunchInOut());
}
}

View File

@@ -15,6 +15,8 @@ use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Timesheet;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Timesheet\TrackingModeService;
use App\Validator\Constraints\Timesheet as TimesheetConstraint;
use App\Validator\Constraints\TimesheetValidator;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
@@ -38,8 +40,10 @@ class TimesheetValidatorTest extends ConstraintValidatorTestCase
],
'mode' => 'default',
]);
$dateTime = (new UserDateTimeFactoryFactory($this))->create();
$service = new TrackingModeService($dateTime, $config);
return new TimesheetValidator($authMock, $config);
return new TimesheetValidator($authMock, $config, $service);
}
/**