added project start and end date (#1303)

* added sortable js library
* activity in invoice is optional
* added javascript widget for paginated boxes
* fix activity dropdown for globals only
* added timesheet service to reduce code duplication
* use repository to query for teams in dropdowns
* added project validator
* validate project start and end against timesheet
* include begin and end in dynamic form requests for projects
* added timezone and language option to import flag, improve timesheet import speed
* deactivate cross-timezone filter
* add virtual fields to field order list
* composer update
* added param to ignore dates
* position loader icon fixed - fixes #1330
* permission problem when creating a new project - fixes #1340
* remove dev dependencies webserver and thanks bundle
* stop information leak (begin and end date) in duration mode - fixes #1307
* unify timesheet edit dialog for user and admins
* fix security issue, own rates exposed to unauthorized users in multi-update dialog
This commit is contained in:
Kevin Papst
2020-01-05 02:49:01 +01:00
committed by GitHub
parent 50383673ca
commit d6798eed1e
121 changed files with 2465 additions and 1439 deletions

View File

@@ -12,7 +12,7 @@ namespace App\Tests\API;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\User;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityInterface;
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\HttpFoundation\Response;
@@ -98,14 +98,14 @@ class ProjectControllerTest extends APIControllerBaseTest
// if you wonder why: SQLite does case-sensitive ordering, so "Title" > "fifth”
yield ['/api/projects', [], [[true, 1], [false, 1], [false, 3]]];
yield ['/api/projects', ['customer' => '1'], [[true, 1], [false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_VISIBLE], [[true, 1], [false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_HIDDEN], [[false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 1], [false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false, 1]]];
// customer is invisible, so nothing should be returned
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityQuery::SHOW_VISIBLE], []];
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityQuery::SHOW_BOTH], [[false, 2], [false, 2]]];
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityInterface::SHOW_VISIBLE], []];
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
// customer is invisible, so nothing should be returned
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityQuery::SHOW_HIDDEN], []];
yield ['/api/projects', ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
}
public function testGetEntity()
@@ -277,7 +277,7 @@ class ProjectControllerTest extends APIControllerBaseTest
protected function assertStructure(array $result, $full = true)
{
$expectedKeys = [
'id', 'name', 'visible', 'customer', 'hourlyRate', 'fixedRate', 'color', 'metaFields', 'parentTitle'
'id', 'name', 'visible', 'customer', 'hourlyRate', 'fixedRate', 'color', 'metaFields', 'parentTitle', 'start', 'end'
];
if ($full) {

View File

@@ -11,7 +11,6 @@ namespace App\Tests\DataFixtures;
use App\Entity\Activity;
use App\Entity\Project;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker\Factory;
@@ -117,12 +116,12 @@ final class ActivityFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return Project[]
* @return array<int|string, Project>
*/
protected function getAllProjects(ObjectManager $manager)
protected function getAllProjects(ObjectManager $manager): array
{
$all = [];
/* @var User[] $entries */
/** @var Project[] $entries */
$entries = $manager->getRepository(Project::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;

View File

@@ -10,7 +10,6 @@
namespace App\Tests\DataFixtures;
use App\Entity\Customer;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker\Factory;
@@ -97,20 +96,4 @@ final class CustomerFixtures extends Fixture
$manager->flush();
}
/**
* @param ObjectManager $manager
* @return Customer[]
*/
protected function getAllCustomers(ObjectManager $manager)
{
$all = [];
/* @var User[] $entries */
$entries = $manager->getRepository(Customer::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;
}
return $all;
}
}

View File

@@ -11,7 +11,6 @@ namespace App\Tests\DataFixtures;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker\Factory;
@@ -99,12 +98,12 @@ final class ProjectFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return Customer[]
* @return array<int|string, Customer>
*/
protected function getAllCustomers(ObjectManager $manager)
protected function getAllCustomers(ObjectManager $manager): array
{
$all = [];
/* @var User[] $entries */
/** @var Customer[] $entries */
$entries = $manager->getRepository(Customer::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;

View File

@@ -138,12 +138,12 @@ final class TeamFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return Customer[]
* @return array<int|string, Customer>
*/
private function getAllCustomers(ObjectManager $manager)
{
$all = [];
/* @var Customer[] $entries */
/** @var Customer[] $entries */
$entries = $manager->getRepository(Customer::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;
@@ -154,12 +154,12 @@ final class TeamFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return User[]
* @return array<int|string, User>
*/
private function getAllUsers(ObjectManager $manager)
private function getAllUsers(ObjectManager $manager): array
{
$all = [];
/* @var User[] $entries */
/** @var User[] $entries */
$entries = $manager->getRepository(User::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;

View File

@@ -297,12 +297,12 @@ final class TimesheetFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return Activity[]
* @return array<int|string, Activity>
*/
protected function getAllActivities(ObjectManager $manager)
protected function getAllActivities(ObjectManager $manager): array
{
$all = [];
/* @var Activity[] $entries */
/** @var Activity[] $entries */
$entries = $manager->getRepository(Activity::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;
@@ -313,12 +313,12 @@ final class TimesheetFixtures extends Fixture
/**
* @param ObjectManager $manager
* @return Project[]
* @return array<int|string, Project>
*/
protected function getAllProjects(ObjectManager $manager)
protected function getAllProjects(ObjectManager $manager): array
{
$all = [];
/* @var Project[] $entries */
/** @var Project[] $entries */
$entries = $manager->getRepository(Project::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;

View File

@@ -29,6 +29,8 @@ class ProjectTest extends TestCase
self::assertNull($sut->getName());
self::assertNull($sut->getOrderNumber());
self::assertNull($sut->getOrderDate());
self::assertNull($sut->getStart());
self::assertNull($sut->getEnd());
self::assertNull($sut->getComment());
self::assertTrue($sut->getVisible());
self::assertTrue($sut->isVisible());
@@ -64,6 +66,16 @@ class ProjectTest extends TestCase
self::assertInstanceOf(Project::class, $sut->setOrderDate(null));
self::assertNull($sut->getOrderDate());
self::assertInstanceOf(Project::class, $sut->setStart($dateTime));
self::assertSame($dateTime, $sut->getStart());
self::assertInstanceOf(Project::class, $sut->setStart(null));
self::assertNull($sut->getStart());
self::assertInstanceOf(Project::class, $sut->setEnd($dateTime));
self::assertSame($dateTime, $sut->getEnd());
self::assertInstanceOf(Project::class, $sut->setEnd(null));
self::assertNull($sut->getEnd());
self::assertInstanceOf(Project::class, $sut->setComment('a comment'));
self::assertEquals('a comment', $sut->getComment());

View File

@@ -18,7 +18,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Renderer\CsvRenderer
* @covers \App\Export\Renderer\AbstractSpreadsheetRenderer
* @covers \App\Export\Renderer\RendererTrait
* @group integration
*/
class CsvRendererTest extends AbstractRendererTest

View File

@@ -18,7 +18,6 @@ use Twig\Environment;
* @covers \App\Export\Base\HtmlRenderer
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Renderer\HtmlRenderer
* @covers \App\Export\Renderer\RendererTrait
* @group integration
*/
class HtmlRendererTest extends AbstractRendererTest

View File

@@ -20,7 +20,6 @@ use Twig\Environment;
* @covers \App\Export\Base\PDFRenderer
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Renderer\PDFRenderer
* @covers \App\Export\Renderer\RendererTrait
* @group integration
*/
class PdfRendererTest extends AbstractRendererTest

View File

@@ -18,7 +18,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Renderer\XlsxRenderer
* @covers \App\Export\Renderer\AbstractSpreadsheetRenderer
* @covers \App\Export\Renderer\RendererTrait
* @group integration
*/
class XlsxRendererTest extends AbstractRendererTest

View File

@@ -17,8 +17,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
* @covers \App\Export\Base\AbstractSpreadsheetRenderer
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Timesheet\CsvRenderer
* @covers \App\Export\Timesheet\AbstractSpreadsheetRenderer
* @covers \App\Export\Timesheet\RendererTrait
* @group integration
*/
class CsvRendererTest extends AbstractRendererTest

View File

@@ -20,7 +20,6 @@ use Twig\Environment;
* @covers \App\Export\Base\PDFRenderer
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Timesheet\PDFRenderer
* @covers \App\Export\Timesheet\RendererTrait
* @group integration
*/
class PdfRendererTest extends AbstractRendererTest

View File

@@ -17,8 +17,6 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
* @covers \App\Export\Base\AbstractSpreadsheetRenderer
* @covers \App\Export\Base\RendererTrait
* @covers \App\Export\Timesheet\XlsxRenderer
* @covers \App\Export\Timesheet\AbstractSpreadsheetRenderer
* @covers \App\Export\Timesheet\RendererTrait
* @group integration
*/
class XlsxRendererTest extends AbstractRendererTest

View File

@@ -24,6 +24,19 @@ use PHPUnit\Framework\TestCase;
abstract class AbstractCalculatorTest extends TestCase
{
protected function assertEmptyModel(CalculatorInterface $sut)
{
$sut->setModel($this->getEmptyModel());
$this->assertEquals(0, $sut->getTotal());
$this->assertEquals(0, $sut->getVat());
$this->assertEquals('EUR', $sut->getCurrency());
$this->assertEquals(0, $sut->getSubtotal());
$this->assertEquals(0, $sut->getTimeWorked());
$this->assertEquals(0, count($sut->getEntries()));
$this->assertEquals(0, $sut->getTax());
}
protected function getEmptyModel()
{
$customer = new Customer();
$template = new InvoiceTemplate();
@@ -34,15 +47,7 @@ abstract class AbstractCalculatorTest extends TestCase
$model->setTemplate($template);
$model->setQuery($query);
$sut->setModel($model);
$this->assertEquals(0, $sut->getTotal());
$this->assertEquals(0, $sut->getVat());
$this->assertEquals('EUR', $sut->getCurrency());
$this->assertEquals(0, $sut->getSubtotal());
$this->assertEquals(0, $sut->getTimeWorked());
$this->assertEquals(0, count($sut->getEntries()));
$this->assertEquals(0, $sut->getTax());
return $model;
}
protected function assertDescription(CalculatorInterface $sut, $addProject = false, $addActivity = false)

View File

@@ -33,6 +33,33 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
$this->assertEmptyModel(new ActivityInvoiceCalculator());
}
public function testExceptionNoActivity()
{
$this->expectException('Exception');
$this->expectExceptionMessage('Cannot work with invoice items that do not have an activity');
$timesheet = new Timesheet();
$sut = new ActivityInvoiceCalculator();
$model = $this->getEmptyModel();
$model->addEntries([$timesheet]);
$sut->setModel($model);
$sut->getEntries();
}
public function testExceptionNoId()
{
$this->expectException('Exception');
$this->expectExceptionMessage('Cannot handle un-persisted activities');
$timesheet = new Timesheet();
$timesheet->setActivity(new Activity());
$sut = new ActivityInvoiceCalculator();
$model = $this->getEmptyModel();
$model->addEntries([$timesheet]);
$sut->setModel($model);
$sut->getEntries();
}
public function testWithMultipleEntries()
{
$customer = new Customer();

View File

@@ -34,12 +34,7 @@ trait KernelTestTrait
$executor->execute($loader->getFixtures(), true);
}
/**
* @param EntityManager $em
* @param string $username
* @return User|null
*/
protected function getUserByName(EntityManager $em, string $username)
protected function getUserByName(EntityManager $em, string $username): ?User
{
return $em->getRepository(User::class)->findOneBy(['username' => $username]);
}

View File

@@ -12,7 +12,7 @@ namespace App\Tests\Repository\Query;
use App\Entity\Customer;
use App\Entity\Project;
use App\Repository\Query\ActivityQuery;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityInterface;
/**
* @covers \App\Repository\Query\ActivityQuery
@@ -24,7 +24,7 @@ class ActivityQueryTest extends BaseQueryTest
$sut = new ActivityQuery();
$this->assertBaseQuery($sut, 'name');
$this->assertInstanceOf(VisibilityQuery::class, $sut);
$this->assertInstanceOf(VisibilityInterface::class, $sut);
$this->assertNull($sut->getCustomer());
$this->assertNull($sut->getProject());

View File

@@ -31,7 +31,7 @@ class BaseQueryTest extends TestCase
}
/**
* @expectedDeprecation BaseQuery::getResultType() is deprecated and will be removed with 1.6
* @expectedDeprecation BaseQuery::getResultType() is deprecated and will be removed with 2.0
* @group legacy
*/
public function testDeprecations()
@@ -100,6 +100,16 @@ class BaseQueryTest extends TestCase
self::assertInstanceOf(BaseQuery::class, $sut->addTeam(new Team()));
self::assertEquals(1, count($sut->getTeams()));
$sut->setTeams(null);
self::assertEmpty($sut->getTeams());
$sut->setTeams([]);
self::assertEmpty($sut->getTeams());
$team = new Team();
self::assertInstanceOf(BaseQuery::class, $sut->setTeams([$team]));
self::assertEquals(1, count($sut->getTeams()));
self::assertSame($team, $sut->getTeams()[0]);
}
protected function assertPage(BaseQuery $sut)

View File

@@ -10,7 +10,7 @@
namespace App\Tests\Repository\Query;
use App\Repository\Query\CustomerQuery;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityInterface;
/**
* @covers \App\Repository\Query\CustomerQuery
@@ -22,7 +22,7 @@ class CustomerQueryTest extends BaseQueryTest
$sut = new CustomerQuery();
$this->assertBaseQuery($sut, 'name');
$this->assertInstanceOf(VisibilityQuery::class, $sut);
$this->assertInstanceOf(VisibilityInterface::class, $sut);
$this->assertResetByFormError(new CustomerQuery(), 'name');
}

View File

@@ -11,7 +11,7 @@ namespace App\Tests\Repository\Query;
use App\Entity\Customer;
use App\Repository\Query\ProjectQuery;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityInterface;
/**
* @covers \App\Repository\Query\ProjectQuery
@@ -23,7 +23,7 @@ class ProjectQueryTest extends BaseQueryTest
$sut = new ProjectQuery();
$this->assertBaseQuery($sut, 'name');
$this->assertInstanceOf(VisibilityQuery::class, $sut);
$this->assertInstanceOf(VisibilityInterface::class, $sut);
$this->assertNull($sut->getCustomer());
@@ -38,5 +38,21 @@ class ProjectQueryTest extends BaseQueryTest
$this->assertEquals(99, $sut->getCustomer());
$this->assertResetByFormError(new ProjectQuery(), 'name');
self::assertNull($sut->getProjectStart());
self::assertNull($sut->getProjectEnd());
}
public function testSetter()
{
$sut = new ProjectQuery();
$start = new \DateTime();
$sut->setProjectStart($start);
self::assertSame($start, $sut->getProjectStart());
$end = new \DateTime('-1 day');
$sut->setProjectEnd($end);
self::assertSame($end, $sut->getProjectEnd());
}
}

View File

@@ -10,7 +10,7 @@
namespace App\Tests\Repository\Query;
use App\Repository\Query\UserQuery;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityInterface;
/**
* @covers \App\Repository\Query\UserQuery
@@ -21,7 +21,7 @@ class UserQueryTest extends BaseQueryTest
{
$sut = new UserQuery();
$this->assertBaseQuery($sut, 'username');
$this->assertInstanceOf(VisibilityQuery::class, $sut);
$this->assertInstanceOf(VisibilityInterface::class, $sut);
$this->assertRole($sut);
$this->assertResetByFormError(new UserQuery(), 'username');

View File

@@ -9,11 +9,14 @@
namespace App\Tests\Repository\Query;
use App\Repository\Query\VisibilityInterface;
use App\Repository\Query\VisibilityQuery;
use App\Repository\Query\VisibilityTrait;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Repository\Query\VisibilityQuery
* @covers \App\Repository\Query\VisibilityTrait
*/
class VisibilityQueryTest extends TestCase
{
@@ -41,4 +44,34 @@ class VisibilityQueryTest extends TestCase
$sut->setVisibility(VisibilityQuery::SHOW_VISIBLE);
$this->assertEquals(VisibilityQuery::SHOW_VISIBLE, $sut->getVisibility());
}
public function testVisibilityTrait()
{
$sut = new VisibilityTraitImpl();
$this->assertEquals(VisibilityInterface::SHOW_VISIBLE, $sut->getVisibility());
$sut->setVisibility('foo-bar');
$this->assertEquals(VisibilityInterface::SHOW_VISIBLE, $sut->getVisibility());
$sut->setVisibility('2');
$this->assertEquals(VisibilityInterface::SHOW_HIDDEN, $sut->getVisibility());
$sut->setVisibility('0'); // keep the value that was previously set
$this->assertEquals(VisibilityInterface::SHOW_HIDDEN, $sut->getVisibility());
$sut->setVisibility(VisibilityInterface::SHOW_BOTH);
$this->assertEquals(VisibilityInterface::SHOW_BOTH, $sut->getVisibility());
$sut->setVisibility(VisibilityInterface::SHOW_HIDDEN);
$this->assertEquals(VisibilityInterface::SHOW_HIDDEN, $sut->getVisibility());
$sut->setVisibility(VisibilityInterface::SHOW_VISIBLE);
$this->assertEquals(VisibilityInterface::SHOW_VISIBLE, $sut->getVisibility());
}
}
class VisibilityTraitImpl implements VisibilityInterface
{
use VisibilityTrait;
}

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\Validator\Constraints;
use App\Entity\Project;
use App\Validator\Constraints\Project as ProjectConstraint;
use App\Validator\Constraints\ProjectValidator;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
/**
* @covers \App\Validator\Constraints\ProjectValidator
*/
class ProjectValidatorTest extends ConstraintValidatorTestCase
{
protected function createValidator()
{
return new ProjectValidator();
}
public function testConstraintIsInvalid()
{
$this->expectException(UnexpectedTypeException::class);
$this->validator->validate('foo', new NotBlank());
}
public function testEndBeforeStartIsInvalid()
{
$begin = new \DateTime();
$end = new \DateTime('-1 hour');
$project = new Project();
$project->setStart($begin);
$project->setEnd($end);
$this->validator->validate($project, new ProjectConstraint());
$this->buildViolation('End date must not be earlier then start date.')
->atPath('property.path.end')
->setCode(ProjectConstraint::END_BEFORE_BEGIN_ERROR)
->assertRaised();
}
}

View File

@@ -22,6 +22,7 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Test\ConstraintViolationAssertion;
/**
* @covers \App\Validator\Constraints\TimesheetValidator
@@ -199,4 +200,68 @@ class TimesheetValidatorTest extends ConstraintValidatorTestCase
->setCode(TimesheetConstraint::DISABLED_CUSTOMER_ERROR)
->assertRaised();
}
public function getProjectStartEndTestData()
{
yield [new \DateTime(), new \DateTime(), [
['begin', TimesheetConstraint::PROJECT_NOT_STARTED, 'The project has not started at that time.'],
['end', TimesheetConstraint::PROJECT_NOT_STARTED, 'The project has not started at that time.'],
]];
yield [new \DateTime('-9 hour'), new \DateTime('-2 hour'), [
['begin', TimesheetConstraint::PROJECT_NOT_STARTED, 'The project has not started at that time.'],
['end', TimesheetConstraint::PROJECT_ALREADY_ENDED, 'The project is finished at that time.'],
]];
yield [new \DateTime('-19 hour'), new \DateTime('-12 hour'), [
['begin', TimesheetConstraint::PROJECT_ALREADY_ENDED, 'The project is finished at that time.'],
['end', TimesheetConstraint::PROJECT_ALREADY_ENDED, 'The project is finished at that time.'],
]];
yield [new \DateTime('-19 hour'), new \DateTime('-2 hour'), [
['end', TimesheetConstraint::PROJECT_ALREADY_ENDED, 'The project is finished at that time.'],
]];
yield [new \DateTime('-9 hour'), new \DateTime(), [
['begin', TimesheetConstraint::PROJECT_NOT_STARTED, 'The project has not started at that time.'],
]];
}
/**
* @dataProvider getProjectStartEndTestData
*/
public function testEndBeforeWithProjectStartAndEnd(\DateTime $start, \DateTime $end, array $violations)
{
$timesheet = new Timesheet();
$timesheet->setBegin(new \DateTime('-10 hour'));
$timesheet->setEnd(new \DateTime('-1 hour'));
$customer = new Customer();
$project = new Project();
$project->setStart($start);
$project->setEnd($end);
$project->setCustomer($customer);
$timesheet->setProject($project);
$timesheet->setActivity(new Activity());
$this->validator->validate($timesheet, new TimesheetConstraint(['message' => 'myMessage']));
/** @var ConstraintViolationAssertion $assertion */
$assertion = null;
foreach ($violations as $violation) {
if (null === $assertion) {
$assertion = $this->buildViolation($violation[2])
->atPath('property.path.' . $violation[0])
->setCode($violation[1])
;
} else {
$assertion = $assertion->buildNextViolation($violation[2])
->atPath('property.path.' . $violation[0])
->setCode($violation[1])
;
}
}
$assertion->assertRaised();
}
}

View File

@@ -10,6 +10,7 @@ parameters:
- '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
- '#Access to an undefined property Faker\\Generator::\$bs.#'
- '#Call to static method PHPUnit\\Framework\\Assert::assertSame\(\) with App\\Entity\\[a-zA-Z0-9]+ and null will always evaluate to false.#'
- '#Method *::getUserByName\(\) should return App\\Entity\\User\|null but returns object|null.#'
excludes_analyse:
- %rootDir%/../../../tests/Ldap/LdapDriverTest.php
inferPrivatePropertyTypeFromConstructor: true