added tags for timesheets (#604)

This commit is contained in:
Mathias
2019-05-12 01:40:04 +02:00
committed by Kevin Papst
parent f31118292c
commit e29e183e84
84 changed files with 2132 additions and 158 deletions

View File

@@ -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;
use App\Entity\User;
use App\Tests\DataFixtures\TagFixtures;
use Symfony\Component\HttpFoundation\Response;
/**
* @coversDefaultClass \App\API\TagController
* @group integration
*/
class TagControllerTest extends APIControllerBaseTest
{
public function setUp()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$tagList = ['Test', 'Administration', 'Support', '#2018-001', '#2018-002', '#2018-003', 'Development',
'Marketing', 'First Level Support', 'Bug Fixing'];
$fixture = new TagFixtures();
$fixture->setTagArray($tagList);
$this->importFixture($em, $fixture);
}
public function testGetCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/tags');
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(10, count($result));
$this->assertEquals('Test', $result[9]);
}
public function testEmptyCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$query = ['name' => 'nothing'];
$this->assertAccessIsGranted($client, '/api/tags', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertEmpty($result);
$this->assertEquals(0, count($result));
}
public function testPartOfEntries()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$query = ['name' => 'in'];
$this->assertAccessIsGranted($client, '/api/tags', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(3, count($result));
$this->assertEquals('Administration', $result[0]);
$this->assertEquals('Bug Fixing', $result[1]);
$this->assertEquals('Marketing', $result[2]);
}
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/api/tags/1', 'DELETE');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
$this->assertEmpty($client->getResponse()->getContent());
$this->assertAccessIsGranted($client, '/api/tags');
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals(9, count($result));
}
}

View File

@@ -586,10 +586,55 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertApiResponseAccessDenied($client->getResponse(), 'You are not allowed to stop this timesheet');
}
public function testGetCollectionWithTags()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TimesheetFixtures();
$fixture
->setFixedRate(true)
->setHourlyRate(true)
->setAmount(10)
->setUser($this->getUserByRole($em, User::ROLE_USER))
->setStartDate(new \DateTime('-10 days'))
->setAllowEmptyDescriptions(false)
->setUseTags(true)
->setTags(['Test', 'Administration']);
$this->importFixture($em, $fixture);
$query = ['tags' => 'Test'];
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(5, count($result));
$this->assertDefaultStructure($result[0], false);
$query = ['tags' => 'Test,Admin'];
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(10, count($result));
$this->assertDefaultStructure($result[0], false);
$query = ['tags' => 'Nothing'];
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(20, count($result));
$this->assertDefaultStructure($result[0], false);
}
protected function assertDefaultStructure(array $result, $full = true)
{
$expectedKeys = [
'id', 'begin', 'end', 'duration', 'description', 'rate', 'activity', 'project', 'user'
'id', 'begin', 'end', 'duration', 'description', 'rate', 'activity', 'project', 'tags', 'user'
];
if ($full) {
@@ -622,5 +667,4 @@ class TimesheetControllerTest extends APIControllerBaseTest
$this->assertArrayHasKey('name', $result['project']['customer']);
$this->assertArrayHasKey('visible', $result['project']['customer']);
}
}

View File

@@ -13,6 +13,7 @@ use App\Calendar\TimesheetEntity;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Tag;
use App\Entity\Timesheet;
use PHPUnit\Framework\TestCase;
@@ -36,12 +37,15 @@ class TimesheetEntityTest extends TestCase
$timesheet = new Timesheet();
$timesheet->setActivity($activity);
$timesheet->setProject($project);
$timesheet->addTag((new Tag())->setName('foo'));
$timesheet->addTag((new Tag())->setName('bar'));
$sut = new TimesheetEntity($timesheet);
$this->assertEquals('customer', $sut->getCustomer());
$this->assertEquals('project', $sut->getProject());
$this->assertEquals('activity', $sut->getTitle());
$this->assertEquals('foo, bar', $sut->getTags());
$sut->setId(13);
$this->assertEquals(13, $sut->getId());
@@ -66,6 +70,9 @@ class TimesheetEntityTest extends TestCase
$sut->setActivity('cccccccc');
$this->assertEquals('cccccccc', $sut->getActivity());
$sut->setTags('hello, world');
$this->assertEquals('hello, world', $sut->getTags());
$this->assertNull($sut->getBorderColor());
$sut->setBorderColor('#cccccc');
$this->assertEquals('#cccccc', $sut->getBorderColor());

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\User;
use App\Tests\DataFixtures\TagFixtures;
/**
* @coversDefaultClass \App\Controller\TagController
* @group integration
*/
class TagControllerTest extends ControllerBaseTest
{
public function setUp()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$tagList = ['Test', 'Administration', 'Support', '#2018-001', '#2018-002', '#2018-003', 'Development',
'Marketing', 'First Level Support', 'Bug Fixing'];
$fixture = new TagFixtures();
$fixture->setTagArray($tagList);
$this->importFixture($em, $fixture);
}
public function testDebugIsSecure()
{
$this->assertUrlIsSecured('/admin/tags/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$this->assertAccessIsGranted($client, '/admin/tags/');
$this->assertHasDataTable($client);
$this->assertDataTableRowCount($client, 'datatable_admin_tags', 10);
}
}

View File

@@ -303,7 +303,8 @@ class TimesheetControllerTest extends ControllerBaseTest
$form = $client->getCrawler()->filter('form[name=timesheet_edit_form]')->form();
$client->submit($form, [
'timesheet_edit_form' => [
'description' => 'foo-bar'
'description' => 'foo-bar',
'tags' => 'foo,bar, testing, hello world,,',
]
]);

View File

@@ -0,0 +1,68 @@
<?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\DataFixtures;
use App\Entity\Tag;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
/**
* Defines the sample data to load in during controller tests.
*/
class TagFixtures extends Fixture
{
/**
* @var string[]
*/
protected $tagArray = [];
/**
* @return string[]
*/
public function getTagArray()
{
return $this->tagArray;
}
/**
* @param string[] $tagArray
* @return TagFixtures
*/
public function setTagArray(array $tagArray)
{
$this->tagArray = $tagArray;
return $this;
}
/**
* {@inheritdoc}
*/
public function load(ObjectManager $manager)
{
foreach ($this->getTagArray() as $tagName) {
$entry = $this->createTagEntry($tagName);
$manager->persist($entry);
}
$manager->flush();
}
/**
* @param $tagName
* @return Tag
*/
protected function createTagEntry($tagName)
{
$tagObject = new Tag();
$tagObject->setName($tagName);
return $tagObject;
}
}

View File

@@ -11,6 +11,7 @@ namespace App\Tests\DataFixtures;
use App\Entity\Activity;
use App\Entity\Project;
use App\Entity\Tag;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Entity\UserPreference;
@@ -60,6 +61,14 @@ class TimesheetFixtures extends Fixture
* @var int
*/
protected $exported = false;
/**
* @var bool
*/
protected $useTags = false;
/**
* @var array
*/
protected $tags = [];
/**
* @param bool $allowEmptyDescriptions
@@ -163,6 +172,28 @@ class TimesheetFixtures extends Fixture
return $this;
}
/**
* @param bool $useTags
* @return TimesheetFixtures
*/
public function setUseTags(bool $useTags)
{
$this->useTags = $useTags;
return $this;
}
/**
* @param array $tags
* @return TimesheetFixtures
*/
public function setTags(array $tags)
{
$this->tags = $tags;
return $this;
}
/**
* {@inheritdoc}
*/
@@ -195,12 +226,15 @@ class TimesheetFixtures extends Fixture
$project = $projects[array_rand($projects)];
}
$tags = $this->getTagObjectList($i);
$entry = $this->createTimesheetEntry(
$user,
$activity,
$project,
$description,
$this->getDateTime($i)
$this->getDateTime($i),
$tags
);
$manager->persist($entry);
@@ -214,12 +248,15 @@ class TimesheetFixtures extends Fixture
$project = $projects[array_rand($projects)];
}
$tags = $this->getTagObjectList($i);
$entry = $this->createTimesheetEntry(
$user,
$activity,
$project,
$faker->text,
$this->getDateTime($i),
$tags,
false
);
$manager->persist($entry);
@@ -228,6 +265,22 @@ class TimesheetFixtures extends Fixture
$manager->flush();
}
/**
* @param $cnt
* @return array
*/
protected function getTagObjectList($cnt)
{
if (true === $this->useTags) {
$tagObject = new Tag();
$tagObject->setName($this->tags[($cnt % count($this->tags))]);
return [$tagObject];
}
return [];
}
/**
* @param $i
* @return bool|\DateTime
@@ -278,10 +331,11 @@ class TimesheetFixtures extends Fixture
* @param Project $project
* @param string $description
* @param \DateTime $start
* @param null|array $tagArray
* @param bool $setEndDate
* @return Timesheet
*/
private function createTimesheetEntry(User $user, Activity $activity, Project $project, $description, \DateTime $start, $setEndDate = true)
private function createTimesheetEntry(User $user, Activity $activity, Project $project, $description, \DateTime $start, $tagArray = [], $setEndDate = true)
{
$end = clone $start;
$end = $end->modify('+ ' . (rand(1, 86400)) . ' seconds');
@@ -299,6 +353,12 @@ class TimesheetFixtures extends Fixture
->setRate($rate)
->setBegin($start);
if (count($tagArray) > 0) {
foreach ($tagArray as $item) {
$entry->addTag($item);
}
}
if ($this->fixedRate) {
$entry->setFixedRate(rand(10, 100));
}

51
tests/Entity/TagTest.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Entity;
use App\Entity\Tag;
use App\Entity\Timesheet;
/**
* @covers \App\Entity\Tag
*/
class TagTest extends AbstractEntityTest
{
public function testDefaultValues()
{
$sut = new Tag();
$this->assertNull($sut->getId());
$this->assertNull($sut->getName());
}
public function testSetterAndGetter()
{
$sut = new Tag();
$this->assertInstanceOf(Tag::class, $sut->setName('foo'));
$this->assertEquals('foo', $sut->getName());
$this->assertEquals('foo', (string) $sut);
}
public function testWithTimesheet()
{
$sut = new Tag();
$timesheet = new Timesheet();
$this->assertEmpty($timesheet->getTags());
$sut->setName('bar');
$sut->addTimesheet($timesheet);
$this->assertSame($sut, $timesheet->getTags()[0]);
$sut->removeTimesheet($timesheet);
$this->assertEmpty($timesheet->getTags());
}
}

View File

@@ -12,8 +12,10 @@ namespace App\Tests\Entity;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Tag;
use App\Entity\Timesheet;
use App\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @covers \App\Entity\Timesheet
@@ -34,6 +36,8 @@ class TimesheetTest extends AbstractEntityTest
$this->assertSame(0.00, $sut->getRate());
$this->assertNull($sut->getFixedRate());
$this->assertNull($sut->getHourlyRate());
$this->assertEquals(new ArrayCollection(), $sut->getTags());
$this->assertEquals([], $sut->getTagsAsArray());
$this->assertInstanceOf(Timesheet::class, $sut->setFixedRate(13.47));
$this->assertEquals(13.47, $sut->getFixedRate());
@@ -251,4 +255,27 @@ class TimesheetTest extends AbstractEntityTest
$this->assertHasViolationForField($entity, []);
}
public function testTags()
{
$sut = new Timesheet();
$tag = new Tag();
$tag->setName('bar');
$tag1 = new Tag();
$tag1->setName('foo');
$this->assertEmpty($sut->getTags());
$sut->addTag($tag);
$sut->addTag($tag1);
$this->assertEquals([0 => 'bar', 1 => 'foo'], $sut->getTagsAsArray());
$this->assertEquals(new ArrayCollection([$tag, $tag1]), $sut->getTags());
$sut->removeTag($tag);
$this->assertEquals([1 => 'foo'], $sut->getTagsAsArray());
$sut->removeTag($tag1);
$this->assertEmpty($sut->getTags());
}
}

View File

@@ -0,0 +1,59 @@
<?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\Export\Renderer;
use App\Entity\Tag;
use App\Form\DataTransformer\TagArrayToStringTransformer;
use App\Repository\TagRepository;
/**
* @covers \App\Form\DataTransformer\TagArrayToStringTransformer
*/
class TagArrayToStringTransformerTest extends AbstractRendererTest
{
public function testTransform()
{
$results = [
(new Tag())->setName('foo'),
(new Tag())->setName('bar'),
];
$repository = $this->getMockBuilder(TagRepository::class)->disableOriginalConstructor()->getMock();
$sut = new TagArrayToStringTransformer($repository);
$this->assertEquals('', $sut->transform([]));
$this->assertEquals('', $sut->transform(null));
$actual = $sut->transform($results);
$this->assertEquals('foo, bar', $actual);
}
public function testReverseTransform()
{
$results = [
(new Tag())->setName('foo'),
(new Tag())->setName('bar'),
];
$repository = $this->getMockBuilder(TagRepository::class)->setMethods(['findBy'])->disableOriginalConstructor()->getMock();
$repository->expects($this->once())->method('findBy')->willReturn($results);
$sut = new TagArrayToStringTransformer($repository);
$this->assertEquals([], $sut->reverseTransform(''));
$this->assertEquals([], $sut->reverseTransform(null));
$actual = $sut->reverseTransform('foo, bar');
$this->assertEquals($results, $actual);
}
}

View File

@@ -0,0 +1,85 @@
<?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\Repository;
use App\Entity\Tag;
use App\Tests\DataFixtures\TagFixtures;
/**
* @covers \App\Repository\TagRepository
*/
class TagRepositoryTest extends AbstractRepositoryTest
{
public function setUp()
{
parent::setUp();
$em = $this->getEntityManager();
$data = new TagFixtures();
$data->setTagArray(['Test', 'Travel', '#2018-001', '#2018-002', '#2018-003', '#2018-004', '#2018-005', 'Administration', 'Support', 'PR', '#2018-012']);
$this->importFixture($em, $data);
}
public function testFindIds()
{
$em = $this->getEntityManager();
$repository = $em->getRepository(Tag::class);
$result = $repository->findIdsByTagNameList('2018,Test');
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(7, count($result));
$this->assertEquals(1, $result[0]);
$this->assertEquals(3, $result[1]);
$this->assertEquals(4, $result[2]);
$this->assertEquals(5, $result[3]);
$this->assertEquals(6, $result[4]);
$this->assertEquals(7, $result[5]);
$this->assertEquals(11, $result[6]);
}
public function testFindNoIds()
{
$em = $this->getEntityManager();
$repository = $em->getRepository(Tag::class);
$result = $repository->findIdsByTagNameList('Simply');
$this->assertIsArray($result);
$this->assertEmpty($result);
$this->assertEquals(0, count($result));
}
public function testFindAllTagNames()
{
$em = $this->getEntityManager();
$repository = $em->getRepository(Tag::class);
$result = $repository->findAllTagNames('2018');
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(6, count($result));
$this->assertEquals('#2018-001', $result[0]);
$this->assertEquals('#2018-002', $result[1]);
$this->assertEquals('#2018-003', $result[2]);
$this->assertEquals('#2018-004', $result[3]);
$this->assertEquals('#2018-005', $result[4]);
$this->assertEquals('#2018-012', $result[5]);
}
public function testFindNoTagNames()
{
$em = $this->getEntityManager();
$repository = $em->getRepository(Tag::class);
$result = $repository->findAllTagNames('Nothing');
$this->assertIsArray($result);
$this->assertEmpty($result);
$this->assertEquals(0, count($result));
}
}

View File

@@ -11,6 +11,7 @@ namespace App\Tests\Repository;
use App\Entity\Activity;
use App\Entity\Project;
use App\Entity\Tag;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Repository\Query\BaseQuery;
@@ -119,4 +120,39 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest
$repository->save($timesheet);
$this->assertEquals(1, $timesheet->getId());
}
public function testSaveWithTags()
{
$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);
$tagOne = new Tag();
$tagOne->setName('Travel');
$tagTwo = new Tag();
$tagTwo->setName('Picture');
$timesheet = new Timesheet();
$timesheet
->setBegin(new \DateTime())
->setEnd(new \DateTime())
->setDescription('foo')
->setUser($user)
->setActivity($activity)
->setProject($project)
->addTag($tagOne)
->addTag($tagTwo);
$this->assertNull($timesheet->getId());
$repository->save($timesheet);
$this->assertEquals(1, $timesheet->getId());
$this->assertEquals(2, $timesheet->getTags()->count());
$this->assertEquals('Travel', $timesheet->getTags()->get(0)->getName());
$this->assertEquals(1, $timesheet->getTags()->get(0)->getId());
$this->assertEquals('Picture', $timesheet->getTags()->get(1)->getName());
$this->assertEquals(2, $timesheet->getTags()->get(1)->getId());
}
}