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);
|
||||
|
||||
Reference in New Issue
Block a user