diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..887d2cd8 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,29 @@ +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "80...100" + + status: + project: yes + patch: yes + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "diff, flags, files" + behavior: default + require_changes: yes + require_base: no + require_head: yes + branches: null diff --git a/.travis.yml b/.travis.yml index f75d271e..3a96cd2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,14 +11,19 @@ addons: services: - mysql +env: + global: + - CODECOVERAGE=0 + matrix: fast_finish: true include: - php: 7.2 - php: 7.3 + env: CODECOVERAGE=1 before_install: - - phpenv config-rm xdebug.ini +# - phpenv config-rm xdebug.ini - composer self-update - php -i @@ -26,8 +31,9 @@ install: - composer install script: - - php -r "echo date(DATE_RFC850);" - - composer code-check + - composer codestyle + - composer phpstan + - if [[ $CODECOVERAGE == 1 ]]; then vendor/bin/phpunit tests/ --coverage-clover=coverage.xml; else vendor/bin/phpunit tests/; fi; - cp tests/.env.dist.sqlite .env - bin/console doctrine:database:create -n - bin/console doctrine:migrations:migrate -n @@ -36,3 +42,6 @@ script: - bin/console doctrine:database:create -n - bin/console doctrine:migrations:migrate -n - bin/console doctrine:migrations:migrate first -n + +after_success: + - if [[ $CODECOVERAGE == 1 ]]; then bash <(curl -s https://codecov.io/bash); fi diff --git a/README.md b/README.md index 62d1b87a..ddc4292a 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ Kimai - the open source time-tracker application with a mobile-first approach (r [![Latest Stable Version](https://poser.pugx.org/kevinpapst/kimai2/v/stable)](https://packagist.org/packages/kevinpapst/kimai2) [![License](https://poser.pugx.org/kevinpapst/kimai2/license)](https://packagist.org/packages/kevinpapst/kimai2) [![Travis Status](https://travis-ci.org/kevinpapst/kimai2.svg?branch=master)](https://travis-ci.org/kevinpapst/kimai2) -[![Code Coverage](https://scrutinizer-ci.com/g/kevinpapst/kimai2/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master) -[![Scrutinizer Status](https://scrutinizer-ci.com/g/kevinpapst/kimai2/badges/build.png?b=master)](https://scrutinizer-ci.com/g/kevinpapst/kimai2/build-status/master) +[![Code Coverage](https://codecov.io/gh/kevinpapst/kimai2/branch/master/graph/badge.svg)](https://codecov.io/gh/kevinpapst/kimai2) ## Introduction diff --git a/tests/API/TimesheetControllerTest.php b/tests/API/TimesheetControllerTest.php index 544a1957..460b9ae9 100644 --- a/tests/API/TimesheetControllerTest.php +++ b/tests/API/TimesheetControllerTest.php @@ -15,6 +15,8 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Tests\DataFixtures\TimesheetFixtures; +use App\Tests\Mocks\Security\UserDateTimeFactoryFactory; +use App\Timesheet\UserDateTimeFactory; use Symfony\Component\HttpFoundation\Response; /** @@ -24,10 +26,17 @@ class TimesheetControllerTest extends APIControllerBaseTest { public const DATE_FORMAT = 'Y-m-d H:i:s'; public const DATE_FORMAT_HTML5 = 'Y-m-d\TH:i:s'; + public const TEST_TIMEZONE = 'Europe/London'; + + /** + * @var UserDateTimeFactory + */ + protected $dateTime; public function setUp() { $this->importFixtureForUser(User::ROLE_USER); + $this->dateTime = (new UserDateTimeFactoryFactory($this))->create(self::TEST_TIMEZONE); } protected function importFixtureForUser(string $role) @@ -278,8 +287,8 @@ class TimesheetControllerTest extends APIControllerBaseTest $data = [ 'activity' => 1, 'project' => 1, - 'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m'), - 'end' => (new \DateTime())->format('Y-m-d H:m'), + 'begin' => ($this->dateTime->createDateTime('- 16 hours'))->format('Y-m-d H:m:0'), + 'end' => ($this->dateTime->createDateTime())->format('Y-m-d H:m:0'), 'description' => 'foo', 'fixedRate' => 2016, 'hourlyRate' => 127 @@ -291,7 +300,7 @@ class TimesheetControllerTest extends APIControllerBaseTest $this->assertIsArray($result); $this->assertDefaultStructure($result); $this->assertNotEmpty($result['id']); - $this->assertEquals(28800, $result['duration']); + $this->assertTrue($result['duration'] == 57600 || $result['duration'] == 57660); // 1 minute rounding might be applied $this->assertEquals(2016, $result['rate']); } @@ -357,8 +366,8 @@ class TimesheetControllerTest extends APIControllerBaseTest $data = [ 'activity' => 1, 'project' => 1, - 'begin' => (new \DateTime('- 7 hours'))->format('Y-m-d\TH:m'), - 'end' => (new \DateTime())->format('Y-m-d\TH:m'), + 'begin' => ($this->dateTime->createDateTime('- 7 hours'))->format('Y-m-d\TH:m:0'), + 'end' => ($this->dateTime->createDateTime())->format('Y-m-d\TH:m:0'), 'description' => 'foo', 'exported' => true, ]; diff --git a/tests/Export/Renderer/PdfRendererTest.php b/tests/Export/Renderer/PdfRendererTest.php index 7ca448c9..7164cc14 100644 --- a/tests/Export/Renderer/PdfRendererTest.php +++ b/tests/Export/Renderer/PdfRendererTest.php @@ -9,16 +9,11 @@ namespace App\Tests\Export\Renderer; -use App\Entity\User; use App\Export\Renderer\PDFRenderer; -use App\Repository\UserRepository; -use App\Security\CurrentUser; -use App\Timesheet\UserDateTimeFactory; +use App\Tests\Mocks\Security\UserDateTimeFactoryFactory; use App\Utils\HtmlToPdfConverter; use App\Utils\MPdfConverter; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Twig\Environment; /** @@ -29,17 +24,7 @@ class PdfRendererTest extends AbstractRendererTest { protected function getDateTimeFactory() { - $user = new User(); - $repository = $this->getMockBuilder(UserRepository::class)->setMethods(['getById'])->disableOriginalConstructor()->getMock(); - $repository->expects($this->once())->method('getById')->willReturn($user); - $token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock(); - $token->expects($this->once())->method('getUser')->willReturn($user); - $tokenStorage = new TokenStorage(); - $tokenStorage->setToken($token); - - $user = new CurrentUser($tokenStorage, $repository); - - return new UserDateTimeFactory($user); + return (new UserDateTimeFactoryFactory($this))->create(); } public function testConfiguration() diff --git a/tests/Mocks/AbstractMockFactory.php b/tests/Mocks/AbstractMockFactory.php new file mode 100644 index 00000000..220b68ab --- /dev/null +++ b/tests/Mocks/AbstractMockFactory.php @@ -0,0 +1,31 @@ +testCase = $testCase; + } + + protected function getMockBuilder(string $className): MockBuilder + { + return new MockBuilder($this->testCase, $className); + } +} diff --git a/tests/Mocks/Security/UserDateTimeFactoryFactory.php b/tests/Mocks/Security/UserDateTimeFactoryFactory.php new file mode 100644 index 00000000..5c8821dd --- /dev/null +++ b/tests/Mocks/Security/UserDateTimeFactoryFactory.php @@ -0,0 +1,47 @@ +getCurrentUserMock($timezone)); + } + + protected function getCurrentUserMock(?string $timezone = null) + { + $user = new User(); + if (null !== $timezone) { + $pref = new UserPreference(); + $pref->setName('timezone'); + $pref->setValue($timezone); + $user->addPreference($pref); + } + $repository = $this->getMockBuilder(UserRepository::class)->setMethods(['getById'])->disableOriginalConstructor()->getMock(); + $repository->expects(TestCase::exactly(1))->method('getById')->willReturn($user); + $token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock(); + $token->expects(TestCase::exactly(1))->method('getUser')->willReturn($user); + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + return new CurrentUser($tokenStorage, $repository); + } +} diff --git a/tests/Timesheet/UserDateTimeFactoryTest.php b/tests/Timesheet/UserDateTimeFactoryTest.php index 6a981538..ad37f44f 100644 --- a/tests/Timesheet/UserDateTimeFactoryTest.php +++ b/tests/Timesheet/UserDateTimeFactoryTest.php @@ -9,14 +9,9 @@ namespace App\Tests\Timesheet; -use App\Entity\User; -use App\Entity\UserPreference; -use App\Repository\UserRepository; -use App\Security\CurrentUser; +use App\Tests\Mocks\Security\UserDateTimeFactoryFactory; use App\Timesheet\UserDateTimeFactory; use PHPUnit\Framework\TestCase; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; /** * @covers \App\Timesheet\UserDateTimeFactory @@ -25,28 +20,9 @@ class UserDateTimeFactoryTest extends TestCase { public const TEST_TIMEZONE = 'Europe/London'; - protected function createDateTimeFactory(string $timezone) + protected function createDateTimeFactory(?string $timezone = null): UserDateTimeFactory { - return new UserDateTimeFactory($this->getCurrentUserMock($timezone)); - } - - protected function getCurrentUserMock($timezone = null) - { - $user = new User(); - if (null !== $timezone) { - $pref = new UserPreference(); - $pref->setName('timezone'); - $pref->setValue($timezone); - $user->addPreference($pref); - } - $repository = $this->getMockBuilder(UserRepository::class)->setMethods(['getById'])->disableOriginalConstructor()->getMock(); - $repository->expects($this->once())->method('getById')->willReturn($user); - $token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock(); - $token->expects($this->once())->method('getUser')->willReturn($user); - $tokenStorage = new TokenStorage(); - $tokenStorage->setToken($token); - - return new CurrentUser($tokenStorage, $repository); + return (new UserDateTimeFactoryFactory($this))->create($timezone); } public function testGetTimezone() @@ -57,20 +33,13 @@ class UserDateTimeFactoryTest extends TestCase public function testGetTimezoneWithFallbackTimezone() { - $repository = $this->getMockBuilder(UserRepository::class)->disableOriginalConstructor()->getMock(); - $token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock(); - $token->expects($this->once())->method('getUser')->willReturn('anonymous'); - $tokenStorage = new TokenStorage(); - $tokenStorage->setToken($token); - - $current = new CurrentUser($tokenStorage, $repository); - $sut = new UserDateTimeFactory($current); + $sut = $this->createDateTimeFactory(); $this->assertEquals(date_default_timezone_get(), $sut->getTimezone()->getName()); } public function testGetStartOfMonth() { - $expected = new \DateTime(); + $expected = new \DateTime('now', new \DateTimeZone(self::TEST_TIMEZONE)); $sut = $this->createDateTimeFactory(self::TEST_TIMEZONE); $dateTime = $sut->getStartOfMonth(); @@ -85,7 +54,7 @@ class UserDateTimeFactoryTest extends TestCase public function testGetEndOfMonth() { - $expected = new \DateTime('last day of this month'); + $expected = new \DateTime('last day of this month', new \DateTimeZone(self::TEST_TIMEZONE)); $sut = $this->createDateTimeFactory(self::TEST_TIMEZONE); $dateTime = $sut->getEndOfMonth();