added drag and drop for new records via calendar (#1962)
This commit is contained in:
@@ -67,7 +67,15 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
*/
|
||||
protected function createUrl($url, $json = true)
|
||||
{
|
||||
return '/' . ltrim($url, '/') . ($json ? '.json' : '');
|
||||
if ($json) {
|
||||
if (stripos($url, '?') !== false) {
|
||||
$url = str_replace('?', '.json?', $url);
|
||||
} else {
|
||||
$url .= '.json';
|
||||
}
|
||||
}
|
||||
|
||||
return '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET')
|
||||
@@ -453,6 +461,15 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
case 'ActivityExpanded':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'name' => 'string',
|
||||
'visible' => 'bool',
|
||||
'project' => ['result' => 'object', 'type' => '@ProjectExpanded'],
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
// collection of activities
|
||||
case 'ActivityCollection':
|
||||
return [
|
||||
@@ -502,6 +519,26 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
// TODO new fields: billable, category
|
||||
];
|
||||
|
||||
case 'TimesheetEntityFull':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'begin' => 'DateTime',
|
||||
'end' => '@DateTime',
|
||||
'duration' => '@int',
|
||||
'description' => '@string',
|
||||
'rate' => 'float',
|
||||
'activity' => ['result' => 'object', 'type' => 'ActivityExpanded'],
|
||||
'project' => ['result' => 'object', 'type' => 'ProjectExpanded'],
|
||||
'tags' => ['result' => 'array', 'type' => 'string'],
|
||||
'user' => 'int',
|
||||
'metaFields' => ['result' => 'array', 'type' => 'TimesheetMeta'],
|
||||
'internalRate' => 'float',
|
||||
'exported' => 'bool',
|
||||
'fixedRate' => '@float',
|
||||
'hourlyRate' => '@float',
|
||||
// TODO new fields: billable, category
|
||||
];
|
||||
|
||||
case 'TimesheetCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -586,8 +623,6 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
self::assertIsArray($result[$key], sprintf('Key "%s" in type "%s" is not an array', $key, $type));
|
||||
|
||||
if ($value['type'][0] === '@') {
|
||||
if (empty($result[$key])) {
|
||||
break;
|
||||
@@ -595,6 +630,8 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
$value['type'] = substr($value['type'], 1);
|
||||
}
|
||||
|
||||
self::assertIsArray($result[$key], sprintf('Key "%s" in type "%s" is not an array', $key, $type));
|
||||
|
||||
self::assertApiResponseTypeStructure($value['type'], $result[$key]);
|
||||
break;
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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\API\Serializer;
|
||||
|
||||
use App\API\Serializer\ValidationFailedExceptionErrorHandler;
|
||||
use App\Validator\ValidationFailedException;
|
||||
use JMS\Serializer\GraphNavigatorInterface;
|
||||
use JMS\Serializer\Visitor\SerializationVisitorInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Serializer\ValidationFailedExceptionErrorHandler
|
||||
*/
|
||||
class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
{
|
||||
public function testSubscribingMethods()
|
||||
{
|
||||
self::assertEquals([[
|
||||
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
|
||||
'type' => 'App\Validator\ValidationFailedException',
|
||||
'format' => 'json',
|
||||
'method' => 'serializeExceptionToJson',
|
||||
]], ValidationFailedExceptionErrorHandler::getSubscribingMethods());
|
||||
}
|
||||
|
||||
public function testWithEmptyConstraintsList()
|
||||
{
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
$validations = new ValidationFailedException($constraints, 'Uuups, that is broken');
|
||||
|
||||
$serialization = $this->createMock(SerializationVisitorInterface::class);
|
||||
$expected = [
|
||||
'code' => '400',
|
||||
'message' => null,
|
||||
'errors' => [
|
||||
'children' => []
|
||||
]
|
||||
];
|
||||
self::assertEquals($expected, $sut->serializeExceptionToJson($serialization, $validations, []));
|
||||
}
|
||||
|
||||
public function testWithConstraintsList()
|
||||
{
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator);
|
||||
$translator->method('trans')->willReturnArgument(0);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
$constraints->add(new ConstraintViolation('toooo many tests', 'abc.def', [], '$root', 'begin', 4, null, null, null, '$cause'));
|
||||
$constraints->add(new ConstraintViolation('missing tests', 'abc.def', [], '$root', 'begin', 4, null, null, null, '$cause'));
|
||||
$constraints->add(new ConstraintViolation('missing tests', 'test %wuuf% 123', ['%wuuf%' => 'xcv'], '$root', 'end', 4, 3, null, null, '$cause'));
|
||||
$validations = new ValidationFailedException($constraints, 'Uuups, that is broken');
|
||||
|
||||
$serialization = $this->createMock(SerializationVisitorInterface::class);
|
||||
$expected = [
|
||||
'code' => '400',
|
||||
'message' => 'Uuups, that is broken',
|
||||
'errors' => [
|
||||
'children' => [
|
||||
'begin' => [
|
||||
'errors' => [
|
||||
0 => 'abc.def',
|
||||
1 => 'abc.def',
|
||||
],
|
||||
],
|
||||
'end' => [
|
||||
'errors' => [
|
||||
0 => 'test %wuuf% 123',
|
||||
],
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
self::assertEquals($expected, $sut->serializeExceptionToJson($serialization, $validations, []));
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\API\BaseApiController;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
@@ -381,6 +382,30 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEquals(2016, $result['rate']);
|
||||
}
|
||||
|
||||
public function testPostActionWithFullExpandedResponse()
|
||||
{
|
||||
$dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'description' => 'foo',
|
||||
'fixedRate' => 2016,
|
||||
'hourlyRate' => 127
|
||||
];
|
||||
$this->request($client, '/api/timesheets?full=true', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntityFull', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
$this->assertTrue($result['duration'] == 57600 || $result['duration'] == 57660); // 1 minute rounding might be applied
|
||||
$this->assertEquals(2016, $result['rate']);
|
||||
}
|
||||
|
||||
public function testPostActionForDifferentUser()
|
||||
{
|
||||
$dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE);
|
||||
@@ -833,6 +858,38 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEmpty($timesheet->getTags());
|
||||
}
|
||||
|
||||
public function testRestartActionWithBegin()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
|
||||
$data = [
|
||||
'description' => 'foo',
|
||||
'tags' => 'another,testing,bar'
|
||||
];
|
||||
$this->request($client, '/api/timesheets/1', 'PATCH', [], json_encode($data));
|
||||
|
||||
$begin = new \DateTime('2019-11-27 13:55:00');
|
||||
$this->request($client, '/api/timesheets/1/restart', 'PATCH', ['begin' => $begin->format(BaseApiController::DATE_FORMAT_PHP)]);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||
$this->assertEmpty($result['description']);
|
||||
$this->assertEmpty($result['tags']);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
/** @var Timesheet $timesheet */
|
||||
$timesheet = $em->getRepository(Timesheet::class)->find($result['id']);
|
||||
$this->assertInstanceOf(\DateTime::class, $timesheet->getBegin());
|
||||
$this->assertEquals($begin->format(BaseApiController::DATE_FORMAT_PHP), $timesheet->getBegin()->format(BaseApiController::DATE_FORMAT_PHP));
|
||||
$this->assertNull($timesheet->getEnd());
|
||||
$this->assertEquals(1, $timesheet->getActivity()->getId());
|
||||
$this->assertEquals(1, $timesheet->getProject()->getId());
|
||||
$this->assertEmpty($timesheet->getDescription());
|
||||
$this->assertEmpty($timesheet->getTags());
|
||||
}
|
||||
|
||||
public function testRestartActionWithCopyData()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
@@ -9,28 +9,26 @@
|
||||
|
||||
namespace App\Tests\Calendar;
|
||||
|
||||
use App\Calendar\Source;
|
||||
use App\Calendar\GoogleSource;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Calendar\Source
|
||||
* @covers \App\Calendar\GoogleSource
|
||||
*/
|
||||
class SourceTest extends TestCase
|
||||
class GoogleSourceTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$sut = new Source();
|
||||
|
||||
$this->assertNull($sut->getId());
|
||||
$this->assertNull($sut->getColor());
|
||||
$this->assertNull($sut->getUri());
|
||||
|
||||
$this->assertInstanceOf(Source::class, $sut->setId('0815'));
|
||||
$this->assertInstanceOf(Source::class, $sut->setColor('#fffccc'));
|
||||
$this->assertInstanceOf(Source::class, $sut->setUri('askdjfhlaksjdhflaksjhdflkjasdlkfjh'));
|
||||
$sut = new GoogleSource('0815', 'askdjfhlaksjdhflaksjhdflkjasdlkfjh', '#fffccc');
|
||||
|
||||
$this->assertEquals('0815', $sut->getId());
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
$this->assertEquals('askdjfhlaksjdhflaksjhdflkjasdlkfjh', $sut->getUri());
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
|
||||
$sut = new GoogleSource('0815', 'askdjfhlaksjdhflaksjhdflkjasdlkfjh', null);
|
||||
|
||||
$this->assertEquals('0815', $sut->getId());
|
||||
$this->assertEquals('askdjfhlaksjdhflaksjhdflkjasdlkfjh', $sut->getUri());
|
||||
$this->assertNull($sut->getColor());
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace App\Tests\Calendar;
|
||||
|
||||
use App\Calendar\Google;
|
||||
use App\Calendar\Source;
|
||||
use App\Calendar\GoogleSource;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
@@ -21,8 +21,8 @@ class GoogleTest extends TestCase
|
||||
public function testConstruct()
|
||||
{
|
||||
$sources = [
|
||||
(new Source())->setId('foo')->setColor('#ccc'),
|
||||
(new Source())->setId('bar')->setUri('sdsdfsdfsdfsdffd'),
|
||||
new GoogleSource('foo', '', '#ccc'),
|
||||
new GoogleSource('bar', 'sdsdfsdfsdfsdffd'),
|
||||
];
|
||||
|
||||
$sut = new Google('qwertzuiop1234567890');
|
||||
|
||||
36
tests/Calendar/RecentActivitiesSourceTest.php
Normal file
36
tests/Calendar/RecentActivitiesSourceTest.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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\Calendar;
|
||||
|
||||
use App\Calendar\RecentActivitiesSource;
|
||||
use App\Calendar\TimesheetEntry;
|
||||
use App\Entity\Timesheet;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Calendar\RecentActivitiesSource
|
||||
*/
|
||||
class RecentActivitiesSourceTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$entries = [new TimesheetEntry(new Timesheet(), '#cccccc')];
|
||||
|
||||
$sut = new RecentActivitiesSource($entries);
|
||||
|
||||
$this->assertEquals('calendar/drag-drop.html.twig', $sut->getBlockInclude());
|
||||
$this->assertSame($entries, $sut->getEntries());
|
||||
$this->assertEquals('POST', $sut->getMethod());
|
||||
$this->assertEquals('post_timesheet', $sut->getRoute());
|
||||
$this->assertEquals(['full' => 'true'], $sut->getRouteParams());
|
||||
$this->assertEquals([], $sut->getRouteReplacer());
|
||||
$this->assertEquals('recent.activities', $sut->getTitle());
|
||||
}
|
||||
}
|
||||
99
tests/Calendar/TimesheetEntryTest.php
Normal file
99
tests/Calendar/TimesheetEntryTest.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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\Calendar;
|
||||
|
||||
use App\Calendar\TimesheetEntry;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Tag;
|
||||
use App\Entity\Timesheet;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Calendar\TimesheetEntry
|
||||
*/
|
||||
class TimesheetEntryTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$project = new Project();
|
||||
$activity = new Activity();
|
||||
$activity->setName('a wonderful activity!!');
|
||||
$activity->setProject($project);
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity($activity);
|
||||
$timesheet->setProject($project);
|
||||
$timesheet->setDescription('hello foo bar');
|
||||
$timesheet->addTag((new Tag())->setName('bulb'));
|
||||
$timesheet->addTag((new Tag())->setName('action test'));
|
||||
|
||||
$expectedData = [
|
||||
'description' => 'hello foo bar',
|
||||
'activity' => null,
|
||||
'project' => null,
|
||||
'tags' => 'bulb,action test',
|
||||
];
|
||||
|
||||
$sut = new TimesheetEntry($timesheet, '#cccccc');
|
||||
|
||||
$this->assertEquals('a wonderful activity!!', $sut->getTitle());
|
||||
$this->assertEquals('#cccccc', $sut->getColor());
|
||||
$this->assertSame($project, $sut->getProject());
|
||||
$this->assertSame($activity, $sut->getActivity());
|
||||
$this->assertEquals('dd_timesheet', $sut->getBlockName());
|
||||
$this->assertEquals($expectedData, $sut->getData());
|
||||
}
|
||||
|
||||
public function testEmpty()
|
||||
{
|
||||
$timesheet = new Timesheet();
|
||||
|
||||
$expectedData = [
|
||||
'description' => null,
|
||||
'activity' => null,
|
||||
'project' => null,
|
||||
'tags' => '',
|
||||
];
|
||||
|
||||
$sut = new TimesheetEntry($timesheet, '#ddd');
|
||||
|
||||
$this->assertEquals('', $sut->getTitle());
|
||||
$this->assertEquals('#ddd', $sut->getColor());
|
||||
$this->assertNull($sut->getProject());
|
||||
$this->assertNull($sut->getActivity());
|
||||
$this->assertEquals($expectedData, $sut->getData());
|
||||
}
|
||||
|
||||
public function testGetTitle()
|
||||
{
|
||||
$project = new Project();
|
||||
$project->setName('sdfsdf');
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setProject($project);
|
||||
|
||||
$sut = new TimesheetEntry($timesheet, '#ddd');
|
||||
self::assertEquals('sdfsdf', $sut->getTitle());
|
||||
|
||||
$project = new Project();
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setProject($project);
|
||||
|
||||
$sut = new TimesheetEntry($timesheet, '#ddd');
|
||||
self::assertEquals('', $sut->getTitle());
|
||||
|
||||
$project = new Project();
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setDescription('fooooo');
|
||||
$timesheet->setProject($project);
|
||||
|
||||
$sut = new TimesheetEntry($timesheet, '#ddd');
|
||||
self::assertEquals('fooooo', $sut->getTitle());
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase;
|
||||
/**
|
||||
* @covers \App\Configuration\CalendarConfiguration
|
||||
* @covers \App\Configuration\StringAccessibleConfigTrait
|
||||
* @group legacy
|
||||
*/
|
||||
class CalendarConfigurationTest extends TestCase
|
||||
{
|
||||
@@ -41,6 +42,10 @@ class CalendarConfigurationTest extends TestCase
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '09:00',
|
||||
'end' => '21:34',
|
||||
],
|
||||
'day_limit' => 20,
|
||||
'slot_duration' => '01:11:00',
|
||||
'week_numbers' => false,
|
||||
@@ -80,5 +85,9 @@ class CalendarConfigurationTest extends TestCase
|
||||
$this->assertEquals('wertwertwegsdfbdf243w567fg8ihuon', $sut->getGoogleApiKey());
|
||||
$sources = $sut->getGoogleSources();
|
||||
$this->assertEquals(2, \count($sources));
|
||||
|
||||
self::assertTrue($sut->isShowWeekends());
|
||||
self::assertEquals('09:00', $sut->getTimeframeBegin());
|
||||
self::assertEquals('21:34', $sut->getTimeframeEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,34 @@ class SystemConfigurationTest extends TestCase
|
||||
'country' => 'FR',
|
||||
],
|
||||
],
|
||||
'calendar' => [
|
||||
'businessHours' => [
|
||||
'days' => [2, 4, 6],
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
'day_limit' => 20,
|
||||
'slot_duration' => '01:11:00',
|
||||
'week_numbers' => false,
|
||||
'visibleHours' => [
|
||||
'begin' => '06:00:00',
|
||||
'end' => '21:00:43',
|
||||
],
|
||||
'google' => [
|
||||
'api_key' => 'wertwertwegsdfbdf243w567fg8ihuon',
|
||||
'sources' => [
|
||||
'holidays' => [
|
||||
'id' => 'de.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#ccc',
|
||||
],
|
||||
'holidays_en' => [
|
||||
'id' => 'en.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#fff',
|
||||
],
|
||||
]
|
||||
],
|
||||
'weekends' => true,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -65,6 +93,7 @@ class SystemConfigurationTest extends TestCase
|
||||
(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'),
|
||||
(new Configuration())->setName('calendar.slot_duration')->setValue('00:30:00'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -110,4 +139,30 @@ class SystemConfigurationTest extends TestCase
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('foo'));
|
||||
}
|
||||
|
||||
public function testCalendarWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals([2, 4, 6], $sut->getCalendarBusinessDays());
|
||||
$this->assertEquals('07:49', $sut->getCalendarBusinessTimeBegin());
|
||||
$this->assertEquals('19:27', $sut->getCalendarBusinessTimeEnd());
|
||||
$this->assertEquals('06:00:00', $sut->getCalendarTimeframeBegin());
|
||||
$this->assertEquals('21:00:43', $sut->getCalendarTimeframeEnd());
|
||||
$this->assertEquals('01:11:00', $sut->getCalendarSlotDuration());
|
||||
$this->assertEquals(20, $sut->getCalendarDayLimit());
|
||||
$this->assertFalse($sut->isCalendarShowWeekNumbers());
|
||||
$this->assertTrue($sut->isCalendarShowWeekends());
|
||||
|
||||
$this->assertEquals('wertwertwegsdfbdf243w567fg8ihuon', $sut->getCalendarGoogleApiKey());
|
||||
$sources = $sut->getCalendarGoogleSources();
|
||||
$this->assertEquals(2, \count($sources));
|
||||
}
|
||||
|
||||
public function testCalendarWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals('00:30:00', $sut->getCalendarSlotDuration());
|
||||
$sources = $sut->getCalendarGoogleSources();
|
||||
$this->assertEquals(2, \count($sources));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\CalendarConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Tests\Configuration\TestConfigLoader;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -25,21 +26,27 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
public function testCalendarAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$fixtures = new TimesheetFixtures($this->getUserByRole(), 10);
|
||||
$fixtures->setStartDate(new \DateTime('-6 month'));
|
||||
$this->importFixture($fixtures);
|
||||
|
||||
$this->request($client, '/calendar/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$crawler = $client->getCrawler();
|
||||
$calendar = $crawler->filter('div#timesheet_calendar');
|
||||
$this->assertEquals(1, $calendar->count());
|
||||
$dragAndDropBoxes = $crawler->filter('div.box-body.drag-and-drop-source');
|
||||
$this->assertEquals(1, $dragAndDropBoxes->count());
|
||||
}
|
||||
|
||||
public function testCalendarActionWithGoogleSource()
|
||||
{
|
||||
$loader = new TestConfigLoader([]);
|
||||
$config = new CalendarConfiguration($loader, $this->getDefaultSettings());
|
||||
$config = new SystemConfiguration($loader, $this->getDefaultSettings());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
static::$kernel->getContainer()->set(CalendarConfiguration::class, $config);
|
||||
static::$kernel->getContainer()->set(SystemConfiguration::class, $config);
|
||||
$this->request($client, '/calendar/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
@@ -57,32 +64,37 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'businessHours' => [
|
||||
'days' => [2, 4, 6],
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
'timesheet' => [
|
||||
'default_begin' => '08:30:00',
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
'calendar' => [
|
||||
'businessHours' => [
|
||||
'days' => [2, 4, 6],
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
'day_limit' => 20,
|
||||
'week_numbers' => false,
|
||||
'slot_duration' => '00:15:00',
|
||||
'google' => [
|
||||
'api_key' => 'wertwertwegsdfbdf243w567fg8ihuon',
|
||||
'sources' => [
|
||||
'holidays' => [
|
||||
'id' => 'de.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#ccc',
|
||||
],
|
||||
'holidays_en' => [
|
||||
'id' => 'en.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#fff',
|
||||
],
|
||||
]
|
||||
],
|
||||
'weekends' => true,
|
||||
],
|
||||
'day_limit' => 20,
|
||||
'week_numbers' => false,
|
||||
'slot_duration' => '00:15:00',
|
||||
'google' => [
|
||||
'api_key' => 'wertwertwegsdfbdf243w567fg8ihuon',
|
||||
'sources' => [
|
||||
'holidays' => [
|
||||
'id' => 'de.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#ccc',
|
||||
],
|
||||
'holidays_en' => [
|
||||
'id' => 'en.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#fff',
|
||||
],
|
||||
]
|
||||
],
|
||||
'weekends' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,16 @@ final class TimesheetFixtures extends Fixture
|
||||
*/
|
||||
private $tags = [];
|
||||
|
||||
public function __construct(?User $user = null, ?int $amount = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$this->setUser($user);
|
||||
}
|
||||
if ($amount !== null) {
|
||||
$this->setAmount($amount);
|
||||
}
|
||||
}
|
||||
|
||||
public function setAllowEmptyDescriptions(bool $allowEmptyDescriptions): TimesheetFixtures
|
||||
{
|
||||
$this->allowEmptyDescriptions = $allowEmptyDescriptions;
|
||||
|
||||
@@ -158,7 +158,7 @@ class AppExtensionTest extends TestCase
|
||||
'select_type' => 'selectpicker',
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => 'rgba(0,115,183,0.7)',
|
||||
'background_color' => '#3c8dbc',
|
||||
'border_color' => '#3b8bba',
|
||||
'grid_color' => 'rgba(0,0,0,.05)',
|
||||
'height' => '200'
|
||||
@@ -173,6 +173,9 @@ class AppExtensionTest extends TestCase
|
||||
'auto_reload_datatable' => false,
|
||||
'autocomplete_chars' => 3,
|
||||
'tags_create' => true,
|
||||
'calendar' => [
|
||||
'background_color' => '#d2d6de',
|
||||
],
|
||||
],
|
||||
'kimai.theme.select_type' => 'selectpicker',
|
||||
'kimai.theme.show_about' => true,
|
||||
|
||||
@@ -340,7 +340,7 @@ class ConfigurationTest extends TestCase
|
||||
'auto_reload_datatable' => false,
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => 'rgba(0,115,183,0.7)',
|
||||
'background_color' => '#3c8dbc',
|
||||
'border_color' => '#3b8bba',
|
||||
'grid_color' => 'rgba(0,0,0,.05)',
|
||||
'height' => '200',
|
||||
@@ -354,6 +354,9 @@ class ConfigurationTest extends TestCase
|
||||
],
|
||||
'autocomplete_chars' => 3,
|
||||
'tags_create' => true,
|
||||
'calendar' => [
|
||||
'background_color' => '#d2d6de'
|
||||
]
|
||||
],
|
||||
'industry' => [
|
||||
'translation' => null,
|
||||
|
||||
@@ -35,6 +35,7 @@ class ActivityTest extends TestCase
|
||||
$this->assertTrue($sut->isVisible());
|
||||
$this->assertTrue($sut->isGlobal());
|
||||
$this->assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
$this->assertEquals(0.0, $sut->getBudget());
|
||||
$this->assertEquals(0, $sut->getTimeBudget());
|
||||
$this->assertInstanceOf(Collection::class, $sut->getMetaFields());
|
||||
@@ -56,11 +57,14 @@ class ActivityTest extends TestCase
|
||||
$this->assertInstanceOf(Activity::class, $sut->setComment('hello world'));
|
||||
$this->assertEquals('hello world', $sut->getComment());
|
||||
|
||||
self::assertFalse($sut->hasColor());
|
||||
$this->assertInstanceOf(Activity::class, $sut->setColor('#fffccc'));
|
||||
$this->assertEquals('#fffccc', $sut->getColor());
|
||||
self::assertTrue($sut->hasColor());
|
||||
|
||||
$this->assertInstanceOf(Activity::class, $sut->setColor(Constants::DEFAULT_COLOR));
|
||||
$this->assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
|
||||
$this->assertInstanceOf(Activity::class, $sut->setBudget(12345.67));
|
||||
$this->assertEquals(12345.67, $sut->getBudget());
|
||||
|
||||
@@ -48,6 +48,7 @@ class CustomerTest extends TestCase
|
||||
self::assertNull($sut->getTimezone());
|
||||
|
||||
self::assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
self::assertEquals(0.0, $sut->getBudget());
|
||||
self::assertEquals(0, $sut->getTimeBudget());
|
||||
self::assertInstanceOf(Collection::class, $sut->getMetaFields());
|
||||
@@ -70,11 +71,14 @@ class CustomerTest extends TestCase
|
||||
self::assertInstanceOf(Customer::class, $sut->setComment('hello world'));
|
||||
self::assertEquals('hello world', $sut->getComment());
|
||||
|
||||
self::assertFalse($sut->hasColor());
|
||||
self::assertInstanceOf(Customer::class, $sut->setColor('#fffccc'));
|
||||
self::assertEquals('#fffccc', $sut->getColor());
|
||||
self::assertTrue($sut->hasColor());
|
||||
|
||||
self::assertInstanceOf(Customer::class, $sut->setColor(Constants::DEFAULT_COLOR));
|
||||
self::assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
|
||||
self::assertInstanceOf(Customer::class, $sut->setCompany('test company'));
|
||||
self::assertEquals('test company', $sut->getCompany());
|
||||
|
||||
@@ -38,6 +38,7 @@ class ProjectTest extends TestCase
|
||||
self::assertNull($sut->getComment());
|
||||
self::assertTrue($sut->isVisible());
|
||||
self::assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
self::assertEquals(0.0, $sut->getBudget());
|
||||
self::assertEquals(0, $sut->getTimeBudget());
|
||||
self::assertInstanceOf(Collection::class, $sut->getMetaFields());
|
||||
@@ -80,11 +81,14 @@ class ProjectTest extends TestCase
|
||||
self::assertInstanceOf(Project::class, $sut->setComment('a comment'));
|
||||
self::assertEquals('a comment', $sut->getComment());
|
||||
|
||||
self::assertFalse($sut->hasColor());
|
||||
self::assertInstanceOf(Project::class, $sut->setColor('#fffccc'));
|
||||
self::assertEquals('#fffccc', $sut->getColor());
|
||||
self::assertTrue($sut->hasColor());
|
||||
|
||||
self::assertInstanceOf(Project::class, $sut->setColor(Constants::DEFAULT_COLOR));
|
||||
self::assertNull($sut->getColor());
|
||||
self::assertFalse($sut->hasColor());
|
||||
|
||||
self::assertInstanceOf(Project::class, $sut->setVisible(false));
|
||||
self::assertFalse($sut->isVisible());
|
||||
|
||||
73
tests/Event/CalendarDragAndDropSourceEventTest.php
Normal file
73
tests/Event/CalendarDragAndDropSourceEventTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?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\Event;
|
||||
|
||||
use App\Calendar\DragAndDropSource;
|
||||
use App\Entity\User;
|
||||
use App\Event\CalendarDragAndDropSourceEvent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Event\CalendarDragAndDropSourceEvent
|
||||
*/
|
||||
class CalendarDragAndDropSourceEventTest extends TestCase
|
||||
{
|
||||
public function testGetterAndSetter()
|
||||
{
|
||||
$user = new User();
|
||||
$user->setAlias('foo');
|
||||
|
||||
$sut = new CalendarDragAndDropSourceEvent($user);
|
||||
|
||||
self::assertSame($user, $sut->getUser());
|
||||
self::assertIsArray($sut->getSources());
|
||||
self::assertEmpty($sut->getSources());
|
||||
self::assertInstanceOf(CalendarDragAndDropSourceEvent::class, $sut->addSource(new TestDragAndDropSource()));
|
||||
self::assertCount(1, $sut->getSources());
|
||||
}
|
||||
}
|
||||
|
||||
class TestDragAndDropSource implements DragAndDropSource
|
||||
{
|
||||
public function getTitle(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRoute(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRouteParams(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getRouteReplacer(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getEntries(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getBlockInclude(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
35
tests/Event/CalendarGoogleSourceEventTest.php
Normal file
35
tests/Event/CalendarGoogleSourceEventTest.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\Event;
|
||||
|
||||
use App\Calendar\GoogleSource;
|
||||
use App\Entity\User;
|
||||
use App\Event\CalendarGoogleSourceEvent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Event\CalendarGoogleSourceEvent
|
||||
*/
|
||||
class CalendarGoogleSourceEventTest extends TestCase
|
||||
{
|
||||
public function testGetterAndSetter()
|
||||
{
|
||||
$user = new User();
|
||||
$user->setAlias('foo');
|
||||
|
||||
$sut = new CalendarGoogleSourceEvent($user);
|
||||
|
||||
self::assertSame($user, $sut->getUser());
|
||||
self::assertIsArray($sut->getSources());
|
||||
self::assertEmpty($sut->getSources());
|
||||
self::assertInstanceOf(CalendarGoogleSourceEvent::class, $sut->addSource(new GoogleSource('', '')));
|
||||
self::assertCount(1, $sut->getSources());
|
||||
}
|
||||
}
|
||||
35
tests/Event/TimesheetRestartPostEventTest.php
Normal file
35
tests/Event/TimesheetRestartPostEventTest.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\Event;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Event\AbstractTimesheetEvent;
|
||||
use App\Event\TimesheetRestartPostEvent;
|
||||
|
||||
/**
|
||||
* @covers \App\Event\TimesheetRestartPostEvent
|
||||
*/
|
||||
class TimesheetRestartPostEventTest extends AbstractTimesheetEventTest
|
||||
{
|
||||
protected function createTimesheetEvent(Timesheet $timesheet): AbstractTimesheetEvent
|
||||
{
|
||||
return new TimesheetRestartPostEvent($timesheet, new Timesheet());
|
||||
}
|
||||
|
||||
public function testGetOriginalTimesheet()
|
||||
{
|
||||
$newTimesheet = new Timesheet();
|
||||
$originalTimesheet = new Timesheet();
|
||||
$sut = new TimesheetRestartPostEvent($newTimesheet, $originalTimesheet);
|
||||
|
||||
self::assertSame($newTimesheet, $sut->getTimesheet());
|
||||
self::assertSame($originalTimesheet, $sut->getOriginalTimesheet());
|
||||
}
|
||||
}
|
||||
35
tests/Event/TimesheetRestartPreEventTest.php
Normal file
35
tests/Event/TimesheetRestartPreEventTest.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\Event;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Event\AbstractTimesheetEvent;
|
||||
use App\Event\TimesheetRestartPreEvent;
|
||||
|
||||
/**
|
||||
* @covers \App\Event\TimesheetRestartPreEvent
|
||||
*/
|
||||
class TimesheetRestartPreEventTest extends AbstractTimesheetEventTest
|
||||
{
|
||||
protected function createTimesheetEvent(Timesheet $timesheet): AbstractTimesheetEvent
|
||||
{
|
||||
return new TimesheetRestartPreEvent($timesheet, new Timesheet());
|
||||
}
|
||||
|
||||
public function testGetOriginalTimesheet()
|
||||
{
|
||||
$newTimesheet = new Timesheet();
|
||||
$originalTimesheet = new Timesheet();
|
||||
$sut = new TimesheetRestartPreEvent($newTimesheet, $originalTimesheet);
|
||||
|
||||
self::assertSame($newTimesheet, $sut->getTimesheet());
|
||||
self::assertSame($originalTimesheet, $sut->getOriginalTimesheet());
|
||||
}
|
||||
}
|
||||
61
tests/Form/Type/APIDateTimeTypeTest.php
Normal file
61
tests/Form/Type/APIDateTimeTypeTest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use App\Form\Type\APIDateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Form\Type\APIDateTimeType
|
||||
*/
|
||||
class APIDateTimeTypeTest extends TypeTestCase
|
||||
{
|
||||
public function testSubmitValidData()
|
||||
{
|
||||
$data = ['date' => '2020-09-17T13:24:56'];
|
||||
$model = new TypeTestModel(['date' => new \DateTime()]);
|
||||
|
||||
$form = $this->factory->createBuilder(FormType::class, $model);
|
||||
$form->add('date', APIDateTimeType::class);
|
||||
$form = $form->getForm();
|
||||
|
||||
$expected = new TypeTestModel([
|
||||
'date' => new \DateTime('2020-09-17T13:24:56')
|
||||
]);
|
||||
|
||||
$form->submit($data);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->assertEquals($expected, $model);
|
||||
}
|
||||
|
||||
public function testAttributes()
|
||||
{
|
||||
$data = ['date' => '2020-09-17T13:24:56'];
|
||||
$model = new TypeTestModel(['date' => new \DateTime()]);
|
||||
|
||||
$form = $this->factory->createBuilder(FormType::class, $model);
|
||||
$form->add('date', APIDateTimeType::class, [
|
||||
'model_timezone' => 'Pacific/Tongatapu',
|
||||
'view_timezone' => 'Pacific/Tongatapu',
|
||||
]);
|
||||
$form = $form->getForm();
|
||||
|
||||
$expected = new TypeTestModel([
|
||||
'date' => new \DateTime('2020-09-17T13:24:56', new \DateTimeZone('Pacific/Tongatapu'))
|
||||
]);
|
||||
|
||||
$form->submit($data);
|
||||
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->assertEquals($expected, $model);
|
||||
}
|
||||
}
|
||||
38
tests/Form/Type/TypeTestModel.php
Normal file
38
tests/Form/Type/TypeTestModel.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
class TypeTestModel
|
||||
{
|
||||
private $fields = [];
|
||||
|
||||
public function __construct(array $fields = [])
|
||||
{
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if (!isset($this->fields[$name])) {
|
||||
throw new \InvalidArgumentException('Unknown field: ' . $name);
|
||||
}
|
||||
|
||||
$this->fields[$name] = $value;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (!isset($this->fields[$name])) {
|
||||
throw new \InvalidArgumentException('Unknown field: ' . $name);
|
||||
}
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,9 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
$this->assertIsArray($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testStoppedEntriesCannotBeStoppedAgain()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
@@ -72,6 +75,9 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
|
||||
$repository->stopRecording($entities[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testStopRecording()
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
244
tests/Timesheet/TimesheetServiceTest.php
Normal file
244
tests/Timesheet/TimesheetServiceTest.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?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\Entity\Timesheet;
|
||||
use App\Event\TimesheetCreatePostEvent;
|
||||
use App\Event\TimesheetCreatePreEvent;
|
||||
use App\Event\TimesheetDeleteMultiplePreEvent;
|
||||
use App\Event\TimesheetDeletePreEvent;
|
||||
use App\Event\TimesheetRestartPostEvent;
|
||||
use App\Event\TimesheetRestartPreEvent;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Timesheet\TimesheetService;
|
||||
use App\Timesheet\TrackingModeService;
|
||||
use App\Validator\ValidationException;
|
||||
use App\Validator\ValidationFailedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\Timesheet\TimesheetService
|
||||
*/
|
||||
class TimesheetServiceTest extends TestCase
|
||||
{
|
||||
private function getSut(
|
||||
?AuthorizationCheckerInterface $authorizationChecker = null,
|
||||
?EventDispatcherInterface $dispatcher = null,
|
||||
?ValidatorInterface $validator = null,
|
||||
?TimesheetRepository $repository = null
|
||||
): TimesheetService {
|
||||
$configuration = $this->createMock(TimesheetConfiguration::class);
|
||||
$configuration->method('getActiveEntriesHardLimit')->willReturn(1);
|
||||
|
||||
if ($repository === null) {
|
||||
$repository = $this->createMock(TimesheetRepository::class);
|
||||
$repository->method('getActiveEntries')->willReturn([]);
|
||||
}
|
||||
|
||||
$service = new TrackingModeService($configuration, []);
|
||||
if ($dispatcher === null) {
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
}
|
||||
if ($authorizationChecker === null) {
|
||||
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
}
|
||||
if ($validator === null) {
|
||||
$validator = $this->createMock(ValidatorInterface::class);
|
||||
$validator->method('validate')->willReturn(new ConstraintViolationList());
|
||||
}
|
||||
|
||||
$service = new TimesheetService($configuration, $repository, $service, $dispatcher, $authorizationChecker, $validator);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function testCannotSavePersistedTimesheetAsNew()
|
||||
{
|
||||
$timesheet = $this->createMock(Timesheet::class);
|
||||
$timesheet->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Cannot create timesheet, already persisted');
|
||||
|
||||
$sut->saveNewTimesheet($timesheet);
|
||||
}
|
||||
|
||||
public function testCannotStartTimesheet()
|
||||
{
|
||||
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false);
|
||||
|
||||
$sut = $this->getSut($authorizationChecker);
|
||||
|
||||
$this->expectException(AccessDeniedHttpException::class);
|
||||
$this->expectExceptionMessage('You are not allowed to start this timesheet record');
|
||||
|
||||
$sut->saveNewTimesheet(new Timesheet());
|
||||
}
|
||||
|
||||
public function testSaveNewTimesheetHasValidationError()
|
||||
{
|
||||
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
$constraints->add(new ConstraintViolation('toooo many tests', 'abc.def', [], '$root', 'begin', 4, null, null, null, '$cause'));
|
||||
|
||||
$validator = $this->createMock(ValidatorInterface::class);
|
||||
$validator->method('validate')->willReturn($constraints);
|
||||
|
||||
$sut = $this->getSut($authorizationChecker, null, $validator);
|
||||
|
||||
$this->expectException(ValidationFailedException::class);
|
||||
$this->expectExceptionMessage('Validation Failed');
|
||||
|
||||
$sut->saveNewTimesheet(new Timesheet());
|
||||
}
|
||||
|
||||
public function testSaveNewTimesheetStopsActiveRecords()
|
||||
{
|
||||
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true);
|
||||
|
||||
$timesheet1 = $this->createMock(Timesheet::class);
|
||||
$timesheet1->method('getId')->willReturn(1);
|
||||
$timesheet1->method('getBegin')->willReturn(new \DateTime());
|
||||
$timesheet1->expects($this->once())->method('setBegin');
|
||||
$timesheet1->expects($this->once())->method('setEnd');
|
||||
|
||||
$timesheet2 = $this->createMock(Timesheet::class);
|
||||
$timesheet2->method('getId')->willReturn(1);
|
||||
$timesheet2->method('getBegin')->willReturn(new \DateTime());
|
||||
$timesheet2->expects($this->once())->method('setBegin');
|
||||
$timesheet2->expects($this->once())->method('setEnd');
|
||||
|
||||
$repository = $this->createMock(TimesheetRepository::class);
|
||||
$repository->method('getActiveEntries')->willReturn([$timesheet1, $timesheet2]);
|
||||
|
||||
$sut = $this->getSut($authorizationChecker, null, null, $repository);
|
||||
|
||||
$sut->saveNewTimesheet(new Timesheet());
|
||||
}
|
||||
|
||||
public function testCannotRestartedPersistedTimesheet()
|
||||
{
|
||||
$timesheet = $this->createMock(Timesheet::class);
|
||||
$timesheet->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->method('dispatch')->willReturnCallback(function ($event) {
|
||||
self::assertInstanceOf(TimesheetRestartPreEvent::class, $event);
|
||||
});
|
||||
|
||||
$sut = $this->getSut(null, $dispatcher);
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Cannot create timesheet, already persisted');
|
||||
|
||||
$sut->restartTimesheet($timesheet, new Timesheet());
|
||||
}
|
||||
|
||||
public function testRestartTimesheetDispatchesTwoEvents()
|
||||
{
|
||||
$timesheet = $this->createMock(Timesheet::class);
|
||||
$authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
|
||||
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true);
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->method('dispatch')->willReturnCallback(function ($event) {
|
||||
static $counter = 0;
|
||||
switch ($counter++) {
|
||||
case 0:
|
||||
self::assertInstanceOf(TimesheetRestartPreEvent::class, $event);
|
||||
break;
|
||||
case 1:
|
||||
self::assertInstanceOf(TimesheetCreatePreEvent::class, $event);
|
||||
break;
|
||||
case 2:
|
||||
self::assertInstanceOf(TimesheetCreatePostEvent::class, $event);
|
||||
break;
|
||||
case 3:
|
||||
self::assertInstanceOf(TimesheetRestartPostEvent::class, $event);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$sut = $this->getSut($authorizationChecker, $dispatcher);
|
||||
|
||||
$sut->restartTimesheet($timesheet, new Timesheet());
|
||||
}
|
||||
|
||||
public function testPreparePersistedTimesheetAsNew()
|
||||
{
|
||||
$timesheet = $this->createMock(Timesheet::class);
|
||||
$timesheet->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Cannot prepare timesheet, already persisted');
|
||||
|
||||
$sut->prepareNewTimesheet($timesheet);
|
||||
}
|
||||
|
||||
public function testStoppedEntriesCannotBeStoppedAgain()
|
||||
{
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setEnd(new \DateTime());
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$this->expectExceptionMessage('Timesheet entry already stopped');
|
||||
|
||||
$sut->stopTimesheet($timesheet);
|
||||
}
|
||||
|
||||
public function testDeleteDispatchesEvent()
|
||||
{
|
||||
$timesheet = new Timesheet();
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->method('dispatch')->willReturnCallback(function ($event) use ($timesheet) {
|
||||
self::assertInstanceOf(TimesheetDeletePreEvent::class, $event);
|
||||
/* @var TimesheetDeletePreEvent $event */
|
||||
self::assertSame($timesheet, $event->getTimesheet());
|
||||
});
|
||||
|
||||
$sut = $this->getSut(null, $dispatcher);
|
||||
|
||||
$sut->deleteTimesheet($timesheet);
|
||||
}
|
||||
|
||||
public function testDeleteMultipleDispatchesEvent()
|
||||
{
|
||||
$timesheets = [new Timesheet(), new Timesheet()];
|
||||
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->method('dispatch')->willReturnCallback(function ($event) use ($timesheets) {
|
||||
self::assertInstanceOf(TimesheetDeleteMultiplePreEvent::class, $event);
|
||||
/* @var TimesheetDeleteMultiplePreEvent $event */
|
||||
self::assertSame($timesheets, $event->getTimesheets());
|
||||
});
|
||||
|
||||
$sut = $this->getSut(null, $dispatcher);
|
||||
|
||||
$sut->deleteMultipleTimesheets($timesheets);
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@
|
||||
|
||||
namespace App\Tests\Twig;
|
||||
|
||||
use App\Constants;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\User;
|
||||
use App\Twig\Extensions;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -30,7 +29,7 @@ class ExtensionsTest extends TestCase
|
||||
|
||||
public function testGetFilters()
|
||||
{
|
||||
$filters = ['docu_link', 'multiline_indent', 'color'];
|
||||
$filters = ['docu_link', 'multiline_indent', 'color', 'font_contrast'];
|
||||
$sut = $this->getSut();
|
||||
$twigFilters = $sut->getFilters();
|
||||
$this->assertCount(\count($filters), $twigFilters);
|
||||
@@ -122,39 +121,30 @@ sdfsdf' . PHP_EOL . "\n" .
|
||||
self::assertEquals(implode("\n", $expected), $sut->multilineIndent($string, $indent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Just a very short test, as this delegates to Utils/Color
|
||||
*/
|
||||
public function testColor()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$globalActivity = new Activity();
|
||||
self::assertNull($sut->color($globalActivity));
|
||||
self::assertEquals(Constants::DEFAULT_COLOR, $sut->color($globalActivity, true));
|
||||
|
||||
$globalActivity->setColor('#000001');
|
||||
self::assertEquals('#000001', $sut->color($globalActivity));
|
||||
self::assertEquals('#000001', $sut->color($globalActivity, true));
|
||||
}
|
||||
|
||||
$customer = new Customer();
|
||||
self::assertNull($sut->color($customer));
|
||||
/**
|
||||
* Just a very short test, as this delegates to Utils/Color
|
||||
*/
|
||||
public function testFontContrast()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$customer->setColor('#000004');
|
||||
self::assertEquals('#000004', $sut->color($customer));
|
||||
|
||||
$project = new Project();
|
||||
self::assertNull($sut->color($project));
|
||||
|
||||
$project->setCustomer($customer);
|
||||
self::assertEquals('#000004', $sut->color($project));
|
||||
|
||||
$project->setColor('#000003');
|
||||
self::assertEquals('#000003', $sut->color($project));
|
||||
|
||||
$activity = new Activity();
|
||||
self::assertNull($sut->color($activity));
|
||||
|
||||
$activity->setProject($project);
|
||||
self::assertEquals('#000003', $sut->color($activity));
|
||||
|
||||
$activity->setColor('#000002');
|
||||
self::assertEquals('#000002', $sut->color($activity));
|
||||
self::assertEquals('#000000', $sut->calculateFontContrastColor('#ccc'));
|
||||
}
|
||||
|
||||
public function testIsoDayByName()
|
||||
|
||||
111
tests/Utils/ColorTest.php
Normal file
111
tests/Utils/ColorTest.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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\Utils;
|
||||
|
||||
use App\Constants;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Utils\Color;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Utils\Color
|
||||
*/
|
||||
class ColorTest extends TestCase
|
||||
{
|
||||
public function testGetColorAndGetTimesheetColor()
|
||||
{
|
||||
$sut = new Color();
|
||||
|
||||
$globalActivity = new Activity();
|
||||
self::assertNull($sut->getColor($globalActivity));
|
||||
|
||||
$globalActivity->setColor('#000001');
|
||||
self::assertEquals('#000001', $sut->getColor($globalActivity));
|
||||
|
||||
$customer = new Customer();
|
||||
self::assertNull($sut->getColor($customer));
|
||||
|
||||
$customer->setColor('#000004');
|
||||
self::assertEquals('#000004', $sut->getColor($customer));
|
||||
|
||||
$project = new Project();
|
||||
self::assertNull($sut->getColor($project));
|
||||
|
||||
$project->setCustomer($customer);
|
||||
self::assertEquals('#000004', $sut->getColor($project));
|
||||
|
||||
$project->setColor('#000003');
|
||||
self::assertEquals('#000003', $sut->getColor($project));
|
||||
|
||||
$activity = new Activity();
|
||||
self::assertNull($sut->getColor($activity));
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity($activity);
|
||||
$timesheet->setProject($project);
|
||||
self::assertEquals('#000003', $sut->getColor($timesheet));
|
||||
self::assertEquals('#000003', $sut->getTimesheetColor($timesheet));
|
||||
|
||||
$activity->setProject($project);
|
||||
self::assertEquals('#000003', $sut->getColor($activity));
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity($activity);
|
||||
$timesheet->setProject($project);
|
||||
self::assertEquals('#000003', $sut->getColor($timesheet));
|
||||
self::assertEquals('#000003', $sut->getTimesheetColor($timesheet));
|
||||
|
||||
$activity->setColor('#000002');
|
||||
self::assertEquals('#000002', $sut->getColor($activity));
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity($activity);
|
||||
$timesheet->setProject($project);
|
||||
self::assertEquals('#000002', $sut->getColor($timesheet));
|
||||
self::assertEquals('#000002', $sut->getTimesheetColor($timesheet));
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
self::assertEquals(Constants::DEFAULT_COLOR, $sut->getTimesheetColor($timesheet));
|
||||
self::assertNull($sut->getColor($timesheet));
|
||||
self::assertEquals(Constants::DEFAULT_COLOR, $sut->getColor($timesheet, true));
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity(new Activity());
|
||||
$project = new Project();
|
||||
$customer = new Customer();
|
||||
$customer->setColor('#123456');
|
||||
$project->setCustomer($customer);
|
||||
$timesheet->setProject($project);
|
||||
self::assertEquals('#123456', $sut->getColor($timesheet, true));
|
||||
}
|
||||
|
||||
public function testGetFontContrastColor()
|
||||
{
|
||||
$sut = new Color();
|
||||
$this->assertEquals('#ffffff', $sut->getFontContrastColor('#666'));
|
||||
$this->assertEquals('#ffffff', $sut->getFontContrastColor('#666666'));
|
||||
$this->assertEquals('#ffffff', $sut->getFontContrastColor('#000000'));
|
||||
$this->assertEquals('#000000', $sut->getFontContrastColor('#ccc'));
|
||||
$this->assertEquals('#000000', $sut->getFontContrastColor('#cccccc'));
|
||||
$this->assertEquals('#000000', $sut->getFontContrastColor('#ffffff'));
|
||||
}
|
||||
|
||||
public function testGetFontContrastColorThrowsExceptionOnNonHexadecimalColor()
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Invalid color code given, only #hexadecimal is supported.');
|
||||
|
||||
$sut = new Color();
|
||||
$sut->getFontContrastColor('000000');
|
||||
}
|
||||
}
|
||||
33
tests/Validator/ValidationExceptionTest.php
Normal file
33
tests/Validator/ValidationExceptionTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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;
|
||||
|
||||
use App\Validator\ValidationException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Validator\ValidationException
|
||||
*/
|
||||
class ValidationExceptionTest extends TestCase
|
||||
{
|
||||
public function testException()
|
||||
{
|
||||
$sut = new ValidationException();
|
||||
self::assertEquals(400, $sut->getCode());
|
||||
self::assertEquals('Validation failed', $sut->getMessage());
|
||||
}
|
||||
|
||||
public function testConstruct()
|
||||
{
|
||||
$sut = new ValidationException('Something went wrong');
|
||||
self::assertEquals(400, $sut->getCode());
|
||||
self::assertEquals('Something went wrong', $sut->getMessage());
|
||||
}
|
||||
}
|
||||
38
tests/Validator/ValidationFailedExceptionTest.php
Normal file
38
tests/Validator/ValidationFailedExceptionTest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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;
|
||||
|
||||
use App\Validator\ValidationFailedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
|
||||
/**
|
||||
* @covers \App\Validator\ValidationFailedException
|
||||
*/
|
||||
class ValidationFailedExceptionTest extends TestCase
|
||||
{
|
||||
public function testException()
|
||||
{
|
||||
$list = new ConstraintViolationList();
|
||||
$sut = new ValidationFailedException($list);
|
||||
self::assertEquals(400, $sut->getCode());
|
||||
self::assertEquals('Validation failed', $sut->getMessage());
|
||||
self::assertSame($list, $sut->getViolations());
|
||||
}
|
||||
|
||||
public function testConstruct()
|
||||
{
|
||||
$list = new ConstraintViolationList();
|
||||
$sut = new ValidationFailedException($list, 'Something went wrong');
|
||||
self::assertEquals(400, $sut->getCode());
|
||||
self::assertEquals('Something went wrong', $sut->getMessage());
|
||||
self::assertSame($list, $sut->getViolations());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user