diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index e9bab8dc..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ -## Description - - -## Expected Behavior - - -## Actual Behavior - - -## Possible Fix - - -## Steps to Reproduce -1. -2. -3. -4. - -## Your Environment -* Version: -* Browser Name and version: -* Operating System and version (desktop or mobile): diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b7353733..97c7c7bf 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help us improve +about: Create a report to help us improve Kimai --- @@ -11,25 +11,24 @@ A clear and concise description of what the bug is. Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' -3. Scroll down to '....' -4. See error +3. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +**Logfile** +``` +Add the last lines from your logfile at var/log/prod.log, around the time when the problem happened. +``` **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** +**Desktop/Smartphone** +Please complete the following information if this seems to be a frontend issue: + - Device: [e.g. iPhone6s] - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - **Additional context** Add any other context about the problem here. + - Kimai version + - PHP version diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 066b2d92..35f82300 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,6 @@ --- name: Feature request -about: Suggest an idea for this project +about: Suggest an idea to make Kimai better --- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 22829bd2..083082c5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,5 @@ ## Description - +A clear and concise description of what this pull request changes. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) @@ -7,8 +7,7 @@ - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist -- [ ] My code follows the code style -- [ ] All files have a license header -- [ ] All methods have a doc header with type declarations +- [ ] I have executed `bin/console kimai:phpcs --fix` to make sure my changes adopt the correct code style - [ ] I have updated the documentation accordingly - [ ] I have added tests to cover my changes +- [ ] I agree that this code is used in Kimai and will be published under the [MIT license](https://github.com/kevinpapst/kimai2/blob/master/LICENSE) diff --git a/.github_changelog_generator b/.github_changelog_generator index c03b7660..f1bc90eb 100644 --- a/.github_changelog_generator +++ b/.github_changelog_generator @@ -1,4 +1,4 @@ unreleased=true -future-release=0.7 +future-release=0.8 exclude-labels=duplicate,question,invalid,wontfix,release enhancement_labels=>enhancement,Enhancement,feature request \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ce914eaf..4ab92d33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Change Log +## [0.8](https://github.com/kevinpapst/kimai2/tree/0.8) (2019-02-19) +[Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.7...0.8) + +**Implemented enhancements:** + +- Export other users' timesheets using the API [\#562](https://github.com/kevinpapst/kimai2/issues/562) +- Some importer bugs/improvements [\#553](https://github.com/kevinpapst/kimai2/issues/553) +- Export timesheet only exports entries on the current page [\#534](https://github.com/kevinpapst/kimai2/issues/534) +- On the last activities dropdown show tha last ten DIFFERENT activities [\#533](https://github.com/kevinpapst/kimai2/issues/533) +- Better user expierence on "My Times" [\#526](https://github.com/kevinpapst/kimai2/issues/526) +- Highlight current day in datepickers and not only the selected day [\#522](https://github.com/kevinpapst/kimai2/issues/522) +- Configuration of first page after login [\#501](https://github.com/kevinpapst/kimai2/issues/501) +- Visual Grouping of entry from same day or same week with some stats of that group [\#495](https://github.com/kevinpapst/kimai2/issues/495) +- add currency field to invoice template [\#422](https://github.com/kevinpapst/kimai2/issues/422) + +**Fixed bugs:** + +- Error 404 on 'My times' page 2+ if the time range is changed to something smaller [\#558](https://github.com/kevinpapst/kimai2/issues/558) +- Timezone is not working correctly [\#554](https://github.com/kevinpapst/kimai2/issues/554) +- New time picker not working on languages without updated translation [\#546](https://github.com/kevinpapst/kimai2/issues/546) +- Export timesheet only exports entries on the current page [\#534](https://github.com/kevinpapst/kimai2/issues/534) +- Kimai2 will not allow me to enter times in the future [\#531](https://github.com/kevinpapst/kimai2/issues/531) +- No projects in filter section [\#525](https://github.com/kevinpapst/kimai2/issues/525) +- This value should be greater than or equal to zero [\#511](https://github.com/kevinpapst/kimai2/issues/511) +- Wrong time after migration kimai v1 data [\#507](https://github.com/kevinpapst/kimai2/issues/507) +- SQL-Error for getRecentActivities\(\) caused by GROUP BY [\#488](https://github.com/kevinpapst/kimai2/issues/488) + +**Closed issues:** + +- Feedback for improving installation setup [\#541](https://github.com/kevinpapst/kimai2/issues/541) +- template entry.end\_time shows empty field [\#535](https://github.com/kevinpapst/kimai2/issues/535) +- Global Projects [\#413](https://github.com/kevinpapst/kimai2/issues/413) + +**Merged pull requests:** + +- handle deleted user during import from v1 [\#569](https://github.com/kevinpapst/kimai2/pull/569) ([kevinpapst](https://github.com/kevinpapst)) +- fix pagination in combination with daterange picker [\#568](https://github.com/kevinpapst/kimai2/pull/568) ([kevinpapst](https://github.com/kevinpapst)) +- allow to query other users timesheets via api [\#563](https://github.com/kevinpapst/kimai2/pull/563) ([kevinpapst](https://github.com/kevinpapst)) +- Updated HTML invoice templates [\#560](https://github.com/kevinpapst/kimai2/pull/560) ([kevinpapst](https://github.com/kevinpapst)) +- updated composer packages [\#559](https://github.com/kevinpapst/kimai2/pull/559) ([kevinpapst](https://github.com/kevinpapst)) +- fix timezone problems in timesheet forms [\#555](https://github.com/kevinpapst/kimai2/pull/555) ([kevinpapst](https://github.com/kevinpapst)) +- Daily stats in timesheet [\#552](https://github.com/kevinpapst/kimai2/pull/552) ([kevinpapst](https://github.com/kevinpapst)) +- Added more php requirements to documentation [\#551](https://github.com/kevinpapst/kimai2/pull/551) ([infeeeee](https://github.com/infeeeee)) +- improve recent activities [\#550](https://github.com/kevinpapst/kimai2/pull/550) ([kevinpapst](https://github.com/kevinpapst)) +- scss fixes: year selector width, dropdown menu width, navbar refactoring [\#549](https://github.com/kevinpapst/kimai2/pull/549) ([infeeeee](https://github.com/infeeeee)) +- improved installation docs [\#548](https://github.com/kevinpapst/kimai2/pull/548) ([kevinpapst](https://github.com/kevinpapst)) +- fix daterange-picker for fr, hu and ar [\#547](https://github.com/kevinpapst/kimai2/pull/547) ([kevinpapst](https://github.com/kevinpapst)) +- do not limit users timesheet export page size [\#545](https://github.com/kevinpapst/kimai2/pull/545) ([kevinpapst](https://github.com/kevinpapst)) +- Use Symfony formatter for currency symbol placement [\#542](https://github.com/kevinpapst/kimai2/pull/542) ([sanjitlpatel](https://github.com/sanjitlpatel)) +- Localized date-inputs and daterange-picker [\#540](https://github.com/kevinpapst/kimai2/pull/540) ([kevinpapst](https://github.com/kevinpapst)) +- added export module [\#538](https://github.com/kevinpapst/kimai2/pull/538) ([kevinpapst](https://github.com/kevinpapst)) +- updated documentation [\#536](https://github.com/kevinpapst/kimai2/pull/536) ([kevinpapst](https://github.com/kevinpapst)) +- support remote data in beta-test selectpicker [\#529](https://github.com/kevinpapst/kimai2/pull/529) ([kevinpapst](https://github.com/kevinpapst)) +- Improve allowed running records [\#528](https://github.com/kevinpapst/kimai2/pull/528) ([kevinpapst](https://github.com/kevinpapst)) +- Improve daterangepicker [\#527](https://github.com/kevinpapst/kimai2/pull/527) ([kevinpapst](https://github.com/kevinpapst)) +- added configurable view after login [\#523](https://github.com/kevinpapst/kimai2/pull/523) ([kevinpapst](https://github.com/kevinpapst)) +- convert timesheets to UTC with support for user timezone [\#372](https://github.com/kevinpapst/kimai2/pull/372) ([kevinpapst](https://github.com/kevinpapst)) + ## [0.7](https://github.com/kevinpapst/kimai2/tree/0.7) (2019-01-28) [Full Changelog](https://github.com/kevinpapst/kimai2/compare/0.6.1...0.7) @@ -18,11 +76,10 @@ - Configuration option to disable fixed rate and hourly rate from "edit timesheet" [\#330](https://github.com/kevinpapst/kimai2/issues/330) - Set other users hourly rate [\#303](https://github.com/kevinpapst/kimai2/issues/303) - Feature request - Make "Rate" hideable [\#217](https://github.com/kevinpapst/kimai2/issues/217) -- fixed null project for advanced invoice calculator [\#462](https://github.com/kevinpapst/kimai2/pull/462) ([kevinpapst](https://github.com/kevinpapst)) +- fixed null project for advanced invoice calculator [\#462](https://github.com/kevinpapst/kimai2/pull/462) ([kevinpapst](https://github.com/kevinpapst)) **Fixed bugs:** -- This value should be greater than or equal to zero [\#511](https://github.com/kevinpapst/kimai2/issues/511) - Timesheet Export for Admins [\#503](https://github.com/kevinpapst/kimai2/issues/503) - admin activity: visibility "none" \(no filter\) causes sql-error [\#491](https://github.com/kevinpapst/kimai2/issues/491) - login-screen optimizations [\#483](https://github.com/kevinpapst/kimai2/issues/483) @@ -51,7 +108,7 @@ - pagination without reload while keeping filters applied [\#521](https://github.com/kevinpapst/kimai2/pull/521) ([kevinpapst](https://github.com/kevinpapst)) - go back to calendar after editing and creation of time-records [\#519](https://github.com/kevinpapst/kimai2/pull/519) ([kevinpapst](https://github.com/kevinpapst)) -- fetch toolbar results without page reload [\#518](https://github.com/kevinpapst/kimai2/pull/518) ([kevinpapst](https://github.com/kevinpapst)) +- fetch toolbar results without page reload [\#518](https://github.com/kevinpapst/kimai2/pull/518) ([kevinpapst](https://github.com/kevinpapst)) - Form and theme improvements [\#513](https://github.com/kevinpapst/kimai2/pull/513) ([kevinpapst](https://github.com/kevinpapst)) - validation for future and negative times [\#512](https://github.com/kevinpapst/kimai2/pull/512) ([kevinpapst](https://github.com/kevinpapst)) - alphabetical order for selectboxes [\#510](https://github.com/kevinpapst/kimai2/pull/510) ([kevinpapst](https://github.com/kevinpapst)) diff --git a/UPGRADING.md b/UPGRADING.md index f42a5f2d..8e5edbca 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -5,26 +5,30 @@ Database upgrades are currently ONLY provided for MySQL/MariaDB and SQLite. If you plan on using e.g. PostgreSQL, please read more about the `bin/console doctrine:migrations:diff` and `bin/console doctrine:migrations:migrate` commands and contact us, so we can integrate them into the official releases. -A normal upgrade can be executed with these commands: +Upgrading to the latest available version can be achieved with these commands: ```bash -git pull origin master +git fetch --tags +git checkout 0.8 sudo -u www-data composer install --no-dev --optimize-autoloader sudo -u www-data bin/console cache:clear --env=prod sudo -u www-data bin/console cache:warmup --env=prod bin/console doctrine:migrations:migrate ``` +Be careful when upgrading multiple versions at once: follow each version specific information! + There might be version specific tasks that need to be executed before or after these steps, please see below if your updated version is mentioned below. ## [0.8](https://github.com/kevinpapst/kimai2/releases/tag/0.8) (unreleased) -There was a change introduced regarding the storage of the date-time objects in timesheet records. -Please read this [Pull request](https://github.com/kevinpapst/kimai2/pull/372) carefully before you follow the instructions -to convert the timezones in your existing time records with `bin/console kimai:convert-timezone --help`. +After you followed the normal update and database migration process (see above), you need to execute a bash command (see below) and check if you want to apply changes to your `local.yaml`. -If you don't do that, you will end up with wrong times in your database. Be especially careful, when you previously imported data from Kimai v1. +- An important change was introduced regarding the storage of date-time objects in the database. Please read this [pull request](https://github.com/kevinpapst/kimai2/pull/372) BEFORE you follow the instructions +to convert the timezones in your existing time records with `bin/console kimai:convert-timezone`. Without that, you will end up with wrong times in your database. +- A new boolean setting `kimai.timesheet.rules.allow_future_times` was introduced +- New [permissions](var/docs/permissions.md) are available: `view_export,create_export,edit_export_own_timesheet,edit_export_other_timesheet,system_information` ## [0.7](https://github.com/kevinpapst/kimai2/releases/tag/0.7) (2019-01-28) @@ -68,16 +72,6 @@ sudo composer self-update sudo -u www-data composer update symfony/flex --no-plugins --no-scripts ``` -Then the full update can be executed as usual: - -```bash -git pull origin master -sudo -u www-data composer install --no-dev --optimize-autoloader -sudo -u www-data bin/console cache:clear --env=prod -sudo -u www-data bin/console cache:warmup --env=prod -bin/console doctrine:migrations:migrate -``` - ## [0.3](https://github.com/kevinpapst/kimai2/releases/tag/0.3) (2018-07-22) **Update from 0.2:** diff --git a/assets/js/kimai.js b/assets/js/kimai.js index a7057a99..91f9f866 100644 --- a/assets/js/kimai.js +++ b/assets/js/kimai.js @@ -62,7 +62,6 @@ $(function() { $('input[data-daterangepickerenable="on"]').each(function(index) { var localeFormat = $(this).data('format'); var separator = $(this).data('separator'); - var transToday = $.kimai.settings['today']; var rangesList = {}; rangesList[$.kimai.settings['today']] = [moment(), moment()]; rangesList[$.kimai.settings['yesterday']] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')]; diff --git a/src/API/ActivityController.php b/src/API/ActivityController.php index 600dec2d..3e29d340 100644 --- a/src/API/ActivityController.php +++ b/src/API/ActivityController.php @@ -83,11 +83,11 @@ class ActivityController extends BaseApiController $query->setOrderBy($orderBy); } - if (null !== ($globals = $paramFetcher->get('globals'))) { + if (null !== $paramFetcher->get('globals')) { $query->setGlobalsOnly(true); } - if ('false' === ($globalsFirst = $paramFetcher->get('globalsFirst'))) { + if ('false' === $paramFetcher->get('globalsFirst')) { $query->setOrderGlobalsFirst(false); } diff --git a/src/Command/ConvertTimezoneCommand.php b/src/Command/ConvertTimezoneCommand.php index a23e7efe..e677b4ee 100644 --- a/src/Command/ConvertTimezoneCommand.php +++ b/src/Command/ConvertTimezoneCommand.php @@ -25,6 +25,8 @@ use Symfony\Component\Console\Style\SymfonyStyle; * You should convert all timesheet records that were saved with Kimai 2 directly, but NOT the ones migrated from Kimai v1. * * Please read https://github.com/kevinpapst/kimai2/pull/372 to find out more! + * + * @codeCoverageIgnore */ class ConvertTimezoneCommand extends Command { diff --git a/src/Command/KimaiImporterCommand.php b/src/Command/KimaiImporterCommand.php index c98de081..c71a8980 100644 --- a/src/Command/KimaiImporterCommand.php +++ b/src/Command/KimaiImporterCommand.php @@ -35,6 +35,8 @@ use Symfony\Component\Validator\Validator\ValidatorInterface; /** * Command used to import data from a Kimai v1 installation. * Getting help in improving this script would be fantastic, it currently only handles the most basic use-cases. + * + * @codeCoverageIgnore */ class KimaiImporterCommand extends Command { @@ -962,7 +964,6 @@ class KimaiImporterCommand extends Command // ----------------------- unknown user, damned missing data integrity in Kimai v1 ----------------------- if (!isset($this->users[$oldRecord['userID']])) { - $tempUserName = uniqid(); $tempPassword = uniqid() . uniqid(); diff --git a/src/Constants.php b/src/Constants.php index 13fb31d9..42ac89d3 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -21,7 +21,7 @@ class Constants /** * The current release version */ - public const VERSION = '0.7'; + public const VERSION = '0.8'; /** * The release name, will only change for new major version */ diff --git a/src/EventSubscriber/TimezoneSubscriber.php b/src/EventSubscriber/TimezoneSubscriber.php index 32db0268..7df32a43 100644 --- a/src/EventSubscriber/TimezoneSubscriber.php +++ b/src/EventSubscriber/TimezoneSubscriber.php @@ -74,5 +74,4 @@ class TimezoneSubscriber implements EventSubscriberInterface return ($user instanceof User); } - } diff --git a/src/Repository/TimesheetRepository.php b/src/Repository/TimesheetRepository.php index c9e52128..e9193a05 100644 --- a/src/Repository/TimesheetRepository.php +++ b/src/Repository/TimesheetRepository.php @@ -9,6 +9,7 @@ namespace App\Repository; +use App\Entity\Activity; use App\Entity\Timesheet; use App\Entity\User; use App\Model\Statistic\Month; @@ -364,8 +365,7 @@ class TimesheetRepository extends AbstractRepository ->setParameter('activity', $query->getActivity()); } - // TODO if activity is an int, this will fail - if (null === $query->getActivity() || null === $query->getActivity()->getProject()) { + if (null === $query->getActivity() || ($query->getActivity() instanceof Activity && null === $query->getActivity()->getProject())) { if (null !== $query->getProject()) { $qb->andWhere('t.project = :project') ->setParameter('project', $query->getProject()); diff --git a/tests/API/ActivityControllerTest.php b/tests/API/ActivityControllerTest.php index 63f9533e..8a7da298 100644 --- a/tests/API/ActivityControllerTest.php +++ b/tests/API/ActivityControllerTest.php @@ -67,7 +67,7 @@ class ActivityControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, $url, 'GET', $parameters); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertNotEmpty($result); $this->assertEquals(count($expected), count($result)); for ($i = 0; $i < count($result); $i++) { @@ -93,13 +93,32 @@ class ActivityControllerTest extends APIControllerBaseTest yield ['/api/activities', ['project' => '2', 'visible' => VisibilityQuery::SHOW_HIDDEN], [[false], [true, 2]]]; } + public function testGetCollectionWithQuery() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $this->loadActivityTestData($client); + + $query = ['order' => 'ASC', 'orderBy' => 'project', 'globalsFirst' => 'false']; + $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $this->assertAccessIsGranted($client, '/api/activities', 'GET', $query); + $result = json_decode($client->getResponse()->getContent(), true); + + $this->assertIsArray($result); + $this->assertNotEmpty($result); + $this->assertEquals(5, count($result)); + $this->assertStructure($result[0], false); + $this->assertEquals(1, $result[2]['project']); + $this->assertEquals(2, $result[3]['project']); + $this->assertEquals(2, $result[4]['project']); + } + public function testGetEntity() { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $this->assertAccessIsGranted($client, '/api/activities/1'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $expectedKeys = ['id', 'name', 'comment', 'visible']; $actual = array_keys($result); diff --git a/tests/API/ApiDocControllerTest.php b/tests/API/ApiDocControllerTest.php index 66a77e63..508c133d 100644 --- a/tests/API/ApiDocControllerTest.php +++ b/tests/API/ApiDocControllerTest.php @@ -35,7 +35,7 @@ class ApiDocControllerTest extends ControllerBaseTest $this->assertAccessIsGranted($client, '/api/doc.json'); $this->assertContains('"title":"Kimai 2 - API Docs"', $client->getResponse()->getContent()); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertNotEmpty($result); } diff --git a/tests/API/CustomerControllerTest.php b/tests/API/CustomerControllerTest.php index 5080cdbd..62b71bdd 100644 --- a/tests/API/CustomerControllerTest.php +++ b/tests/API/CustomerControllerTest.php @@ -28,7 +28,20 @@ class CustomerControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/customers'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); + $this->assertNotEmpty($result); + $this->assertEquals(1, count($result)); + $this->assertStructure($result[0], false); + } + + public function testGetCollectionWithQuery() + { + $query = ['order' => 'ASC', 'orderBy' => 'name', 'visible' => 3]; + $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $this->assertAccessIsGranted($client, '/api/customers', 'GET', $query); + $result = json_decode($client->getResponse()->getContent(), true); + + $this->assertIsArray($result); $this->assertNotEmpty($result); $this->assertEquals(1, count($result)); $this->assertStructure($result[0], false); @@ -40,7 +53,7 @@ class CustomerControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/customers/1'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertStructure($result, true); } diff --git a/tests/API/HealthcheckControllerTest.php b/tests/API/HealthcheckControllerTest.php index c0a96ef1..bd0910b7 100644 --- a/tests/API/HealthcheckControllerTest.php +++ b/tests/API/HealthcheckControllerTest.php @@ -29,7 +29,7 @@ class HealthcheckControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/ping'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertEquals(['message' => 'pong'], $result); } @@ -39,7 +39,7 @@ class HealthcheckControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/version'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertArrayHasKey('version', $result); $this->assertArrayHasKey('candidate', $result); diff --git a/tests/API/ProjectControllerTest.php b/tests/API/ProjectControllerTest.php index 919874fd..39654f7a 100644 --- a/tests/API/ProjectControllerTest.php +++ b/tests/API/ProjectControllerTest.php @@ -32,7 +32,7 @@ class ProjectControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/projects'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertNotEmpty($result); $this->assertEquals(1, count($result)); $this->assertStructure($result[0], false); @@ -81,7 +81,7 @@ class ProjectControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, $url, 'GET', $parameters); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertEquals(count($expected), count($result), 'Found wrong amount of projects'); for ($i = 0; $i < count($expected); $i++) { @@ -111,7 +111,7 @@ class ProjectControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/projects/1'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertStructure($result); } diff --git a/tests/API/TimesheetControllerTest.php b/tests/API/TimesheetControllerTest.php index 912e6387..fcd3ecef 100644 --- a/tests/API/TimesheetControllerTest.php +++ b/tests/API/TimesheetControllerTest.php @@ -14,6 +14,7 @@ use App\Entity\Customer; use App\Entity\Project; use App\Entity\User; use App\Tests\DataFixtures\TimesheetFixtures; +use Symfony\Component\HttpFoundation\Response; /** * @coversDefaultClass \App\API\TimesheetController @@ -118,7 +119,7 @@ class TimesheetControllerTest extends APIControllerBaseTest public function testGetCollectionWithQuery() { - $query = ['customer' => 1, 'project' => 1, 'page' => 2, 'size' => 5, 'order' => 'DESC', 'orderBy' => 'rate']; + $query = ['customer' => 1, 'project' => 1, 'activity' => 1, 'page' => 2, 'size' => 5, 'order' => 'DESC', 'orderBy' => 'rate']; $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query); $result = json_decode($client->getResponse()->getContent(), true); @@ -218,24 +219,6 @@ class TimesheetControllerTest extends APIControllerBaseTest $this->assertApiCallValidationError($client->getResponse(), ['activity']); } - public function testPostActionWithIdIsNotAllowed() - { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $data = [ - 'id' => 1, - 'activity' => 1, - 'project' => 1, - 'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m'), - 'end' => (new \DateTime())->format('Y-m-d H:m'), - 'description' => 'foo', - 'fixedRate' => 2016, - 'hourlyRate' => 127 - ]; - $this->request($client, '/api/timesheets', 'POST', [], json_encode($data)); - $this->assertFalse($client->getResponse()->isSuccessful()); - $this->assertEquals(400, $client->getResponse()->getStatusCode()); - } - public function testNotFound() { $this->assertEntityNotFound(User::ROLE_USER, '/api/timesheets/20'); @@ -263,6 +246,38 @@ class TimesheetControllerTest extends APIControllerBaseTest $this->assertEquals(1, $result['exported']); } + public function testPatchActionWithInvalidUser() + { + $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_TEAMLEAD)) + ->setStartDate(new \DateTime('-10 days')) + ->setAllowEmptyDescriptions(false) + ; + $this->importFixture($em, $fixture); + + $data = [ + 'activity' => 1, + 'project' => 1, + 'begin' => (new \DateTime('- 7 hours'))->format('Y-m-d H:m'), + 'end' => (new \DateTime())->format('Y-m-d H:m'), + 'description' => 'foo', + 'exported' => true, + ]; + $this->request($client, '/api/timesheets/15', 'PATCH', [], json_encode($data)); + $response = $client->getResponse(); + $this->assertFalse($response->isSuccessful()); + $this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode()); + $json = json_decode($response->getContent(), true); + $this->assertEquals('User cannot update timesheet', $json['message']); + } + public function testInvalidPatchAction() { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); diff --git a/tests/API/UserControllerTest.php b/tests/API/UserControllerTest.php index 429069e9..ee5a929c 100644 --- a/tests/API/UserControllerTest.php +++ b/tests/API/UserControllerTest.php @@ -29,7 +29,7 @@ class UserControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/users'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertNotEmpty($result); $this->assertEquals(6, count($result)); $this->assertStructure($result[0], false); @@ -41,7 +41,7 @@ class UserControllerTest extends APIControllerBaseTest $this->assertAccessIsGranted($client, '/api/users/1'); $result = json_decode($client->getResponse()->getContent(), true); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertStructure($result); } diff --git a/tests/Controller/CalendarControllerTest.php b/tests/Controller/CalendarControllerTest.php index cf8ab906..cf96fdca 100644 --- a/tests/Controller/CalendarControllerTest.php +++ b/tests/Controller/CalendarControllerTest.php @@ -48,7 +48,7 @@ class CalendarControllerTest extends ControllerBaseTest $response = $client->getResponse(); $this->assertTrue($response->isSuccessful()); $json = json_decode($response->getContent(), true); - $this->assertInternalType('array', $json); + $this->assertIsArray($json); $this->assertEmpty($json); } @@ -68,11 +68,11 @@ class CalendarControllerTest extends ControllerBaseTest $response = $client->getResponse(); $this->assertTrue($response->isSuccessful()); $json = json_decode($response->getContent(), true); - $this->assertInternalType('array', $json); + $this->assertIsArray($json); $this->assertNotEmpty($json); $this->assertEquals(10, count($json)); foreach ($json as $result) { - $this->assertInternalType('array', $result); + $this->assertIsArray($result); $this->assertCalendarStructure($result); } } diff --git a/tests/DataFixtures/TimesheetFixtures.php b/tests/DataFixtures/TimesheetFixtures.php index e2fb7c59..4dc73c28 100644 --- a/tests/DataFixtures/TimesheetFixtures.php +++ b/tests/DataFixtures/TimesheetFixtures.php @@ -63,6 +63,7 @@ class TimesheetFixtures extends Fixture public function setAllowEmptyDescriptions(bool $allowEmptyDescriptions) { $this->allowEmptyDescriptions = $allowEmptyDescriptions; + return $this; } diff --git a/tests/Invoice/ServiceInvoiceTest.php b/tests/Invoice/ServiceInvoiceTest.php index 63a3dead..80b310f2 100644 --- a/tests/Invoice/ServiceInvoiceTest.php +++ b/tests/Invoice/ServiceInvoiceTest.php @@ -28,13 +28,13 @@ class ServiceInvoiceTest extends TestCase $sut = new ServiceInvoice($repo); $this->assertEmpty($sut->getCalculator()); - $this->assertInternalType('array', $sut->getCalculator()); + $this->assertIsArray($sut->getCalculator()); $this->assertEmpty($sut->getRenderer()); - $this->assertInternalType('array', $sut->getRenderer()); + $this->assertIsArray($sut->getRenderer()); $this->assertEmpty($sut->getNumberGenerator()); - $this->assertInternalType('array', $sut->getNumberGenerator()); + $this->assertIsArray($sut->getNumberGenerator()); $this->assertEmpty($sut->getDocuments()); - $this->assertInternalType('array', $sut->getDocuments()); + $this->assertIsArray($sut->getDocuments()); $this->assertNull($sut->getCalculatorByName('default')); $this->assertNull($sut->getDocumentByName('default')); diff --git a/tests/Model/InvoiceModelTest.php b/tests/Model/InvoiceModelTest.php index 545d7e62..debce854 100644 --- a/tests/Model/InvoiceModelTest.php +++ b/tests/Model/InvoiceModelTest.php @@ -33,7 +33,7 @@ class InvoiceModelTest extends TestCase $this->assertNull($sut->getQuery()); $this->assertNull($sut->getDueDate()); $this->assertEmpty($sut->getEntries()); - $this->assertInternalType('array', $sut->getEntries()); + $this->assertIsArray($sut->getEntries()); $this->assertInstanceOf(\DateTime::class, $sut->getInvoiceDate()); } diff --git a/tests/Model/Statistic/YearTest.php b/tests/Model/Statistic/YearTest.php index def7b064..27b534ef 100644 --- a/tests/Model/Statistic/YearTest.php +++ b/tests/Model/Statistic/YearTest.php @@ -23,7 +23,7 @@ class YearTest extends TestCase $sut = new Year('1999'); $this->assertNull($sut->getMonth('01')); $this->assertEmpty($sut->getMonths()); - $this->assertInternalType('array', $sut->getMonths()); + $this->assertIsArray($sut->getMonths()); $this->assertEquals('1999', $sut->getYear()); } diff --git a/tests/Repository/InvoiceDocumentRepositoryTest.php b/tests/Repository/InvoiceDocumentRepositoryTest.php index 89996de1..402f1ab2 100644 --- a/tests/Repository/InvoiceDocumentRepositoryTest.php +++ b/tests/Repository/InvoiceDocumentRepositoryTest.php @@ -36,7 +36,7 @@ class InvoiceDocumentRepositoryTest extends TestCase { $sut = new InvoiceDocumentRepository([]); $this->assertEmpty($sut->findAll()); - $this->assertInternalType('array', $sut->findAll()); + $this->assertIsArray($sut->findAll()); $this->assertNull($sut->findByName('default')); } diff --git a/tests/Repository/TimesheetRepositoryTest.php b/tests/Repository/TimesheetRepositoryTest.php index a2c90769..964a71c8 100644 --- a/tests/Repository/TimesheetRepositoryTest.php +++ b/tests/Repository/TimesheetRepositoryTest.php @@ -45,7 +45,7 @@ class TimesheetRepositoryTest extends AbstractRepositoryTest $query->setResultType(BaseQuery::RESULT_TYPE_OBJECTS); $result = $repository->findByQuery($query); - $this->assertInternalType('array', $result); + $this->assertIsArray($result); } public function testStoppedEntriesCannotBeStoppedAgain() diff --git a/tests/Twig/ExtensionsTest.php b/tests/Twig/ExtensionsTest.php index b253c34a..9684807b 100644 --- a/tests/Twig/ExtensionsTest.php +++ b/tests/Twig/ExtensionsTest.php @@ -220,7 +220,7 @@ class ExtensionsTest extends TestCase foreach ($icons as $icon) { $result = $sut->icon($icon); $this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon); - $this->assertInternalType('string', $result); + $this->assertIsString($result); } // test fallback will be returned diff --git a/tests/Utils/DurationTest.php b/tests/Utils/DurationTest.php index 3750a0f7..22c185fb 100644 --- a/tests/Utils/DurationTest.php +++ b/tests/Utils/DurationTest.php @@ -20,6 +20,8 @@ class DurationTest extends TestCase public function testFormat() { $sut = new Duration(); + + $this->assertNull($sut->format(null)); $this->assertEquals('02:38', $sut->format(9494)); $this->assertEquals('02:38:14', $sut->format(9494, Duration::FORMAT_WITH_SECONDS)); } @@ -73,7 +75,7 @@ class DurationTest extends TestCase ['13', Duration::FORMAT_COLON], ['13-13', Duration::FORMAT_COLON], ['13.13', Duration::FORMAT_COLON], - [1111, 1111, Duration::FORMAT_NATURAL], + [1111, Duration::FORMAT_NATURAL], // invalid modes [17, 'foo'], diff --git a/tests/Utils/LocaleSettingsTest.php b/tests/Utils/LocaleSettingsTest.php index d774c6cb..fb2bc73c 100644 --- a/tests/Utils/LocaleSettingsTest.php +++ b/tests/Utils/LocaleSettingsTest.php @@ -142,6 +142,7 @@ class LocaleSettingsTest extends TestCase /** * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unknown locale given: xx */ public function testInvalidLocaleWithGivenLocale() { @@ -197,4 +198,16 @@ class LocaleSettingsTest extends TestCase $this->assertEquals('YYYY-MM-DD HH:mm', $sut->getDateTimePickerFormat()); $this->assertEquals('DD.MM.YYYY HH:mm', $sut->getDateTimePickerFormat('de')); } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unknown setting for locale en: date_time_picker + */ + public function testUnknownSetting() + { + $sut = $this->getSut('en', ['en' => [ + 'xxx' => 'dd.MM.yyyy HH:mm', + ]]); + $sut->getDateTimePickerFormat('en'); + } } diff --git a/tests/Utils/MarkdownTest.php b/tests/Utils/MarkdownTest.php index 0b85faaf..d65da5ae 100644 --- a/tests/Utils/MarkdownTest.php +++ b/tests/Utils/MarkdownTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; /** * @covers \App\Utils\Markdown + * @covers \App\Utils\ParsedownExtension */ class MarkdownTest extends TestCase { @@ -51,6 +52,26 @@ sdfsdf [asdfasdf](#test-1) asdfasdf # test aasdfasdf +EOT; + $this->assertEquals($html, $sut->toHtml($markdown)); + } + + public function testDuplicateIds() + { + $sut = new Markdown(); + + $html = <<<'EOT' +