Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -65,7 +65,20 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
return '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET')
|
||||
protected function assertPagination(Response $response, int $page, int $pageSize, int $totalPages, int $totalResults): void
|
||||
{
|
||||
$this->assertTrue($response->headers->has('X-Page'), 'Missing "X-Page" header');
|
||||
$this->assertTrue($response->headers->has('X-Total-Count'), 'Missing "X-Total-Count" header');
|
||||
$this->assertTrue($response->headers->has('X-Total-Pages'), 'Missing "X-Total-Pages" header');
|
||||
$this->assertTrue($response->headers->has('X-Per-Page'), 'Missing "X-Per-Page" header');
|
||||
|
||||
$this->assertEquals($page, $response->headers->get('X-Page'));
|
||||
$this->assertEquals($totalResults, $response->headers->get('X-Total-Count'));
|
||||
$this->assertEquals($totalPages, $response->headers->get('X-Total-Pages'));
|
||||
$this->assertEquals($pageSize, $response->headers->get('X-Per-Page'));
|
||||
}
|
||||
|
||||
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET'): void
|
||||
{
|
||||
$this->request($client, $url, $method);
|
||||
$this->assertResponseIsSecured($client->getResponse(), $url);
|
||||
@@ -75,9 +88,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param Response $response
|
||||
* @param string $url
|
||||
*/
|
||||
protected function assertResponseIsSecured(Response $response, string $url)
|
||||
protected function assertResponseIsSecured(Response $response, string $url): void
|
||||
{
|
||||
$data = ['message' => 'Authentication required, missing headers: X-AUTH-USER, X-AUTH-TOKEN'];
|
||||
$data = ['message' => 'Authentication required, missing user header: X-AUTH-USER'];
|
||||
|
||||
self::assertEquals(
|
||||
$data,
|
||||
@@ -97,7 +110,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET')
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET'): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$client->request($method, $this->createUrl($url));
|
||||
@@ -108,107 +121,110 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => 403,
|
||||
'message' => 'Access denied.'
|
||||
'code' => Response::HTTP_FORBIDDEN,
|
||||
'message' => 'Forbidden'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler
|
||||
public function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler
|
||||
{
|
||||
$server = ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'];
|
||||
|
||||
return $client->request($method, $this->createUrl($url), $parameters, [], $server, $content);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFound(string $role, string $url, string $method = 'GET', ?string $message = null)
|
||||
protected function assertEntityNotFound(string $role, string $url, string $method = 'GET', ?string $message = null): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->request($client, $url, $method);
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found'
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertNotFoundForDelete(HttpKernelBrowser $client, string $url)
|
||||
protected function assertNotFoundForDelete(HttpKernelBrowser $client, string $url): void
|
||||
{
|
||||
$this->assertExceptionForMethod($client, $url, 'DELETE', [], [
|
||||
'code' => 404,
|
||||
'message' => 'Not found'
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForDelete(string $role, string $url, ?string $message = null)
|
||||
{
|
||||
$this->assertExceptionForDeleteAction($role, $url, [], [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data, ?string $message = null)
|
||||
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data): void
|
||||
{
|
||||
$this->assertExceptionForPatchAction($role, $url, $data, [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found',
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForPost(string $role, string $url, array $data, ?string $message = null)
|
||||
protected function assertEntityNotFoundForPost(HttpKernelBrowser $client, string $url, array $data = []): void
|
||||
{
|
||||
$this->assertExceptionForPostAction($role, $url, $data, [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found',
|
||||
$this->assertExceptionForMethod($client, $url, 'POST', $data, [
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertExceptionForDeleteAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForDeleteAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'DELETE', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForPatchAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForPatchAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'PATCH', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForPostAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForPostAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'POST', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForMethod(HttpKernelBrowser $client, string $url, string $method, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForMethod(HttpKernelBrowser $client, string $url, string $method, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->request($client, $url, $method, [], json_encode($data));
|
||||
$this->assertApiException($client->getResponse(), $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertApiException(Response $response, array $expectedErrors)
|
||||
protected function assertApiException(Response $response, array $expectedErrors): void
|
||||
{
|
||||
self::assertFalse($response->isSuccessful());
|
||||
self::assertEquals($expectedErrors['code'], $response->getStatusCode());
|
||||
self::assertEquals($expectedErrors, json_decode($response->getContent(), true));
|
||||
}
|
||||
|
||||
protected function assertExceptionForRole(string $role, string $url, string $method, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForRole(string $role, string $url, string $method, array $data, array $expectedErrors): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->assertExceptionForMethod($client, $url, $method, $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertApi500Exception(Response $response, string $message)
|
||||
protected function assertApi500Exception(Response $response, string $message): void
|
||||
{
|
||||
$this->assertApiException($response, ['code' => 500, 'message' => $message]);
|
||||
$this->assertApiException($response, ['code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'message' => $message]);
|
||||
}
|
||||
|
||||
protected function assertApiAccessDenied(HttpKernelBrowser $client, string $url, string $message)
|
||||
protected function assertBadRequest(HttpKernelBrowser $client, string $url, string $method): void
|
||||
{
|
||||
$this->assertExceptionForMethod($client, $url, $method, [], [
|
||||
'code' => Response::HTTP_BAD_REQUEST,
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertApiAccessDenied(HttpKernelBrowser $client, string $url, string $message = 'Forbidden'): void
|
||||
{
|
||||
$this->request($client, $url);
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), $message);
|
||||
}
|
||||
|
||||
protected function assertApiResponseAccessDenied(Response $response, string $message)
|
||||
protected function assertApiResponseAccessDenied(Response $response, string $message = 'Forbidden'): void
|
||||
{
|
||||
// APP_DEBUG = 1 means "real exception messages" - it is always overwritten
|
||||
$message = 'Forbidden';
|
||||
|
||||
$this->assertApiException($response, [
|
||||
'code' => Response::HTTP_FORBIDDEN,
|
||||
'message' => $message
|
||||
@@ -219,17 +235,24 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param Response $response
|
||||
* @param array<int, string>|array<string, mixed> $failedFields
|
||||
* @param bool $extraFields test for the error "This form should not contain extra fields"
|
||||
* @param array<int, string>|array<string, mixed> $globalError
|
||||
*/
|
||||
protected function assertApiCallValidationError(Response $response, array $failedFields, bool $extraFields = false)
|
||||
protected function assertApiCallValidationError(Response $response, array $failedFields, bool $extraFields = false, array $globalError = []): void
|
||||
{
|
||||
self::assertFalse($response->isSuccessful());
|
||||
$result = json_decode($response->getContent(), true);
|
||||
|
||||
self::assertArrayHasKey('errors', $result);
|
||||
|
||||
if ($extraFields) {
|
||||
self::assertArrayHasKey('errors', $result['errors']);
|
||||
self::assertEquals($result['errors']['errors'][0], 'This form should not contain extra fields.');
|
||||
self::assertEquals('This form should not contain extra fields.', $result['errors']['errors'][0]);
|
||||
}
|
||||
|
||||
if (\count($globalError) > 0) {
|
||||
self::assertArrayHasKey('errors', $result['errors']);
|
||||
foreach ($globalError as $err) {
|
||||
self::assertTrue(\in_array($err, $result['errors']['errors']), 'Missing global validation error: ' . $err);
|
||||
}
|
||||
}
|
||||
|
||||
self::assertArrayHasKey('children', $result['errors']);
|
||||
@@ -275,6 +298,16 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
protected static function getExpectedResponseStructure(string $type): array
|
||||
{
|
||||
switch ($type) {
|
||||
case 'PageActionItem':
|
||||
return [
|
||||
'id' => 'string',
|
||||
'title' => '@string',
|
||||
'url' => '@string',
|
||||
'class' => '@string',
|
||||
'attr' => 'array',
|
||||
'divider' => 'bool'
|
||||
];
|
||||
|
||||
case 'TagEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -282,7 +315,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
// embedded meta data
|
||||
// embedded meta data
|
||||
case 'UserPreference':
|
||||
return [
|
||||
'name' => 'string',
|
||||
@@ -298,9 +331,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'value' => 'string',
|
||||
];
|
||||
|
||||
// if a user is embedded in other objects
|
||||
// if a user is embedded in other objects
|
||||
case 'User':
|
||||
// if a list of users is loaded
|
||||
// if a list of users is loaded
|
||||
case 'UserCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -309,9 +342,11 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'alias' => '@string',
|
||||
'accountNumber' => '@string',
|
||||
'initials' => '@string',
|
||||
'title' => '@string',
|
||||
];
|
||||
|
||||
// if a user is loaded explicitly
|
||||
// if a user is loaded explicitly
|
||||
case 'UserEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -323,6 +358,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
'roles' => ['result' => 'array', 'type' => 'string'],
|
||||
'initials' => 'string',
|
||||
'language' => 'string',
|
||||
'timezone' => 'string',
|
||||
'accountNumber' => '@string',
|
||||
@@ -330,9 +366,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'preferences' => ['result' => 'array', 'type' => 'UserPreference'],
|
||||
];
|
||||
|
||||
// if a team is embedded
|
||||
// if a team is embedded
|
||||
case 'Team':
|
||||
// if a collection of teams is requested
|
||||
// if a collection of teams is requested
|
||||
case 'TeamCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -340,35 +376,33 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
// explicitly requested team
|
||||
// explicitly requested team
|
||||
case 'TeamEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'name' => 'string',
|
||||
'color' => '@string',
|
||||
'teamlead' => ['result' => 'object', 'type' => '@User'],
|
||||
'members' => ['result' => 'array', 'type' => 'TeamMember'],
|
||||
'users' => ['result' => 'array', 'type' => 'User'],
|
||||
'customers' => ['result' => 'array', 'type' => '@Customer'],
|
||||
'projects' => ['result' => 'array', 'type' => '@Project'],
|
||||
'activities' => ['result' => 'array', 'type' => '@Activity'],
|
||||
];
|
||||
|
||||
// if the team is used inside the team context
|
||||
// if the team is used inside the team context
|
||||
case 'TeamMember':
|
||||
return [
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'teamlead' => 'bool',
|
||||
];
|
||||
|
||||
// if the team is used inside the user context
|
||||
// if the team is used inside the user context
|
||||
case 'TeamMembership':
|
||||
return [
|
||||
'team' => ['result' => 'object', 'type' => 'Team'],
|
||||
'teamlead' => 'bool',
|
||||
];
|
||||
|
||||
// if a customer is embedded in other objects
|
||||
// if a customer is embedded in other objects
|
||||
case 'Customer':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -380,7 +414,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a list of customers is loaded
|
||||
// if a list of customers is loaded
|
||||
case 'CustomerCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -395,7 +429,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'currency' => 'string', // since 1.10
|
||||
];
|
||||
|
||||
// if a customer is loaded explicitly
|
||||
// if a customer is loaded explicitly
|
||||
case 'CustomerEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -424,7 +458,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'budgetType' => '@string', // since 1.15
|
||||
];
|
||||
|
||||
// if a project is embedded
|
||||
// if a project is embedded
|
||||
case 'Project':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -437,7 +471,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a project is embedded in an expanded collection (here timesheet)
|
||||
// if a project is embedded in an expanded collection (here timesheet)
|
||||
case 'ProjectExpanded':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -450,7 +484,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a collection of projects is loaded
|
||||
// if a collection of projects is loaded
|
||||
case 'ProjectCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -468,7 +502,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a project is explicitly loaded
|
||||
// if a project is explicitly loaded
|
||||
case 'ProjectEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -479,19 +513,19 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'],
|
||||
'parentTitle' => 'string',
|
||||
'start' => '@datetime',
|
||||
'end' => '@datetime',
|
||||
'start' => '@date',
|
||||
'end' => '@date',
|
||||
'globalActivities' => 'bool',
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
'comment' => '@string',
|
||||
'budget' => 'float',
|
||||
'timeBudget' => 'int',
|
||||
'orderNumber' => '@string',
|
||||
'orderDate' => '@datetime',
|
||||
'orderDate' => '@date',
|
||||
'budgetType' => '@string', // since 1.15
|
||||
];
|
||||
|
||||
// embedded activities
|
||||
// embedded activities
|
||||
case 'Activity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -514,7 +548,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// collection of activities
|
||||
// collection of activities
|
||||
case 'ActivityCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -529,7 +563,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
];
|
||||
|
||||
// if a activity is explicitly loaded
|
||||
// if a activity is explicitly loaded
|
||||
case 'ActivityEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -565,10 +599,10 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'billable' => 'bool',
|
||||
'fixedRate' => '@float',
|
||||
'hourlyRate' => '@float',
|
||||
// TODO new fields: billable, category
|
||||
// TODO new fields: category
|
||||
];
|
||||
|
||||
case 'TimesheetEntityFull':
|
||||
case 'TimesheetExpanded':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'begin' => 'DateTime',
|
||||
@@ -579,14 +613,14 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'activity' => ['result' => 'object', 'type' => 'ActivityExpanded'],
|
||||
'project' => ['result' => 'object', 'type' => 'ProjectExpanded'],
|
||||
'tags' => ['result' => 'array', 'type' => 'string'],
|
||||
'user' => 'int',
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'metaFields' => ['result' => 'array', 'type' => 'TimesheetMeta'],
|
||||
'internalRate' => 'float',
|
||||
'exported' => 'bool',
|
||||
'billable' => 'bool',
|
||||
'fixedRate' => '@float',
|
||||
'hourlyRate' => '@float',
|
||||
// TODO new fields: billable, category
|
||||
// TODO new fields: category
|
||||
];
|
||||
|
||||
case 'TimesheetCollection':
|
||||
@@ -618,7 +652,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'activity' => ['result' => 'object', 'type' => 'Activity'],
|
||||
'project' => ['result' => 'object', 'type' => 'ProjectExpanded'],
|
||||
'tags' => ['result' => 'array', 'type' => 'string'],
|
||||
'user' => 'int',
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'metaFields' => ['result' => 'array', 'type' => 'TimesheetMeta'],
|
||||
'internalRate' => 'float',
|
||||
'exported' => 'bool',
|
||||
@@ -637,7 +671,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param array $result
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function assertApiResponseTypeStructure(string $type, array $result)
|
||||
protected function assertApiResponseTypeStructure(string $type, array $result): void
|
||||
{
|
||||
$expected = self::getExpectedResponseStructure($type);
|
||||
$expectedKeys = array_keys($expected);
|
||||
@@ -704,7 +738,12 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
}
|
||||
|
||||
if (strtolower($value) === 'datetime') {
|
||||
// TODO
|
||||
$date = \DateTime::createFromFormat('Y-m-d\TH:i:sO', $result[$key]);
|
||||
self::assertInstanceOf(\DateTime::class, $date, sprintf('Field "%s" was expected to be a Date with the format "Y-m-dTH:i:sO", but found: %s', $key, $result[$key]));
|
||||
$value = 'string';
|
||||
} elseif (strtolower($value) === 'date') {
|
||||
$date = \DateTime::createFromFormat('Y-m-d', $result[$key]);
|
||||
self::assertInstanceOf(\DateTime::class, $date, sprintf('Field "%s" was expected to be a Date with the format "Y-m-d", but found: %s', $key, $result[$key]));
|
||||
$value = 'string';
|
||||
}
|
||||
|
||||
|
||||
218
tests/API/ActionsControllerTest.php
Normal file
218
tests/API/ActionsControllerTest.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?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\ActivityFixtures;
|
||||
use App\Tests\DataFixtures\CustomerFixtures;
|
||||
use App\Tests\DataFixtures\ProjectFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class ActionsControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/actions/timesheet/1/index/en');
|
||||
}
|
||||
|
||||
public function test_getTimesheetActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$items = $this->importFixture(new TimesheetFixtures($this->getUserByRole(User::ROLE_USER), 1));
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
'trash',
|
||||
],
|
||||
'calendar' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/timesheet/%s/%s/en', $items[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getActivityActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$projectFixture = new ProjectFixtures(1);
|
||||
$projectFixture->setCustomers($customers);
|
||||
$projects = $this->importFixture($projectFixture);
|
||||
|
||||
$activityFixture = new ActivityFixtures(1);
|
||||
$activityFixture->setProjects($projects);
|
||||
$activities = $this->importFixture($activityFixture);
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/activity/%s/%s/en', $activities[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getProjectActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$projectFixture = new ProjectFixtures(1);
|
||||
$projectFixture->setCustomers($customers);
|
||||
$projects = $this->importFixture($projectFixture);
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report_project_details',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report_project_details',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/project/%s/%s/en', $projects[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getCustomerActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'vcard',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/customer/%s/%s/en', $customers[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ use App\Entity\RateInterface;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ActivityRateRepository;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -141,7 +141,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getCollectionTestData
|
||||
*/
|
||||
public function testGetCollection($url, $project, $parameters, $expected)
|
||||
public function testGetCollection($url, $project, $parameters, $expected): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$imports = $this->loadActivityTestData();
|
||||
@@ -153,10 +153,16 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
|
||||
if (\array_key_exists('projects', $parameters)) {
|
||||
if (stripos($parameters['projects'], ',') !== false) {
|
||||
$parameters['projects'] = $projectId . ',' . $projectId;
|
||||
if (!\is_array($parameters['projects'])) {
|
||||
throw new \InvalidArgumentException('projects needs to be an array');
|
||||
}
|
||||
$count = \count($parameters['projects']);
|
||||
if ($count === 2) {
|
||||
$parameters['projects'] = [$projectId, $projectId];
|
||||
} elseif ($count === 1) {
|
||||
$parameters['projects'] = [$projectId];
|
||||
} else {
|
||||
$parameters['projects'] = (string) $projectId;
|
||||
throw new \InvalidArgumentException('Invalid count for projects');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,19 +183,22 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function getCollectionTestData()
|
||||
/**
|
||||
* @return \Generator<array<mixed>>
|
||||
*/
|
||||
public function getCollectionTestData(): iterable
|
||||
{
|
||||
yield ['/api/activities', null, [], [[false], [true, 2], [true, 2], [null], [true, 1]]];
|
||||
//yield ['/api/activities', [], [[false], [false], [true, 2], [true, 1], [true, 2]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true'], [[false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => 3], [[false], [false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => '2'], [[false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => 1], [[false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_BOTH], [[false], [false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[false], [false]]];
|
||||
yield ['/api/activities', 0, ['project' => '1'], [[false], [false], [true, 1]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => '2', 'visible' => 1], [[true, 2], [true, 2], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => '2,2', 'visible' => '3'], [[true, 2], [true, 2], [true, 2], [false], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => '2,2', 'visible' => 2], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => '2', 'visible' => 2], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => ['2'], 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 2], [true, 2], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 2], [true, 2], [true, 2], [false], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN], [[true, 2], [false]]];
|
||||
}
|
||||
|
||||
public function testGetCollectionWithQuery()
|
||||
@@ -222,7 +231,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/activities/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/activities/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Activity object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -269,10 +278,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/activities', 'POST', [], 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 create activities', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create activities');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -343,10 +349,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/activities/1', '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 activity', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update activity');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -375,32 +378,32 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -38,7 +38,7 @@ class ApiDocControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
$expectedKeys = ['Activity', 'Default', 'Customer', 'Project', 'Tag', 'Team', 'Timesheet', 'User'];
|
||||
$expectedKeys = ['Actions', 'Activity', 'Default', 'Customer', 'Project', 'Tag', 'Team', 'Timesheet', 'User'];
|
||||
$actual = array_keys($tags);
|
||||
|
||||
sort($actual);
|
||||
@@ -51,7 +51,71 @@ class ApiDocControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/doc.json');
|
||||
$this->assertStringContainsString('"title":"Kimai - API Docs"', $client->getResponse()->getContent());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$paths = [
|
||||
'/api/actions/timesheet/{id}/{view}/{locale}',
|
||||
'/api/actions/activity/{id}/{view}/{locale}',
|
||||
'/api/actions/project/{id}/{view}/{locale}',
|
||||
'/api/actions/customer/{id}/{view}/{locale}',
|
||||
'/api/activities',
|
||||
'/api/activities/{id}',
|
||||
'/api/activities/{id}/meta',
|
||||
'/api/activities/{id}/rates',
|
||||
'/api/activities/{id}/rates/{rateId}',
|
||||
'/api/config/timesheet',
|
||||
'/api/customers',
|
||||
'/api/customers/{id}',
|
||||
'/api/customers/{id}/meta',
|
||||
'/api/customers/{id}/rates',
|
||||
'/api/customers/{id}/rates/{rateId}',
|
||||
'/api/projects',
|
||||
'/api/projects/{id}',
|
||||
'/api/projects/{id}/meta',
|
||||
'/api/projects/{id}/rates',
|
||||
'/api/projects/{id}/rates/{rateId}',
|
||||
'/api/ping',
|
||||
'/api/version',
|
||||
'/api/plugins',
|
||||
'/api/tags',
|
||||
'/api/tags/{id}',
|
||||
'/api/teams',
|
||||
'/api/teams/{id}',
|
||||
'/api/teams/{id}/members/{userId}',
|
||||
'/api/teams/{id}/customers/{customerId}',
|
||||
'/api/teams/{id}/projects/{projectId}',
|
||||
'/api/teams/{id}/activities/{activityId}',
|
||||
'/api/timesheets',
|
||||
'/api/timesheets/{id}',
|
||||
'/api/timesheets/recent',
|
||||
'/api/timesheets/active',
|
||||
'/api/timesheets/{id}/stop',
|
||||
'/api/timesheets/{id}/restart',
|
||||
'/api/timesheets/{id}/duplicate',
|
||||
'/api/timesheets/{id}/export',
|
||||
'/api/timesheets/{id}/meta',
|
||||
'/api/users',
|
||||
'/api/users/{id}',
|
||||
'/api/users/me',
|
||||
];
|
||||
|
||||
$this->assertArrayHasKey('openapi', $json);
|
||||
$this->assertEquals('3.0.0', $json['openapi']);
|
||||
$this->assertArrayHasKey('info', $json);
|
||||
$this->assertEquals('Kimai - API Docs', $json['info']['title']);
|
||||
$this->assertEquals('0.7', $json['info']['version']);
|
||||
|
||||
$this->assertArrayHasKey('paths', $json);
|
||||
$this->assertEquals($paths, array_keys($json['paths']));
|
||||
|
||||
$this->assertArrayHasKey('security', $json);
|
||||
$this->assertArrayHasKey('X-AUTH-USER', $json['security'][0]);
|
||||
$this->assertArrayHasKey('X-AUTH-TOKEN', $json['security'][1]);
|
||||
|
||||
$this->assertArrayHasKey('components', $json);
|
||||
$this->assertArrayHasKey('schemas', $json['components']);
|
||||
$this->assertArrayHasKey('securitySchemes', $json['components']);
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
|
||||
178
tests/API/Authentication/SessionAuthenticatorTest.php
Normal file
178
tests/API/Authentication/SessionAuthenticatorTest.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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\Authentication;
|
||||
|
||||
use App\API\Authentication\SessionAuthenticator;
|
||||
use App\API\Authentication\TokenAuthenticator;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ApiUserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Authentication\SessionAuthenticator
|
||||
*/
|
||||
class SessionAuthenticatorTest extends TestCase
|
||||
{
|
||||
private function getSut(bool $verify = true): SessionAuthenticator
|
||||
{
|
||||
$userProvider = $this->createMock(ApiUserRepository::class);
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
$passwordHasher->method('verify')->willReturn($verify);
|
||||
$passwordHasherFactory->method('getPasswordHasher')->willReturn($passwordHasher);
|
||||
|
||||
$token = new TokenAuthenticator($userProvider, $passwordHasherFactory);
|
||||
|
||||
return new SessionAuthenticator($token);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
// not supporting because /api path is not the beginning of the URL
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => 'dfghj/api/doc/dfghj']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/doc']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertFalse($sut->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingAuthHeader()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => '']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => '', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticate()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
self::assertInstanceOf(Passport::class, $passport);
|
||||
$badge = $passport->getBadge(UserBadge::class);
|
||||
self::assertInstanceOf(UserBadge::class, $badge);
|
||||
self::assertEquals('foo2', $badge->getUserIdentifier());
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar2');
|
||||
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
self::assertInstanceOf(CustomCredentials::class, $badge);
|
||||
self::assertFalse($badge->isResolved());
|
||||
$badge->executeCustomChecker($user);
|
||||
self::assertTrue($badge->isResolved());
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnMissingApiTokenForUser()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The user has no activated API account.');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnWrongPassword()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The presented password is invalid.');
|
||||
|
||||
$sut = $this->getSut(false);
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar');
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
}
|
||||
175
tests/API/Authentication/TokenAuthenticatorTest.php
Normal file
175
tests/API/Authentication/TokenAuthenticatorTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?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\Authentication;
|
||||
|
||||
use App\API\Authentication\TokenAuthenticator;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ApiUserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Authentication\TokenAuthenticator
|
||||
*/
|
||||
class TokenAuthenticatorTest extends TestCase
|
||||
{
|
||||
private function getSut(bool $verify = true): TokenAuthenticator
|
||||
{
|
||||
$userProvider = $this->createMock(ApiUserRepository::class);
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
$passwordHasher->method('verify')->willReturn($verify);
|
||||
$passwordHasherFactory->method('getPasswordHasher')->willReturn($passwordHasher);
|
||||
|
||||
return new TokenAuthenticator($userProvider, $passwordHasherFactory);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
// not supporting because /api path is not the beginning of the URL
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => 'dfghj/api/doc/dfghj']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/doc']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertTrue($sut->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingAuthHeader()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => '']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => '', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticate()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
self::assertInstanceOf(Passport::class, $passport);
|
||||
$badge = $passport->getBadge(UserBadge::class);
|
||||
self::assertInstanceOf(UserBadge::class, $badge);
|
||||
self::assertEquals('foo2', $badge->getUserIdentifier());
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar2');
|
||||
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
self::assertInstanceOf(CustomCredentials::class, $badge);
|
||||
self::assertFalse($badge->isResolved());
|
||||
$badge->executeCustomChecker($user);
|
||||
self::assertTrue($badge->isResolved());
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnMissingApiTokenForUser()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The user has no activated API account.');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnWrongPassword()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The presented password is invalid.');
|
||||
|
||||
$sut = $this->getSut(false);
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar');
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
}
|
||||
@@ -16,33 +16,6 @@ use App\Entity\User;
|
||||
*/
|
||||
class ConfigurationControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsI18nSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/config/i18n');
|
||||
}
|
||||
|
||||
public function testGetI18n()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/config/i18n', 'GET');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(7, \count($result));
|
||||
$this->assertI18nStructure($result);
|
||||
}
|
||||
|
||||
protected function assertI18nStructure(array $result)
|
||||
{
|
||||
$expectedKeys = ['date', 'dateTime', 'duration', 'formDate', 'is24hours', 'time', 'now'];
|
||||
$actual = array_keys($result);
|
||||
sort($actual);
|
||||
sort($expectedKeys);
|
||||
|
||||
$this->assertEquals($expectedKeys, $actual, 'Config structure does not match');
|
||||
}
|
||||
|
||||
public function testIsTimesheetSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/config/timesheet');
|
||||
@@ -56,13 +29,8 @@ class ConfigurationControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(6, \count($result));
|
||||
$this->assertTimesheetStructure($result);
|
||||
}
|
||||
|
||||
protected function assertTimesheetStructure(array $result)
|
||||
{
|
||||
$expectedKeys = ['activeEntriesHardLimit', 'activeEntriesSoftLimit', 'defaultBeginTime', 'isAllowFutureTimes', 'isAllowOverlapping', 'trackingMode'];
|
||||
$expectedKeys = ['activeEntriesHardLimit', 'defaultBeginTime', 'isAllowFutureTimes', 'isAllowOverlapping', 'trackingMode'];
|
||||
$this->assertCount(\count($expectedKeys), $result);
|
||||
$actual = array_keys($result);
|
||||
sort($actual);
|
||||
sort($expectedKeys);
|
||||
|
||||
@@ -21,7 +21,6 @@ use App\Entity\User;
|
||||
use App\Repository\CustomerRateRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -63,10 +62,9 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
$customer = $repository->find($id);
|
||||
|
||||
if (null === $customer) {
|
||||
$customer = new Customer();
|
||||
$customer = new Customer('foooo');
|
||||
$customer->setCountry('DE');
|
||||
$customer->setTimezone('Europre/Paris');
|
||||
$customer->setName('foooo');
|
||||
$repository->saveCustomer($customer);
|
||||
}
|
||||
|
||||
@@ -158,8 +156,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
$em->persist($activity);
|
||||
|
||||
// and finally a team
|
||||
$team = new Team();
|
||||
$team->setName('Testing customer 1 team');
|
||||
$team = new Team('Testing customer 1 team');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
|
||||
$team->addCustomer($customer);
|
||||
$team->addProject($project);
|
||||
@@ -176,7 +173,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/customers/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/customers/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Customer object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -230,10 +227,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/customers', 'POST', [], 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 create customers', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create customers');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -288,10 +282,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/customers/1', '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 customer', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update customer');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -330,32 +321,32 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\I18nConfig;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\I18nConfig
|
||||
*/
|
||||
class I18nConfigTest extends TestCase
|
||||
{
|
||||
public function testSetter()
|
||||
{
|
||||
$sut = new I18nConfig();
|
||||
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setIs24hours(false));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDuration('foo'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDate('bar'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDateTime('hello'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setFormDate('world'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setTime('fun'));
|
||||
}
|
||||
}
|
||||
75
tests/API/Model/PageActionTest.php
Normal file
75
tests/API/Model/PageActionTest.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\PageAction;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\PageAction
|
||||
*/
|
||||
class PageActionTest extends TestCase
|
||||
{
|
||||
public function testEmptySettings(): void
|
||||
{
|
||||
$options = [];
|
||||
|
||||
$sut = new PageAction('foo', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('foo', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('foo', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertEquals('', $obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertFalse($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
|
||||
public function testWithSettings(): void
|
||||
{
|
||||
$options = [
|
||||
'title' => 'bar',
|
||||
'url' => 'http://sdkfjhaslkdjfhaskljh',
|
||||
'class' => 'btn-primary',
|
||||
];
|
||||
|
||||
$sut = new PageAction('trash', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('trash', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('bar', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertEquals('http://sdkfjhaslkdjfhaskljh', $obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('btn-primary', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertTrue($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
|
||||
public function testDivider(): void
|
||||
{
|
||||
$options = [
|
||||
'title' => 'bar',
|
||||
'url' => null,
|
||||
'class' => 'btn-primary',
|
||||
];
|
||||
|
||||
$sut = new PageAction('divider0', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('divider0', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('bar', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertNull($obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('btn-primary', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertTrue($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,18 @@ class TimesheetConfigTest extends TestCase
|
||||
public function testSetter()
|
||||
{
|
||||
$sut = new TimesheetConfig();
|
||||
$sut->setIsAllowFutureTimes(false);
|
||||
$sut->setIsAllowOverlapping(false);
|
||||
$sut->setDefaultBeginTime('08:00');
|
||||
$sut->setTrackingMode('punch');
|
||||
$sut->setActiveEntriesHardLimit(3);
|
||||
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setIsAllowFutureTimes(false));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setIsAllowOverlapping(false));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setDefaultBeginTime('08:00'));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setTrackingMode('punch'));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setActiveEntriesSoftLimit(2));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setActiveEntriesHardLimit(3));
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertFalse($obj->getProperty('isAllowFutureTimes')->getValue($sut));
|
||||
self::assertFalse($obj->getProperty('isAllowOverlapping')->getValue($sut));
|
||||
self::assertEquals('08:00', $obj->getProperty('defaultBeginTime')->getValue($sut));
|
||||
self::assertEquals('punch', $obj->getProperty('trackingMode')->getValue($sut));
|
||||
self::assertEquals(3, $obj->getProperty('activeEntriesHardLimit')->getValue($sut));
|
||||
}
|
||||
}
|
||||
|
||||
29
tests/API/Model/VersionTest.php
Normal file
29
tests/API/Model/VersionTest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\Version;
|
||||
use App\Constants;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\Version
|
||||
*/
|
||||
class VersionTest extends TestCase
|
||||
{
|
||||
public function testValues(): void
|
||||
{
|
||||
$sut = new Version();
|
||||
|
||||
self::assertEquals(Constants::VERSION, $sut->version);
|
||||
self::assertEquals(Constants::VERSION_ID, $sut->versionId);
|
||||
self::assertEquals(Constants::SOFTWARE . ' ' . Constants::VERSION . ' by Kevin Papst.', $sut->copyright);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ use App\Repository\ProjectRateRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
|
||||
/**
|
||||
@@ -112,10 +111,10 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
$customer = $em->getRepository(Customer::class)->find(1);
|
||||
|
||||
$customer2 = (new Customer())->setName('first one')->setVisible(false)->setCountry('de')->setTimezone('Europe/Berlin');
|
||||
$customer2 = (new Customer('first one'))->setVisible(false)->setCountry('de')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer2);
|
||||
|
||||
$customer3 = (new Customer())->setName('second one')->setCountry('at')->setTimezone('Europe/Vienna');
|
||||
$customer3 = (new Customer('second one'))->setCountry('at')->setTimezone('Europe/Vienna');
|
||||
$em->persist($customer3);
|
||||
|
||||
$project = (new Project())->setName('first')->setVisible(false)->setCustomer($customer2);
|
||||
@@ -142,8 +141,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$em->persist($project);
|
||||
|
||||
// and a team
|
||||
$team = new Team();
|
||||
$team->setName('Testing project team');
|
||||
$team = new Team('Testing project team');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
|
||||
$team->addCustomer($customer);
|
||||
$team->addProject($project);
|
||||
@@ -161,7 +159,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getCollectionTestData
|
||||
*/
|
||||
public function testGetCollectionWithParams($url, $customer, $parameters, $expected)
|
||||
public function testGetCollectionWithParams($url, $customer, $parameters, $expected): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$imports = $this->loadProjectTestData($client);
|
||||
@@ -171,11 +169,18 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
if ($customerId !== null) {
|
||||
if (\array_key_exists('customer', $parameters)) {
|
||||
$parameters['customer'] = $customerId;
|
||||
} elseif (\array_key_exists('customers', $parameters)) {
|
||||
if (stripos($parameters['customers'], ',') !== false) {
|
||||
$parameters['customers'] = $customerId . ',' . $customerId;
|
||||
}
|
||||
if (\array_key_exists('customers', $parameters)) {
|
||||
if (!\is_array($parameters['customers'])) {
|
||||
throw new \InvalidArgumentException('customers needs to be an array');
|
||||
}
|
||||
$count = \count($parameters['customers']);
|
||||
if ($count === 2) {
|
||||
$parameters['customers'] = [$customerId, $customerId];
|
||||
} elseif ($count === 1) {
|
||||
$parameters['customers'] = [$customerId];
|
||||
} else {
|
||||
$parameters['customers'] = (string) $customerId;
|
||||
throw new \InvalidArgumentException('Invalid count for customers');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +200,10 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function getCollectionTestData()
|
||||
/**
|
||||
* @return \Generator<array<mixed>>
|
||||
*/
|
||||
public function getCollectionTestData(): iterable
|
||||
{
|
||||
// if you wonder why: case-sensitive ordering feels strange ... "Title" > "fifth”
|
||||
yield ['/api/projects', null, [], [[true, 1], [false, 1], [false, 3]]];
|
||||
@@ -203,15 +211,15 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false, 1]]];
|
||||
// customer is invisible, so nothing should be returned
|
||||
// customer is invisible => query only returns results for VisibilityInterface::SHOW_BOTH
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_VISIBLE], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => '2,2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
// customer is invisible, so nothing should be returned
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customers' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customers' => '2,2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => ['2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
@@ -219,8 +227,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$customer = (new Customer())
|
||||
->setName('first one')
|
||||
$customer = (new Customer('first one'))
|
||||
->setVisible(true)
|
||||
->setCountry('de')
|
||||
->setTimezone('Europe/Berlin')
|
||||
@@ -255,9 +262,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
'name' => 'first',
|
||||
'orderNumber' => null,
|
||||
// make sure the timezone is properly applied in serializer (see #1858)
|
||||
'orderDate' => '2019-11-29T14:35:17+1300',
|
||||
'start' => '2020-01-07T18:19:20+1300',
|
||||
'end' => '2021-03-23T00:00:01+1300',
|
||||
'orderDate' => '2019-11-29',
|
||||
'start' => '2020-01-07',
|
||||
'end' => '2021-03-23',
|
||||
'comment' => null,
|
||||
'visible' => true,
|
||||
'budget' => 0.0,
|
||||
@@ -274,7 +281,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/projects/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/projects/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Project object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -283,10 +290,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'visible' => true,
|
||||
'orderDate' => '2018-02-08T13:02:54',
|
||||
'start' => '2019-02-01T19:32:17',
|
||||
'end' => '2020-02-08T21:11:42',
|
||||
'orderDate' => '2018-04-17',
|
||||
'start' => '2019-02-01',
|
||||
'end' => '2020-02-08',
|
||||
'budget' => '999',
|
||||
'timeBudget' => '7200',
|
||||
'orderNumber' => '1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1',
|
||||
@@ -298,11 +304,13 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertEquals('2018-02-08T13:02:54+0000', $result['orderDate']);
|
||||
self::assertEquals('2019-02-01T19:32:17+0000', $result['start']);
|
||||
self::assertEquals('2020-02-08T21:11:42+0000', $result['end']);
|
||||
self::assertEquals('2018-04-17', $result['orderDate']);
|
||||
self::assertEquals('2019-02-01', $result['start']);
|
||||
self::assertEquals('2020-02-08', $result['end']);
|
||||
self::assertEquals('1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1', $result['orderNumber']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFields()
|
||||
@@ -311,7 +319,32 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => '0',
|
||||
'globalActivities' => true,
|
||||
'billable' => 1,
|
||||
'visible' => '',
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertTrue($result['billable']);
|
||||
self::assertTrue($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFieldsAndFalse()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => false,
|
||||
'billable' => false,
|
||||
'visible' => false,
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -322,15 +355,19 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFields2()
|
||||
public function testPostActionWithOtherFields3()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => '1',
|
||||
'globalActivities' => true,
|
||||
'billable' => true,
|
||||
'visible' => true,
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -341,6 +378,8 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertTrue($result['billable']);
|
||||
self::assertTrue($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithLeastFields()
|
||||
@@ -358,6 +397,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidUser()
|
||||
@@ -370,10 +412,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], 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 create projects', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create projects');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -422,10 +461,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/projects/1', '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 project', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update project');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -455,32 +491,32 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\ActivityRate;
|
||||
use App\Entity\CustomerRate;
|
||||
use App\Entity\ProjectRate;
|
||||
use App\Entity\RateInterface;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -41,7 +43,8 @@ trait RateControllerTestTrait
|
||||
'internal_rate' => 6.66,
|
||||
'is_fixed' => false
|
||||
];
|
||||
$this->assertEntityNotFoundForPost(User::ROLE_ADMIN, $this->getRateUrl(99), $data, 'Not found');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertEntityNotFoundForPost($client, $this->getRateUrl(99), $data);
|
||||
}
|
||||
|
||||
public function testAddRateMissingUserAction()
|
||||
@@ -71,10 +74,7 @@ trait RateControllerTestTrait
|
||||
];
|
||||
$this->request($client, $this->getRateUrl(), 'POST', [], 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('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testAddRateAction()
|
||||
@@ -178,12 +178,14 @@ trait RateControllerTestTrait
|
||||
|
||||
public function testDeleteRateEntityNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(99, 1));
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, $this->getRateUrl(99, 1));
|
||||
}
|
||||
|
||||
public function testDeleteRateRateNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(1, 99));
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, $this->getRateUrl(1, 99));
|
||||
}
|
||||
|
||||
public function testDeleteRateWithInvalidAssignment()
|
||||
@@ -198,9 +200,12 @@ trait RateControllerTestTrait
|
||||
public function testDeleteNotAllowed()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importTestRates(1);
|
||||
$rates = $this->importTestRates(1);
|
||||
|
||||
$this->request($client, $this->getRateUrl(1, 1), 'DELETE');
|
||||
/** @var ActivityRate|ProjectRate|CustomerRate $rate */
|
||||
$rate = $rates[0];
|
||||
|
||||
$this->request($client, $this->getRateUrl(1, $rate->getId()), 'DELETE');
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), 'Access denied.');
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use JMS\Serializer\JsonSerializationVisitor;
|
||||
use JMS\Serializer\SerializationContext;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Exception\FlattenException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
@@ -45,9 +46,10 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithEmptyConstraintsList()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
$validations = new ValidationFailedException($constraints, 'Uuups, that is broken');
|
||||
@@ -64,10 +66,11 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithUnsupportedException()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$handler->method('serializeToJson')->willReturn('foooo');
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$actual = $sut->serializeExceptionToJson(
|
||||
new JsonSerializationVisitor(),
|
||||
FlattenException::createFromThrowable(new \Exception('sdfsdf')),
|
||||
@@ -80,9 +83,10 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithConstraintsList()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$translator->method('trans')->willReturnArgument(0);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
@@ -123,10 +127,11 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithConstraintsListAndWrongException()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$handler->method('serializeToJson')->willReturn('foooo');
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$translator->method('trans')->willReturnArgument(0);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
|
||||
@@ -52,18 +52,12 @@ class StatusControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertArrayHasKey('version', $result);
|
||||
$this->assertArrayHasKey('versionId', $result);
|
||||
$this->assertArrayHasKey('candidate', $result);
|
||||
$this->assertArrayHasKey('semver', $result);
|
||||
$this->assertArrayHasKey('name', $result);
|
||||
$this->assertArrayHasKey('copyright', $result);
|
||||
|
||||
$this->assertSame(Constants::VERSION, $result['version']);
|
||||
$this->assertSame(Constants::VERSION_ID, $result['versionId']);
|
||||
$this->assertEquals(Constants::STATUS, $result['candidate']);
|
||||
$this->assertEquals(Constants::VERSION . '-' . Constants::STATUS, $result['semver']);
|
||||
$this->assertEquals(Constants::NAME, $result['name']);
|
||||
$this->assertEquals(
|
||||
'Kimai ' . Constants::VERSION . ' by Kevin Papst and contributors.',
|
||||
'Kimai ' . Constants::VERSION . ' by Kevin Papst.',
|
||||
$result['copyright']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
$this->assertApiCallValidationError($response, ['name', 'color']);
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidUser()
|
||||
public function testPostActionAsRegularUser()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importTagFixtures();
|
||||
@@ -104,11 +104,13 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
'name' => 'foo',
|
||||
];
|
||||
$this->request($client, '/api/tags', 'POST', [], 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 create tags', $json['message']);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TagEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
}
|
||||
|
||||
public function testPartOfEntries()
|
||||
@@ -147,6 +149,7 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/tags/' . PHP_INT_MAX);
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/tags/' . PHP_INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,12 +79,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_ADMIN, '/api/teams/3');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/teams/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Team object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testDeleteActionWithUnknownTeam()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/teams/255');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -114,10 +115,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
self::assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testPostActionWithValidationErrors()
|
||||
@@ -168,14 +166,14 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $updateId);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
|
||||
self::assertFalse($result['members'][1]['teamlead']);
|
||||
self::assertEquals(1, $result['members'][1]['user']['id']);
|
||||
@@ -247,7 +245,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertCount(1, $result['users']);
|
||||
self::assertCount(1, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'POST');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -255,7 +253,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(2, $result['users']);
|
||||
self::assertCount(2, $result['members']);
|
||||
}
|
||||
|
||||
public function testPostMemberActionErrors()
|
||||
@@ -273,26 +271,17 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/members/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/members/999');
|
||||
|
||||
// user not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/members/999');
|
||||
|
||||
// add user
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
// cannot add existing member
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User is already member of the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteMemberAction()
|
||||
@@ -310,7 +299,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertCount(4, $result['users']);
|
||||
self::assertCount(4, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -318,7 +307,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
}
|
||||
|
||||
public function testDeleteMemberActionErrors()
|
||||
@@ -338,32 +327,20 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/members/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/members/999');
|
||||
|
||||
// user not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/members/999');
|
||||
|
||||
// remove user
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
// cannot remove non-member
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User is not a member of the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
|
||||
// cannot remove teamlead
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Cannot remove teamlead', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostCustomerAction()
|
||||
@@ -405,16 +382,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/customers/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/customers/999');
|
||||
|
||||
// customer not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/customers/999');
|
||||
|
||||
// add customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
@@ -423,10 +394,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['customers']);
|
||||
|
||||
// cannot add existing customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to customer', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteCustomerAction()
|
||||
@@ -478,22 +446,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/customers/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/customers/999');
|
||||
|
||||
// customer not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/customers/999');
|
||||
|
||||
// cannot remove customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostProjectAction()
|
||||
@@ -534,16 +493,12 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/projects/999');
|
||||
|
||||
$this->request($client, '/api/teams/999/projects/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
|
||||
// project not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/projects/999');
|
||||
|
||||
// add project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
@@ -552,10 +507,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['projects']);
|
||||
|
||||
// cannot add existing project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to project', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteProjectAction()
|
||||
@@ -607,22 +559,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/projects/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/projects/999');
|
||||
|
||||
// project not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/projects/999');
|
||||
|
||||
// cannot remove project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostActivityAction()
|
||||
@@ -663,16 +606,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/activities/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/activities/999');
|
||||
|
||||
// activity not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/activities/999');
|
||||
|
||||
// add activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
@@ -681,10 +618,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['activities']);
|
||||
|
||||
// cannot add existing activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to activity', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteActivityAction()
|
||||
@@ -736,21 +670,12 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/activities/9999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/activities/9999');
|
||||
|
||||
// activity not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/9999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/activities/9999');
|
||||
|
||||
// cannot remove activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +32,15 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
public const TEST_TIMEZONE = 'Europe/London';
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @return Timesheet[]
|
||||
*/
|
||||
protected function importFixtureForUser(string $role): array
|
||||
protected function importFixtureForUser(string $role, int $amount = 10): array
|
||||
{
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
->setFixedRate(true)
|
||||
->setHourlyRate(true)
|
||||
->setAmount(10)
|
||||
->setAmount($amount)
|
||||
->setUser($this->getUserByRole($role))
|
||||
->setAllowEmptyDescriptions(false)
|
||||
->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1))
|
||||
@@ -85,7 +84,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
@@ -152,11 +150,11 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$end->setTime(23, 59, 59);
|
||||
|
||||
$query = [
|
||||
'customers' => '1',
|
||||
'projects' => '1',
|
||||
'activities' => '1',
|
||||
'customers' => ['1'],
|
||||
'projects' => ['1'],
|
||||
'activities' => ['1'],
|
||||
'page' => 2,
|
||||
'size' => 5,
|
||||
'size' => 4,
|
||||
'order' => 'DESC',
|
||||
'orderBy' => 'rate',
|
||||
'active' => 0,
|
||||
@@ -167,19 +165,19 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER, 22);
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertPagination($client->getResponse(), 2, 4, 6, 22);
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(5, \count($result));
|
||||
$this->assertEquals(4, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
}
|
||||
|
||||
public function testGetCollectionWithQueryFailsWith404OnOutOfRangedPage()
|
||||
{
|
||||
$modifiedAfter = new \DateTime('-1 hour');
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$end = new \DateTime('last day of this month');
|
||||
@@ -193,7 +191,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$this->request($client, '/api/timesheets', 'GET', $query);
|
||||
$this->assertApiException($client->getResponse(), ['code' => 404, 'message' => 'Page "19" does not exist. The currentPage must be inferior to "1"']);
|
||||
$this->assertApiException($client->getResponse(), ['code' => 404, 'message' => 'Not Found']);
|
||||
}
|
||||
|
||||
public function testGetCollectionWithSingleParamsQuery()
|
||||
@@ -426,7 +424,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntityFull', $result);
|
||||
self::assertApiResponseTypeStructure('TimesheetExpanded', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
$this->assertTrue($result['duration'] == 28800 || $result['duration'] == 28860); // 1 minute rounding might be applied
|
||||
$this->assertEquals(2016, $result['rate']);
|
||||
@@ -469,7 +467,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setVisible(false)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setVisible(false)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
|
||||
$em->persist($project);
|
||||
@@ -495,7 +493,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setVisible(true)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setVisible(true)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
|
||||
$em->persist($project);
|
||||
@@ -519,7 +517,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer->setBillable(false);
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setCustomer($customer);
|
||||
@@ -549,7 +547,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer->setBillable(false);
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setCustomer($customer);
|
||||
@@ -627,15 +625,12 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), '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('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/255', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/255', []);
|
||||
}
|
||||
|
||||
public function testInvalidPatchAction()
|
||||
@@ -647,14 +642,14 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
'activity' => 10,
|
||||
'project' => 1,
|
||||
'begin' => (new \DateTime())->format('Y-m-d H:m'),
|
||||
'end' => (new \DateTime('- 7 hours'))->format('Y-m-d H:m'),
|
||||
'end' => (new \DateTime('- 1 hours'))->format('Y-m-d H:m'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$this->assertApiCallValidationError($response, ['end', 'activity']);
|
||||
$this->assertApiCallValidationError($response, ['activity'], false, ['End date must not be earlier then start date.']);
|
||||
}
|
||||
|
||||
// TODO: TEST PATCH FOR EXPORTED TIMESHEET FOR USER WITHOUT PERMISSION IS REJECTED
|
||||
@@ -679,7 +674,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/timesheets/255', 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/timesheets/255');
|
||||
}
|
||||
|
||||
public function testDeleteActionForDifferentUser()
|
||||
@@ -702,10 +698,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'DELETE');
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteActionForExportedRecordIsNotAllowed()
|
||||
@@ -722,7 +715,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$em->flush();
|
||||
|
||||
$this->request($client, '/api/timesheets/' . $id, 'DELETE');
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), 'Access denied.');
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteActionForExportedRecordIsAllowedForAdmin()
|
||||
@@ -744,7 +739,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testGetRecentAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$start = new \DateTime('-10 days');
|
||||
|
||||
@@ -759,7 +754,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$query = [
|
||||
'user' => 'all',
|
||||
'size' => 2,
|
||||
'begin' => $start->format(self::DATE_FORMAT_HTML5),
|
||||
];
|
||||
@@ -805,7 +799,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
|
||||
$start = new \DateTime('-8 hours');
|
||||
$start = new \DateTime('-4 hours');
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
@@ -859,7 +853,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testStopThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/11/stop', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/11/stop', []);
|
||||
}
|
||||
|
||||
public function testStopNotAllowedForUser()
|
||||
@@ -902,7 +896,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
->setTags(['Test', 'Administration']);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$query = ['tags' => 'Test'];
|
||||
$query = ['tags' => ['Test']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -911,7 +905,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEquals(10, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
|
||||
$query = ['tags' => 'Test,Admin'];
|
||||
$query = ['tags' => ['Test', 'Admin']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -920,7 +914,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEquals(10, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
|
||||
$query = ['tags' => 'Nothing-2-see,here'];
|
||||
$query = ['tags' => ['Nothing-2-see', 'here']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -938,7 +932,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$data = [
|
||||
'description' => 'foo',
|
||||
'tags' => 'another,testing,bar'
|
||||
'tags' => ['another', 'testing', 'bar']
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
||||
|
||||
@@ -969,7 +963,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$data = [
|
||||
'description' => 'foo',
|
||||
'tags' => 'another,testing,bar'
|
||||
'tags' => ['another', 'testing', 'bar']
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
||||
|
||||
@@ -1060,7 +1054,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testRestartThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/42/restart', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/42/restart', []);
|
||||
}
|
||||
|
||||
public function testDuplicateAction()
|
||||
@@ -1099,7 +1093,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDuplicateThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/11/duplicate', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/11/duplicate', []);
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
@@ -1143,12 +1137,12 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testExportThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/export', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/export', []);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/meta', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/meta', []);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
@@ -1159,7 +1153,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1170,8 +1164,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1182,8 +1176,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1192,7 +1186,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$timesheets = $this->importFixtureForUser(User::ROLE_USER);
|
||||
$id = $timesheets[0]->getId();
|
||||
static::$container->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -215,10 +214,7 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/users', 'POST', [], 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('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testPatchAction()
|
||||
@@ -229,7 +225,6 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
'email' => 'foo@example.com',
|
||||
'title' => 'asdfghjkl',
|
||||
'plainPassword' => 'foo@example.com',
|
||||
'enabled' => true,
|
||||
'language' => 'ru',
|
||||
'timezone' => 'Europe/Paris',
|
||||
'roles' => [
|
||||
@@ -240,10 +235,11 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/users', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertFalse($result['enabled']);
|
||||
|
||||
$data = [
|
||||
'title' => 'qwertzui',
|
||||
'enabled' => false,
|
||||
'enabled' => true,
|
||||
'language' => 'it',
|
||||
'timezone' => 'America/New_York',
|
||||
'roles' => [
|
||||
@@ -259,7 +255,7 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['username']);
|
||||
self::assertEquals('qwertzui', $result['title']);
|
||||
self::assertFalse($result['enabled']);
|
||||
self::assertTrue($result['enabled']);
|
||||
self::assertEquals('it', $result['language']);
|
||||
self::assertEquals('America/New_York', $result['timezone']);
|
||||
self::assertEquals(['ROLE_TEAMLEAD'], $result['roles']);
|
||||
|
||||
@@ -97,6 +97,8 @@ class ActivityServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
@@ -115,6 +117,8 @@ class ActivityServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
@@ -136,6 +140,8 @@ class ActivityServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
|
||||
@@ -47,10 +47,6 @@ class ActivateUserCommandTest extends KernelTestCase
|
||||
|
||||
$command = $application->find('kimai:user:activate');
|
||||
self::assertInstanceOf(ActivateUserCommand::class, $command);
|
||||
|
||||
// test alias
|
||||
$command = $application->find('fos:user:activate');
|
||||
self::assertInstanceOf(ActivateUserCommand::class, $command);
|
||||
}
|
||||
|
||||
protected function callCommand(?string $username)
|
||||
@@ -80,7 +76,7 @@ class ActivateUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('chris_user');
|
||||
$user = $userRepository->loadUserByIdentifier('chris_user');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertTrue($user->isEnabled());
|
||||
}
|
||||
|
||||
@@ -116,23 +116,12 @@ class BundleInstallerCommandTest extends KernelTestCase
|
||||
|
||||
class FakeCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $exception = null;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $exitCode = 0;
|
||||
|
||||
public function __construct(string $commandName, int $exitCode, ?string $executeThrows = null)
|
||||
public function __construct(string $commandName, private int $exitCode, private ?string $exception = null)
|
||||
{
|
||||
parent::__construct($commandName);
|
||||
$this->exitCode = $exitCode;
|
||||
$this->exception = $executeThrows;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (null !== $this->exception) {
|
||||
throw new \Exception($this->exception);
|
||||
|
||||
@@ -17,6 +17,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\Command\ChangePasswordCommand
|
||||
@@ -25,10 +26,7 @@ use Symfony\Component\Console\Tester\CommandTester;
|
||||
*/
|
||||
class ChangePasswordCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
private $application;
|
||||
private Application $application;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
@@ -42,19 +40,15 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
$this->application->add(new ChangePasswordCommand($userService));
|
||||
}
|
||||
|
||||
public function testCommandName()
|
||||
public function testCommandName(): void
|
||||
{
|
||||
$application = $this->application;
|
||||
|
||||
$command = $application->find('kimai:user:password');
|
||||
self::assertInstanceOf(ChangePasswordCommand::class, $command);
|
||||
|
||||
// test alias
|
||||
$command = $application->find('fos:user:change-password');
|
||||
self::assertInstanceOf(ChangePasswordCommand::class, $command);
|
||||
}
|
||||
|
||||
protected function callCommand(?string $username, ?string $password)
|
||||
protected function callCommand(?string $username, ?string $password): CommandTester
|
||||
{
|
||||
$command = $this->application->find('kimai:user:password');
|
||||
$input = [
|
||||
@@ -85,25 +79,32 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
return $commandTester;
|
||||
}
|
||||
|
||||
public function testChangePassword()
|
||||
public function testChangePassword(): void
|
||||
{
|
||||
$commandTester = $this->callCommand('john_user', '0987654321');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[OK] Changed password for user "john_user".', $output);
|
||||
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('john_user');
|
||||
$userRepository = self::getContainer()->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
|
||||
$container = self::$kernel->getContainer();
|
||||
$encoderService = $container->get('security.password_encoder');
|
||||
self::assertTrue($encoderService->isPasswordValid($user, '0987654321'));
|
||||
/** @var PasswordHasherFactoryInterface $passwordEncoder */
|
||||
$passwordEncoder = self::getContainer()->get('security.password_hasher_factory');
|
||||
self::assertTrue($passwordEncoder->getPasswordHasher($user)->verify($user->getPassword(), '0987654321'));
|
||||
}
|
||||
|
||||
public function testWithMissingUsername()
|
||||
public function testChangePasswordFailsOnShortPassword(): void
|
||||
{
|
||||
$commandTester = $this->callCommand('john_user', '1');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] plainPassword: This value is too short.', $output);
|
||||
}
|
||||
|
||||
public function testWithMissingUsername(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Not enough arguments (missing: "username").');
|
||||
@@ -111,7 +112,7 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
$this->callCommand(null, '1234567890');
|
||||
}
|
||||
|
||||
public function testWithMissingPasswordAsksForPassword()
|
||||
public function testWithMissingPasswordAsksForPassword(): void
|
||||
{
|
||||
$commandTester = $this->callCommand('john_user', null);
|
||||
$output = $commandTester->getDisplay();
|
||||
|
||||
@@ -23,10 +23,7 @@ use Symfony\Component\Console\Tester\CommandTester;
|
||||
*/
|
||||
class CreateUserCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
private $application;
|
||||
private Application $application;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
@@ -40,16 +37,15 @@ class CreateUserCommandTest extends KernelTestCase
|
||||
));
|
||||
}
|
||||
|
||||
public function testCreateUserFailsForShortPassword()
|
||||
public function testCreateUserFailsForShortPassword(): void
|
||||
{
|
||||
$commandTester = $this->createUser('MyTestUser', 'user@example.com', 'ROLE_USER', 'foobar');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] plainPassword (foobar)', $output);
|
||||
$this->assertStringContainsString('This value is too short. It should have 8 characters or more.', $output);
|
||||
$this->assertStringContainsString('[ERROR] plainPassword: This value is too short.', $output);
|
||||
}
|
||||
|
||||
public function testCreateUser()
|
||||
public function testCreateUser(): void
|
||||
{
|
||||
$commandTester = $this->createUser('MyTestUser', 'user@example.com', 'ROLE_USER', 'foobar12');
|
||||
|
||||
@@ -59,12 +55,12 @@ class CreateUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('MyTestUser');
|
||||
$user = $userRepository->loadUserByIdentifier('MyTestUser');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertNotNull($user);
|
||||
}
|
||||
|
||||
protected function createUser($username, $email, $role, $password)
|
||||
protected function createUser($username, $email, $role, $password): CommandTester
|
||||
{
|
||||
$command = $this->application->find('kimai:user:create');
|
||||
$commandTester = new CommandTester($command);
|
||||
@@ -79,44 +75,38 @@ class CreateUserCommandTest extends KernelTestCase
|
||||
return $commandTester;
|
||||
}
|
||||
|
||||
public function testUserWithEmptyFieldsTriggersValidationProblem()
|
||||
public function testUserWithEmptyFieldsTriggersValidationProblem(): void
|
||||
{
|
||||
$commandTester = $this->createUser('xx', '', 'ROLE_USER', '');
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] email ()', $output);
|
||||
$this->assertStringContainsString('This value should not be blank', $output);
|
||||
$this->assertStringContainsString('[ERROR] plainPassword ()', $output);
|
||||
$this->assertStringContainsString('This value should not be blank', $output);
|
||||
$this->assertStringContainsString('[ERROR] plainPassword ()', $output);
|
||||
$this->assertStringContainsString('This value is too short. It should have 8 characters or more', $output);
|
||||
$this->assertStringContainsString('[ERROR] email: This value should not be blank', $output);
|
||||
$this->assertStringContainsString('[ERROR] plainPassword: This value should not be blank', $output);
|
||||
$this->assertStringContainsString('[ERROR] plainPassword: This value is too short.', $output);
|
||||
}
|
||||
|
||||
public function testUserAlreadyExisting()
|
||||
public function testUserAlreadyExisting(): void
|
||||
{
|
||||
$this->createUser('MyTestUser', 'user@example.com', 'ROLE_USER', 'foobar123');
|
||||
$commandTester = $this->createUser('MyTestUser', 'user2@example.com', 'ROLE_USER', 'foobar123');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] username (MyTestUser)', $output);
|
||||
$this->assertStringContainsString('The username is already used.', $output);
|
||||
$this->assertStringContainsString('[ERROR] username: The username is already used.', $output);
|
||||
}
|
||||
|
||||
public function testEmailAlreadyExisting()
|
||||
public function testEmailAlreadyExisting(): void
|
||||
{
|
||||
$this->createUser('MyTestUser', 'user@example.com', 'ROLE_USER', 'foobar12');
|
||||
$commandTester = $this->createUser('MyTestUser2', 'user@example.com', 'ROLE_USER', 'foobar');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] email (MyTestUser2)', $output);
|
||||
$this->assertStringContainsString(' The email is already used.', $output);
|
||||
$this->assertStringContainsString('[ERROR] email: The email is already used.', $output);
|
||||
}
|
||||
|
||||
public function testUserEmail()
|
||||
public function testUserEmail(): void
|
||||
{
|
||||
$commandTester = $this->createUser('MyTestUser', 'ROLE_USER', 'ROLE_USER', 'foobar12');
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[ERROR] email (ROLE_USER)', $output);
|
||||
$this->assertStringContainsString('This value is not a valid email address', $output);
|
||||
$this->assertStringContainsString('[ERROR] email: This value is not a valid email address', $output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,6 @@ class DeactivateUserCommandTest extends KernelTestCase
|
||||
|
||||
$command = $application->find('kimai:user:deactivate');
|
||||
self::assertInstanceOf(DeactivateUserCommand::class, $command);
|
||||
|
||||
// test alias
|
||||
$command = $application->find('fos:user:deactivate');
|
||||
self::assertInstanceOf(DeactivateUserCommand::class, $command);
|
||||
}
|
||||
|
||||
protected function callCommand(?string $username)
|
||||
@@ -80,7 +76,7 @@ class DeactivateUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('john_user');
|
||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertFalse($user->isEnabled());
|
||||
}
|
||||
|
||||
@@ -48,10 +48,6 @@ class DemoteUserCommandTest extends KernelTestCase
|
||||
|
||||
$command = $application->find('kimai:user:demote');
|
||||
self::assertInstanceOf(DemoteUserCommand::class, $command);
|
||||
|
||||
// test alias
|
||||
$command = $application->find('fos:user:demote');
|
||||
self::assertInstanceOf(DemoteUserCommand::class, $command);
|
||||
}
|
||||
|
||||
protected function callCommand(?string $username, ?string $role, bool $super = false)
|
||||
@@ -89,7 +85,7 @@ class DemoteUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('tony_teamlead');
|
||||
$user = $userRepository->loadUserByIdentifier('tony_teamlead');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertFalse($user->hasTeamleadRole());
|
||||
}
|
||||
@@ -104,7 +100,7 @@ class DemoteUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('susan_super');
|
||||
$user = $userRepository->loadUserByIdentifier('susan_super');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertFalse($user->isSuperAdmin());
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ use App\Tests\KernelTestTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
@@ -36,10 +37,7 @@ class ExportCreateCommandTest extends KernelTestCase
|
||||
{
|
||||
use KernelTestTrait;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
protected Application $application;
|
||||
|
||||
private function clearExportFiles()
|
||||
{
|
||||
@@ -70,7 +68,7 @@ class ExportCreateCommandTest extends KernelTestCase
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$application = new Application($kernel);
|
||||
$container = self::$container;
|
||||
$container = self::getContainer();
|
||||
|
||||
$application->add(new ExportCreateCommand(
|
||||
$container->get(ServiceExport::class),
|
||||
@@ -236,7 +234,7 @@ class ExportCreateCommandTest extends KernelTestCase
|
||||
$this->prepareFixtures($start);
|
||||
$options = ['--template' => 'csv', '--email' => ['foo@example.com', 'foo2@example.com'], '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')];
|
||||
|
||||
$mailer = $this->createMock(KimaiMailer::class);
|
||||
$mailer = $this->createMock(MailerInterface::class);
|
||||
$mailer->expects($this->exactly(2))->method('send');
|
||||
|
||||
$application = $this->createApplication($mailer);
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\ImportCustomerCommand;
|
||||
use App\Importer\ImporterService;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class ImportCustomerCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$container = self::$kernel->getContainer();
|
||||
|
||||
$importer = $container->get(ImporterService::class);
|
||||
|
||||
$this->application->add(new ImportCustomerCommand($importer));
|
||||
}
|
||||
|
||||
public function testCommandName()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
self::assertInstanceOf(ImportCustomerCommand::class, $command);
|
||||
}
|
||||
|
||||
public function testImportWithMissingFile()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/foo_bar.csv1'
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Kimai importer: Customers', $result);
|
||||
self::assertStringContainsString('[ERROR] File not existing or not readable', $result);
|
||||
self::assertStringContainsString('_data/foo_bar', $result);
|
||||
|
||||
self::assertEquals(2, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithUnknownReader()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--reader' => 'fooo',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('[ERROR] Unknown import reader: fooo', $result);
|
||||
|
||||
self::assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testImportWithUnknownImporter()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--importer' => 'fooo',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('[ERROR] Unknown customer importer: fooo', $result);
|
||||
|
||||
self::assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImport()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers.csv'
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 10 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 10 customers, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 9 customer', $result);
|
||||
self::assertStringContainsString('[OK] Updated 1 customer', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportSkipUpdate()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers.csv',
|
||||
'--no-update' => true,
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 10 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 10 customers, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 9 customer', $result);
|
||||
self::assertStringContainsString('[OK] Skipped 1 existing customer', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithSemicolon()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--importer' => 'default',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 10 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 10 customers, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 10 customer', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testGrandtotalImportWithInvalidCsvFile()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--importer' => 'grandtotal',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Invalid row 1: Missing customer name', $result);
|
||||
self::assertStringContainsString('! [CAUTION] Not importing, previous 10 errors need to be fixed first.', $result);
|
||||
|
||||
self::assertEquals(3, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testGrandtotalImport()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/grandtotal_en.csv',
|
||||
'--importer' => 'grandtotal',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 1 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 1 customers, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 1 customer', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testGrandtotalImportGerman()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:customer');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/grandtotal_de.csv',
|
||||
'--importer' => 'grandtotal',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 2 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 2 customers, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 1 customer', $result);
|
||||
self::assertStringContainsString('[OK] Updated 1 customer', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\ImportProjectCommand;
|
||||
use App\Importer\ImporterService;
|
||||
use App\Repository\TeamRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class ImportProjectCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$container = self::$kernel->getContainer();
|
||||
|
||||
$importer = $container->get(ImporterService::class);
|
||||
$teams = $this->createMock(TeamRepository::class);
|
||||
/** @var UserRepository $users */
|
||||
$users = $container->get(UserRepository::class);
|
||||
|
||||
$this->application->add(new ImportProjectCommand($importer, $teams, $users));
|
||||
}
|
||||
|
||||
public function testCommandName()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
self::assertInstanceOf(ImportProjectCommand::class, $command);
|
||||
}
|
||||
|
||||
public function testImportWithMissingFile()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/foo_bar.csv1'
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Kimai importer: Projects', $result);
|
||||
self::assertStringContainsString('[ERROR] File not existing or not readable', $result);
|
||||
self::assertStringContainsString('_data/foo_bar', $result);
|
||||
|
||||
self::assertEquals(2, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithUnknownReader()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--reader' => 'fooo',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('[ERROR] Unknown import reader: fooo', $result);
|
||||
|
||||
self::assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testImportWithUnknownImporter()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/customers2.csv',
|
||||
'--importer' => 'grandtotal',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('[ERROR] Unknown project importer: grandtotal', $result);
|
||||
|
||||
self::assertEquals(1, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImport()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects.csv',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 3 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 3 projects, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 2 projects', $result);
|
||||
self::assertStringContainsString('[OK] Updated 1 projects', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportSkipUpdate()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects.csv',
|
||||
'--no-update' => true,
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 3 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 3 projects, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 2 projects', $result);
|
||||
self::assertStringContainsString('[OK] Skipped 1 existing projects', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithInvalidCustomerMapping()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects_invalid.csv',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 3 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('[ERROR] Invalid row 2: Customer mismatch for project', $result);
|
||||
self::assertStringContainsString('[CAUTION] Not importing, previous 1 errors need to be fixed first.', $result);
|
||||
|
||||
self::assertEquals(3, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithSemicolon()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects2.csv',
|
||||
'--importer' => 'default',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 39 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 39 projects, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 39 projects', $result);
|
||||
self::assertStringContainsString('[OK] Imported 10 customers', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testGrandtotalImportWithInvalidCsvFile()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects.csv',
|
||||
'--reader' => 'csv-semicolon',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Invalid row 1: Missing customer name', $result);
|
||||
self::assertStringContainsString('! [CAUTION] Not importing, previous 3 errors need to be fixed first.', $result);
|
||||
|
||||
self::assertEquals(3, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithSemicolonAndTeamlead()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects2.csv',
|
||||
'--importer' => 'default',
|
||||
'--reader' => 'csv-semicolon',
|
||||
'--teamlead' => 'clara_customer',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('Found 39 rows to process, converting now ...', $result);
|
||||
self::assertStringContainsString('Converted 39 projects, importing into Kimai now ...', $result);
|
||||
self::assertStringContainsString('[OK] Imported 39 projects', $result);
|
||||
self::assertStringContainsString('[OK] Imported 10 customers', $result);
|
||||
self::assertStringContainsString('[OK] Created 39 teams', $result);
|
||||
|
||||
self::assertEquals(0, $commandTester->getStatusCode());
|
||||
}
|
||||
|
||||
public function testDefaultImportWithSemicolonAndMissingTeamlead()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:project');
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->setInputs(['no']);
|
||||
$commandTester->execute([
|
||||
'command' => $command->getName(),
|
||||
'file' => __DIR__ . '/../Importer/_data/projects2.csv',
|
||||
'--importer' => 'default',
|
||||
'--reader' => 'csv-semicolon',
|
||||
'--teamlead' => 'foobar',
|
||||
]);
|
||||
|
||||
$result = $commandTester->getDisplay();
|
||||
|
||||
self::assertStringContainsString('You requested to create empty teams for each project', $result);
|
||||
self::assertStringContainsString('Please create a user with the name (or email) foobar', $result);
|
||||
|
||||
self::assertEquals(3, $commandTester->getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?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\Command;
|
||||
|
||||
use App\Command\ImportTimesheetCommand;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\TagRepository;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\Command\ImportTimesheetCommand
|
||||
* @group integration
|
||||
*/
|
||||
class ImportTimesheetCommandTest extends KernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
|
||||
$customers = $this->createMock(CustomerRepository::class);
|
||||
$projects = $this->createMock(ProjectRepository::class);
|
||||
$activities = $this->createMock(ActivityRepository::class);
|
||||
$users = $this->createMock(UserRepository::class);
|
||||
$tagRepository = $this->createMock(TagRepository::class);
|
||||
$timesheets = $this->createMock(TimesheetRepository::class);
|
||||
$configuration = $this->createMock(SystemConfiguration::class);
|
||||
$encoder = $this->createMock(UserPasswordEncoderInterface::class);
|
||||
|
||||
$this->application->add(new ImportTimesheetCommand($customers, $projects, $activities, $users, $tagRepository, $timesheets, $configuration, $encoder));
|
||||
}
|
||||
|
||||
public function testCommandName()
|
||||
{
|
||||
$command = $this->application->find('kimai:import:timesheet');
|
||||
self::assertInstanceOf(ImportTimesheetCommand::class, $command);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,8 @@ class InstallCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
|
||||
$this->application->add(new InstallCommand(
|
||||
$container->get('doctrine')->getConnection()
|
||||
$container->get('doctrine')->getConnection(),
|
||||
$this->application->getKernel()->getEnvironment()
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace App\Tests\Command;
|
||||
use App\Command\InvoiceCreateCommand;
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerMeta;
|
||||
use App\Entity\Project;
|
||||
use App\Invoice\ServiceInvoice;
|
||||
use App\Repository\CustomerRepository;
|
||||
@@ -36,12 +35,9 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
{
|
||||
use KernelTestTrait;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
protected Application $application;
|
||||
|
||||
private function clearInvoiceFiles()
|
||||
private function clearInvoiceFiles(): void
|
||||
{
|
||||
$path = __DIR__ . '/../_data/invoices/';
|
||||
|
||||
@@ -65,7 +61,7 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$this->clearInvoiceFiles();
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$container = self::$container;
|
||||
$container = self::getContainer();
|
||||
|
||||
$this->application->add(new InvoiceCreateCommand(
|
||||
$container->get(ServiceInvoice::class),
|
||||
@@ -94,7 +90,7 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
* @param array $options
|
||||
* @return CommandTester
|
||||
*/
|
||||
protected function createInvoice(array $options = [])
|
||||
protected function createInvoice(array $options = []): CommandTester
|
||||
{
|
||||
$command = $this->application->find('kimai:invoice:create');
|
||||
$commandTester = new CommandTester($command);
|
||||
@@ -105,7 +101,7 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
return $commandTester;
|
||||
}
|
||||
|
||||
protected function assertCommandErrors(array $options = [], string $errorMessage = '')
|
||||
protected function assertCommandErrors(array $options = [], string $errorMessage = ''): void
|
||||
{
|
||||
$commandTester = $this->createInvoice($options);
|
||||
|
||||
@@ -113,67 +109,62 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$this->assertStringContainsString('[ERROR] ' . $errorMessage, $output);
|
||||
}
|
||||
|
||||
public function testCreateWithUnknownExportFilter()
|
||||
public function testCreateWithUnknownExportFilter(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'foo'], 'Unknown "exported" filter given');
|
||||
}
|
||||
|
||||
public function testCreateWithMissingUser()
|
||||
public function testCreateWithMissingUser(): void
|
||||
{
|
||||
$this->assertCommandErrors([], 'You must set a "user" to create invoices');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidUser()
|
||||
public function testCreateWithInvalidUser(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => 'assdfd'], 'The given username "assdfd" could not be resolved');
|
||||
}
|
||||
|
||||
public function testCreateWithMissingEnd()
|
||||
public function testCreateWithMissingEnd(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--start' => '2020-01-01'], 'You need to supply a end date if a start date was given');
|
||||
}
|
||||
|
||||
public function testCreateByCustomerAndByProject()
|
||||
public function testCreateByCustomerAndByProject(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--by-customer' => null, '--by-project' => null], 'You cannot mix "by-customer" and "by-project"');
|
||||
}
|
||||
|
||||
public function testCreateWithMissingGenerationMode()
|
||||
public function testCreateWithMissingGenerationMode(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN], 'Could not determine generation mode');
|
||||
}
|
||||
|
||||
public function testCreateWithMissingTemplate()
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1], 'You must either pass the "template" or "template-meta" option');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidStart()
|
||||
public function testCreateWithInvalidStart(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1, '--exported' => 'exported', '--template' => 'x', '--start' => 'öäüß', '--end' => '2020-01-01'], 'Invalid start date given');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidEnd()
|
||||
public function testCreateWithInvalidEnd(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1, '--template' => 'x', '--start' => '2020-01-01', '--end' => 'öäüß'], 'Invalid end date given');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidPreviewDirectory()
|
||||
public function testCreateWithInvalidPreviewDirectory(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1, '--template' => 'x', '--start' => '2020-01-01', '--end' => '2020-01-02', '--preview' => '/kjhg/'], 'Invalid preview directory given');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidCustomer()
|
||||
public function testCreateWithInvalidCustomer(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 3, '--template' => 'x'], 'Unknown customer ID: 3');
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidProject()
|
||||
public function testCreateWithInvalidProject(): void
|
||||
{
|
||||
$this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--project' => 3, '--template' => 'x'], 'Unknown project ID: 3');
|
||||
}
|
||||
|
||||
public function testCreateInvoice()
|
||||
public function testCreateInvoice(): void
|
||||
{
|
||||
$fixture = new InvoiceTemplateFixtures();
|
||||
$this->importFixture($fixture);
|
||||
@@ -190,15 +181,19 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$this->assertStringContainsString('/tests/_data/invoices/' . ((new \DateTime())->format('Y')) . '-001-Test.html |', $output);
|
||||
}
|
||||
|
||||
protected function prepareFixtures(\DateTime $start)
|
||||
/**
|
||||
* @param \DateTime $start
|
||||
* @return array<Customer>
|
||||
*/
|
||||
protected function prepareFixtures(\DateTime $start): array
|
||||
{
|
||||
$fixture = new InvoiceTemplateFixtures();
|
||||
$invoiceTemplate = $this->importFixture($fixture);
|
||||
|
||||
$fixture = new CustomerFixtures();
|
||||
$fixture->setAmount(1);
|
||||
$fixture->setCallback(function (Customer $customer) {
|
||||
$meta = new CustomerMeta();
|
||||
$meta->setName('template');
|
||||
$meta->setValue('Invoice');
|
||||
$customer->setMetaField($meta);
|
||||
$fixture->setCallback(function (Customer $customer) use ($invoiceTemplate) {
|
||||
$customer->setInvoiceTemplate($invoiceTemplate[0]);
|
||||
});
|
||||
$customer = $this->importFixture($fixture)[0];
|
||||
|
||||
@@ -214,26 +209,23 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$fixture->setProjects($projects);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$fixture = new InvoiceTemplateFixtures();
|
||||
$this->importFixture($fixture);
|
||||
|
||||
return [$customer];
|
||||
}
|
||||
|
||||
public function testCreateInvoiceByCustomer()
|
||||
public function testCreateInvoiceByCustomer(): void
|
||||
{
|
||||
$start = new \DateTime('-2 months');
|
||||
$end = new \DateTime();
|
||||
|
||||
$this->prepareFixtures($start);
|
||||
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--by-customer' => null, '--template-meta' => 'template', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--by-customer' => null, '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Created 1 invoice(s) ', $output);
|
||||
}
|
||||
|
||||
public function testCreateInvoiceByCustomerId()
|
||||
public function testCreateInvoiceByCustomerId(): void
|
||||
{
|
||||
$start = new \DateTime('-2 months');
|
||||
$end = new \DateTime();
|
||||
@@ -241,26 +233,26 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$imports = $this->prepareFixtures($start);
|
||||
$customer = $imports[0]->getId();
|
||||
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => $customer . ',1', '--template-meta' => 'template', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => $customer . ',1', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Created 1 invoice(s) ', $output);
|
||||
}
|
||||
|
||||
public function testCreateInvoiceByProject()
|
||||
public function testCreateInvoiceByProject(): void
|
||||
{
|
||||
$start = new \DateTime('-2 months');
|
||||
$end = new \DateTime();
|
||||
|
||||
$this->prepareFixtures($start);
|
||||
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'all', '--by-project' => null, '--template-meta' => 'template', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'all', '--by-project' => null, '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Created 1 invoice(s) ', $output);
|
||||
}
|
||||
|
||||
public function testCreateInvoiceByProjectId()
|
||||
public function testCreateInvoiceByProjectId(): void
|
||||
{
|
||||
$start = new \DateTime('-2 months');
|
||||
$end = new \DateTime();
|
||||
@@ -273,14 +265,14 @@ class InvoiceCreateCommandTest extends KernelTestCase
|
||||
$this->assertStringContainsString('Created 1 invoice(s) ', $output);
|
||||
}
|
||||
|
||||
public function testCreateInvoiceByProjectWithPreview()
|
||||
public function testCreateInvoiceByProjectWithPreview(): void
|
||||
{
|
||||
$start = new \DateTime('-2 months');
|
||||
$end = new \DateTime();
|
||||
|
||||
$this->prepareFixtures($start);
|
||||
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'all', '--preview' => sys_get_temp_dir(), '--by-project' => null, '--template-meta' => 'template', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
$commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'all', '--preview' => sys_get_temp_dir(), '--by-project' => null, '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]);
|
||||
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('Created 1 invoice(s) ', $output);
|
||||
|
||||
@@ -13,7 +13,7 @@ use App\Command\KimaiImporterCommand;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,7 @@ class KimaiImporterCommandTest extends KernelTestCase
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
|
||||
$encoder = $this->createMock(UserPasswordEncoderInterface::class);
|
||||
$encoder = $this->createMock(UserPasswordHasherInterface::class);
|
||||
$registry = $this->createMock(ManagerRegistry::class);
|
||||
$validator = $this->createMock(ValidatorInterface::class);
|
||||
|
||||
|
||||
@@ -30,19 +30,14 @@ class PluginCommandTest extends KernelTestCase
|
||||
public function testWithPlugins()
|
||||
{
|
||||
$plugin1 = $this->getMockBuilder(PluginInterface::class)->onlyMethods(['getName', 'getPath'])->getMock();
|
||||
$plugin1->expects($this->any())->method('getName')->willReturn('Test-Bundle');
|
||||
$plugin1->expects($this->once())->method('getPath')->willReturn(__DIR__);
|
||||
$plugin1->expects($this->any())->method('getName')->willReturn('TestBundle');
|
||||
$plugin1->expects($this->once())->method('getPath')->willReturn(__DIR__ . '/../Plugin/Fixtures/TestPlugin');
|
||||
|
||||
$plugin2 = $this->getMockBuilder(PluginInterface::class)->onlyMethods(['getName', 'getPath'])->getMock();
|
||||
$plugin2->expects($this->any())->method('getName')->willReturn('Another one');
|
||||
$plugin2->expects($this->once())->method('getPath')->willReturn('BundleDirectory');
|
||||
|
||||
$commandTester = $this->getCommandTester([$plugin1, $plugin2], []);
|
||||
$commandTester = $this->getCommandTester([$plugin1], []);
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString(__DIR__, $output);
|
||||
$this->assertStringContainsString('BundleDirectory', $output);
|
||||
$this->assertStringContainsString('Test-Bundle', $output);
|
||||
$this->assertStringContainsString('Another one', $output);
|
||||
$this->assertStringContainsString('Plugin/Fixtures/TestPlugin', $output);
|
||||
$this->assertStringContainsString('TestPlugin from composer.json', $output);
|
||||
}
|
||||
|
||||
protected function getCommandTester(array $plugins, array $options = [])
|
||||
|
||||
@@ -48,10 +48,6 @@ class PromoteUserCommandTest extends KernelTestCase
|
||||
|
||||
$command = $application->find('kimai:user:promote');
|
||||
self::assertInstanceOf(PromoteUserCommand::class, $command);
|
||||
|
||||
// test alias
|
||||
$command = $application->find('fos:user:promote');
|
||||
self::assertInstanceOf(PromoteUserCommand::class, $command);
|
||||
}
|
||||
|
||||
protected function callCommand(?string $username, ?string $role, bool $super = false)
|
||||
@@ -89,7 +85,7 @@ class PromoteUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('john_user');
|
||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertTrue($user->hasTeamleadRole());
|
||||
}
|
||||
@@ -104,7 +100,7 @@ class PromoteUserCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername('john_user');
|
||||
$user = $userRepository->loadUserByIdentifier('john_user');
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
self::assertTrue($user->isSuperAdmin());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,10 @@ class ReloadCommandTest extends KernelTestCase
|
||||
parent::setUp();
|
||||
$kernel = self::bootKernel();
|
||||
$this->application = new Application($kernel);
|
||||
$this->application->add(new ReloadCommand());
|
||||
$this->application->add(new ReloadCommand(
|
||||
$this->application->getKernel()->getProjectDir(),
|
||||
$this->application->getKernel()->getEnvironment()
|
||||
));
|
||||
}
|
||||
|
||||
public function testCommandName()
|
||||
|
||||
@@ -23,19 +23,16 @@ class ResetDevelopmentCommandTest extends KernelTestCase
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$application = new Application($kernel);
|
||||
$application->add(new ResetDevelopmentCommand());
|
||||
$application->add(new ResetDevelopmentCommand('dev'));
|
||||
|
||||
self::assertTrue($application->has('kimai:reset-dev'));
|
||||
$command = $application->find('kimai:reset-dev');
|
||||
self::assertTrue($application->has('kimai:reset:dev'));
|
||||
$command = $application->find('kimai:reset:dev');
|
||||
self::assertInstanceOf(ResetDevelopmentCommand::class, $command);
|
||||
}
|
||||
|
||||
public function testCommandNameIsNotEnabledInProd()
|
||||
{
|
||||
$kernel = self::bootKernel(['environment' => 'prod']);
|
||||
$application = new Application($kernel);
|
||||
$application->add(new ResetDevelopmentCommand());
|
||||
|
||||
self::assertFalse($application->has('kimai:reset-dev'));
|
||||
$sut = new ResetDevelopmentCommand('prod');
|
||||
self::assertFalse($sut->isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,19 +24,16 @@ class ResetTestCommandTest extends KernelTestCase
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
$application = new Application($kernel);
|
||||
$application->add(new ResetTestCommand($this->createMock(EntityManagerInterface::class)));
|
||||
$application->add(new ResetTestCommand($this->createMock(EntityManagerInterface::class), 'test'));
|
||||
|
||||
self::assertTrue($application->has('kimai:reset-test'));
|
||||
$command = $application->find('kimai:reset-test');
|
||||
self::assertTrue($application->has('kimai:reset:test'));
|
||||
$command = $application->find('kimai:reset:test');
|
||||
self::assertInstanceOf(ResetTestCommand::class, $command);
|
||||
}
|
||||
|
||||
public function testCommandNameIsNotEnabledInProd()
|
||||
{
|
||||
$kernel = self::bootKernel(['environment' => 'prod']);
|
||||
$application = new Application($kernel);
|
||||
$application->add(new ResetTestCommand($this->createMock(EntityManagerInterface::class)));
|
||||
|
||||
self::assertFalse($application->has('kimai:reset-test'));
|
||||
$sut = new ResetTestCommand($this->createMock(EntityManagerInterface::class), 'prod');
|
||||
self::assertFalse($sut->isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ class UpdateCommandTest extends KernelTestCase
|
||||
$container = self::$kernel->getContainer();
|
||||
|
||||
$this->application->add(new UpdateCommand(
|
||||
$container->get('doctrine')->getConnection()
|
||||
$container->get('doctrine')->getConnection(),
|
||||
$this->application->getKernel()->getEnvironment()
|
||||
));
|
||||
|
||||
return $this->application->find('kimai:update');
|
||||
|
||||
@@ -48,13 +48,9 @@ class VersionCommandTest extends KernelTestCase
|
||||
public function getTestData()
|
||||
{
|
||||
return [
|
||||
[[], 'Kimai ' . Constants::VERSION . ' by Kevin Papst and contributors.'],
|
||||
[[], 'Kimai ' . Constants::VERSION . ' by Kevin Papst.'],
|
||||
[['--short' => true], Constants::VERSION],
|
||||
[['--number' => true], Constants::VERSION_ID],
|
||||
// @deprecated since 1.14.1
|
||||
[['--name' => true], Constants::NAME],
|
||||
[['--candidate' => true], Constants::STATUS],
|
||||
[['--semver' => true], Constants::VERSION . '-' . Constants::STATUS],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<?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\Configuration;
|
||||
|
||||
use App\Configuration\CalendarConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\CalendarConfiguration
|
||||
* @group legacy
|
||||
*/
|
||||
class CalendarConfigurationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array $settings
|
||||
* @param array $loaderSettings
|
||||
* @return CalendarConfiguration
|
||||
*/
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new CalendarConfiguration(new SystemConfiguration($loader, ['calendar' => $settings]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'businessHours' => [
|
||||
'days' => [2, 4, 6],
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '09:00',
|
||||
'end' => '21:34',
|
||||
],
|
||||
'day_limit' => 20,
|
||||
'slot_duration' => '01:11:00',
|
||||
'week_numbers' => false,
|
||||
'google' => [
|
||||
'api_key' => 'wertwertwegsdfbdf243w567fg8ihuon',
|
||||
'sources' => [
|
||||
'holidays' => [
|
||||
'id' => 'de.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#ccc',
|
||||
],
|
||||
'holidays_en' => [
|
||||
'id' => 'en.german#holiday@group.v.calendar.google.com',
|
||||
'color' => '#fff',
|
||||
],
|
||||
]
|
||||
],
|
||||
'weekends' => true,
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('calendar', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals([2, 4, 6], $sut->getBusinessDays());
|
||||
$this->assertEquals('07:49', $sut->getBusinessTimeBegin());
|
||||
$this->assertEquals('19:27', $sut->getBusinessTimeEnd());
|
||||
$this->assertEquals('01:11:00', $sut->getSlotDuration());
|
||||
$this->assertEquals(20, $sut->getDayLimit());
|
||||
$this->assertFalse($sut->isShowWeekNumbers());
|
||||
|
||||
$this->assertEquals('wertwertwegsdfbdf243w567fg8ihuon', $sut->getGoogleApiKey());
|
||||
$sources = $sut->getGoogleSources();
|
||||
$this->assertEquals(2, \count($sources));
|
||||
|
||||
self::assertTrue($sut->isShowWeekends());
|
||||
self::assertEquals('09:00', $sut->getTimeframeBegin());
|
||||
self::assertEquals('21:34', $sut->getTimeframeEnd());
|
||||
}
|
||||
|
||||
public function testFindByKey()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertFalse($sut->find('week_numbers'));
|
||||
$this->assertFalse($sut->find('calendar.week_numbers'));
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
<?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\Configuration;
|
||||
|
||||
use App\Configuration\FormConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\FormConfiguration
|
||||
* @group legacy
|
||||
*/
|
||||
class FormConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new FormConfiguration(new SystemConfiguration($loader, ['defaults' => $settings]));
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'customer' => [
|
||||
'timezone' => 'Europe/London',
|
||||
'currency' => 'GBP',
|
||||
'country' => 'FR',
|
||||
],
|
||||
'user' => [
|
||||
'timezone' => 'Europe/London',
|
||||
'currency' => 'GBP',
|
||||
'country' => 'FR',
|
||||
'language' => 'it',
|
||||
'theme' => 'blue',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultLoaderSettings()
|
||||
{
|
||||
return [
|
||||
(new Configuration())->setName('defaults.customer.timezone')->setValue('Russia/Moscov'),
|
||||
(new Configuration())->setName('defaults.customer.currency')->setValue('USD'),
|
||||
(new Configuration())->setName('defaults.customer.country')->setValue('RU'),
|
||||
(new Configuration())->setName('defaults.user.timezone')->setValue('Russia/Moscov'),
|
||||
(new Configuration())->setName('defaults.user.currency')->setValue('USD'),
|
||||
(new Configuration())->setName('defaults.user.language')->setValue('RU'),
|
||||
(new Configuration())->setName('defaults.user.country')->setValue('RU'),
|
||||
(new Configuration())->setName('defaults.user.theme')->setValue('black'),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('defaults', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('GBP', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('FR', $sut->getCustomerDefaultCountry());
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals('Russia/Moscov', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('USD', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('RU', $sut->getCustomerDefaultCountry());
|
||||
$this->assertEquals('USD', $sut->getUserDefaultCurrency());
|
||||
$this->assertEquals('RU', $sut->getUserDefaultLanguage());
|
||||
$this->assertEquals('black', $sut->getUserDefaultTheme());
|
||||
$this->assertEquals('Russia/Moscov', $sut->getUserDefaultTimezone());
|
||||
$this->assertEquals('Russia/Moscov', $sut->find('defaults.user.timezone'));
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('defaults.customer.country')->setValue('RU'),
|
||||
(new Configuration())->setName('defaults.customer.foobar')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone());
|
||||
$this->assertEquals('GBP', $sut->getCustomerDefaultCurrency());
|
||||
$this->assertEquals('RU', $sut->getCustomerDefaultCountry());
|
||||
}
|
||||
|
||||
public function testFindByKey()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('FR', $sut->find('customer.country'));
|
||||
$this->assertEquals('FR', $sut->find('defaults.customer.country'));
|
||||
}
|
||||
|
||||
public function testUnknownConfigAreImported()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('defaults.customer.foobar')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('customer.foobar'));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\LdapConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ class LdapConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings)
|
||||
{
|
||||
$systemConfig = new SystemConfiguration(new TestConfigLoader([]), ['ldap' => $settings]);
|
||||
$systemConfig = SystemConfigurationFactory::create(new TestConfigLoader([]), ['ldap' => $settings]);
|
||||
|
||||
return new LdapConfiguration($systemConfig);
|
||||
}
|
||||
|
||||
@@ -9,81 +9,70 @@
|
||||
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\LanguageFormattings;
|
||||
use App\Configuration\LocaleService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\LanguageFormattings
|
||||
* @covers \App\Configuration\LocaleService
|
||||
*/
|
||||
class LanguageFormattingsTest extends TestCase
|
||||
class LocaleServiceTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings)
|
||||
{
|
||||
return new LanguageFormattings($settings);
|
||||
return new LocaleService($settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'de' => [
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date' => 'd.m.Y',
|
||||
'date_time' => 'd.m. H:i',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
'time' => 'H:i',
|
||||
],
|
||||
'en' => [
|
||||
'date_type' => 'yyyy-MM-dd',
|
||||
'date' => 'Y-m-d',
|
||||
'date_time' => 'm-d H:i',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
'time' => 'H:i:s',
|
||||
],
|
||||
'pt_BR' => [
|
||||
'date_type' => 'dd-MM-yyyy',
|
||||
'date' => 'd-m-Y',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
'it' => [
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
'fr' => [
|
||||
'date_type' => 'dd/MM/yyyy',
|
||||
'date' => 'd/m/Y',
|
||||
'duration' => '%h h %m',
|
||||
],
|
||||
'es' => [
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
'ru' => [
|
||||
'date_type' => 'dd.MM.yyyy',
|
||||
'date' => 'd.m.Y',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
'ar' => [
|
||||
'date_type' => 'yyyy-MM-dd',
|
||||
'date' => 'Y-m-d',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
'hu' => [
|
||||
'date_type' => 'yyyy.MM.dd',
|
||||
'date' => 'Y.m.d.',
|
||||
'duration' => '%h:%m h',
|
||||
'duration' => '%h:%m',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetAvailableLanguages()
|
||||
public function testGetAllLocales()
|
||||
{
|
||||
$sut = $this->getSut([]);
|
||||
$this->assertEquals([], $sut->getAvailableLanguages());
|
||||
$this->assertEquals([], $sut->getAllLocales());
|
||||
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertEquals(['de', 'en', 'pt_BR', 'it', 'fr', 'es', 'ru', 'ar', 'hu'], $sut->getAvailableLanguages());
|
||||
$this->assertEquals(['de', 'en', 'pt_BR', 'it', 'fr', 'es', 'ru', 'ar', 'hu'], $sut->getAllLocales());
|
||||
}
|
||||
|
||||
public function testInvalidLocaleWithGivenLocale()
|
||||
@@ -98,7 +87,7 @@ class LanguageFormattingsTest extends TestCase
|
||||
public function testGetDurationFormat()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertEquals('%h:%m h', $sut->getDurationFormat('de'));
|
||||
$this->assertEquals('%h:%m', $sut->getDurationFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateFormat()
|
||||
@@ -110,19 +99,7 @@ class LanguageFormattingsTest extends TestCase
|
||||
public function testGetDateTimeFormat()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertEquals('d.m. H:i', $sut->getDateTimeFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDateTypeFormat()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertEquals('dd.MM.yyyy', $sut->getDateTypeFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetDatePickerFormat()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertEquals('DD.MM.YYYY', $sut->getDatePickerFormat('de'));
|
||||
$this->assertEquals('d.m.Y H:i', $sut->getDateTimeFormat('de'));
|
||||
}
|
||||
|
||||
public function testGetTimeFormat()
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\SamlConfiguration;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ class SamlConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings)
|
||||
{
|
||||
$systemConfig = new SystemConfiguration(new TestConfigLoader([]), ['saml' => $settings]);
|
||||
$systemConfig = SystemConfigurationFactory::create(new TestConfigLoader([]), ['saml' => $settings]);
|
||||
|
||||
return new SamlConfiguration($systemConfig);
|
||||
}
|
||||
@@ -31,6 +31,7 @@ class SamlConfigurationTest extends TestCase
|
||||
return [
|
||||
'activate' => true,
|
||||
'title' => 'SAML title',
|
||||
'provider' => 'google',
|
||||
'connection' => [
|
||||
'host' => '1.2.3.4',
|
||||
],
|
||||
@@ -40,6 +41,7 @@ class SamlConfigurationTest extends TestCase
|
||||
],
|
||||
'roles' => [
|
||||
'attribute' => 'Roles',
|
||||
'resetOnLogin' => true,
|
||||
'mapping' => [
|
||||
['saml' => 'Kimai - Admin', 'kimai' => 'ROLE_SUPER_ADMIN'],
|
||||
['saml' => 'Management', 'kimai' => 'ROLE_TEAMLEAD'],
|
||||
@@ -57,13 +59,16 @@ class SamlConfigurationTest extends TestCase
|
||||
$this->assertEquals([], $sut->getRolesMapping());
|
||||
$this->assertEquals('', $sut->getRolesAttribute());
|
||||
$this->assertEquals([], $sut->getAttributeMapping());
|
||||
$this->assertFalse($sut->isRolesResetOnLogin());
|
||||
}
|
||||
|
||||
public function testDefaultSettings()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings());
|
||||
$this->assertTrue($sut->isActivated());
|
||||
$this->assertTrue($sut->isRolesResetOnLogin());
|
||||
$this->assertEquals('SAML title', $sut->getTitle());
|
||||
$this->assertEquals('google', $sut->getProvider());
|
||||
$this->assertEquals([
|
||||
'host' => '1.2.3.4',
|
||||
], $sut->getConnection());
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace App\Tests\Configuration;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\SystemConfiguration
|
||||
* @covers \App\Configuration\StringAccessibleConfigTrait
|
||||
*/
|
||||
class SystemConfigurationTest extends TestCase
|
||||
{
|
||||
@@ -28,7 +28,7 @@ class SystemConfigurationTest extends TestCase
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new SystemConfiguration($loader, $settings);
|
||||
return SystemConfigurationFactory::create($loader, $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
@@ -42,11 +42,10 @@ class SystemConfigurationTest extends TestCase
|
||||
'lockdown_period_end' => null,
|
||||
'lockdown_grace_period' => null,
|
||||
],
|
||||
'mode' => 'duration_only',
|
||||
'mode' => 'punch',
|
||||
'markdown_content' => false,
|
||||
'active_entries' => [
|
||||
'hard_limit' => 99,
|
||||
'soft_limit' => 15,
|
||||
],
|
||||
'default_begin' => 'now',
|
||||
'duration_increment' => 10,
|
||||
@@ -67,7 +66,6 @@ class SystemConfigurationTest extends TestCase
|
||||
],
|
||||
'calendar' => [
|
||||
'businessHours' => [
|
||||
'days' => [2, 4, 6],
|
||||
'begin' => '07:49',
|
||||
'end' => '19:27'
|
||||
],
|
||||
@@ -100,13 +98,9 @@ class SystemConfigurationTest extends TestCase
|
||||
'theme' => [
|
||||
'color_choices' => 'Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,#ffffff,,|#000000',
|
||||
'colors_limited' => true,
|
||||
'tags_create' => true,
|
||||
'branding' => [
|
||||
'logo' => null,
|
||||
'mini' => null,
|
||||
'company' => 'Acme Corp.',
|
||||
'title' => 'Fantastic Time-Tracking',
|
||||
'translation' => null,
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -126,16 +120,9 @@ class SystemConfigurationTest extends TestCase
|
||||
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.default_begin')->setValue('07:00'),
|
||||
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
|
||||
(new Configuration())->setName('theme.colors_limited')->setValue(false),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('kimai', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
@@ -143,11 +130,7 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertEquals('GBP', $sut->find('defaults.customer.currency'));
|
||||
$this->assertFalse($sut->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(99, $sut->find('timesheet.active_entries.hard_limit'));
|
||||
$this->assertTrue($sut->find('theme.colors_limited'));
|
||||
$this->assertTrue($sut->isThemeColorsLimited());
|
||||
$this->assertEquals('Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,#ffffff,,|#000000', $sut->getThemeColorChoices());
|
||||
$this->assertEquals('Fantastic Time-Tracking', $sut->getBrandingTitle());
|
||||
$this->assertTrue($sut->isAllowTagCreation());
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
@@ -158,8 +141,6 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertTrue($sut->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertEquals(7, $sut->find('timesheet.active_entries.hard_limit'));
|
||||
$this->assertFalse($sut->isSamlActive());
|
||||
$this->assertFalse($sut->find('theme.colors_limited'));
|
||||
$this->assertEquals('Europe/London', $sut->default('defaults.customer.timezone'));
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
@@ -172,24 +153,45 @@ class SystemConfigurationTest extends TestCase
|
||||
]);
|
||||
$this->assertFalse($sut->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertTrue($sut->isSamlActive());
|
||||
$this->assertEquals('Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,#ffffff,,|#000000', $sut->getThemeColorChoices());
|
||||
$this->assertEquals('Silver|#c0c0c0', $sut->getThemeColorChoices());
|
||||
$this->assertEquals('2020-03-27', $sut->getFinancialYearStart());
|
||||
}
|
||||
|
||||
public function testOffsetUnsetThrowsException()
|
||||
{
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$this->expectExceptionMessage('SystemBundleConfiguration does not support offsetUnset()');
|
||||
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$sut->offsetUnset('dfsdf');
|
||||
}
|
||||
|
||||
public function testUnknownConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.foo')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('timesheet.foo'));
|
||||
$this->assertEquals('hello', $sut->offsetGet('timesheet.foo'));
|
||||
$this->assertTrue($sut->has('timesheet.foo'));
|
||||
$this->assertTrue($sut->offsetExists('timesheet.foo'));
|
||||
$this->assertFalse($sut->has('timesheet.yyyyyyyyy'));
|
||||
$this->assertFalse($sut->offsetExists('timesheet.yyyyyyyyy'));
|
||||
$this->assertFalse($sut->has('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertFalse($sut->offsetExists('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertNull($sut->find('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertNull($sut->offsetGet('xxxxxxxx.yyyyyyyyy'));
|
||||
|
||||
$sut->offsetSet('xxxxxxxx.yyyyyyyyy', 'foooo-bar!');
|
||||
$this->assertTrue($sut->has('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertTrue($sut->offsetExists('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertEquals('foooo-bar!', $sut->find('xxxxxxxx.yyyyyyyyy'));
|
||||
$this->assertEquals('foooo-bar!', $sut->offsetGet('xxxxxxxx.yyyyyyyyy'));
|
||||
}
|
||||
|
||||
public function testCalendarWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals([2, 4, 6], $sut->getCalendarBusinessDays());
|
||||
$this->assertEquals('07:49', $sut->getCalendarBusinessTimeBegin());
|
||||
$this->assertEquals('19:27', $sut->getCalendarBusinessTimeEnd());
|
||||
$this->assertEquals('06:00:00', $sut->getCalendarTimeframeBegin());
|
||||
@@ -243,12 +245,8 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertEquals(99, $sut->getTimesheetActiveEntriesHardLimit());
|
||||
$this->assertFalse($sut->isTimesheetAllowFutureTimes());
|
||||
$this->assertFalse($sut->isTimesheetMarkdownEnabled());
|
||||
$this->assertEquals('duration_only', $sut->getTimesheetTrackingMode());
|
||||
$this->assertEquals('punch', $sut->getTimesheetTrackingMode());
|
||||
$this->assertEquals('now', $sut->getTimesheetDefaultBeginTime());
|
||||
$this->assertFalse($sut->isTimesheetLockdownActive());
|
||||
$this->assertEquals('', $sut->getTimesheetLockdownPeriodStart());
|
||||
$this->assertEquals('', $sut->getTimesheetLockdownPeriodEnd());
|
||||
$this->assertEquals('', $sut->getTimesheetLockdownGracePeriod());
|
||||
$this->assertEquals('', $sut->isTimesheetAllowOverlappingRecords());
|
||||
$this->assertEquals('', $sut->getTimesheetDefaultRoundingDays());
|
||||
$this->assertEquals('', $sut->getTimesheetDefaultRoundingMode());
|
||||
@@ -256,17 +254,7 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertEquals(0, $sut->getTimesheetDefaultRoundingEnd());
|
||||
$this->assertEquals(0, $sut->getTimesheetDefaultRoundingBegin());
|
||||
$this->assertEquals(10, $sut->getTimesheetIncrementDuration());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementBegin());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementEnd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testDeprecatedSettingsWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals(99, $sut->getTimesheetActiveEntriesSoftLimit());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementMinutes());
|
||||
}
|
||||
|
||||
public function testTimesheetWithLoader()
|
||||
@@ -277,10 +265,6 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertTrue($sut->isTimesheetMarkdownEnabled());
|
||||
$this->assertEquals('default', $sut->getTimesheetTrackingMode());
|
||||
$this->assertEquals('07:00', $sut->getTimesheetDefaultBeginTime());
|
||||
$this->assertTrue($sut->isTimesheetLockdownActive());
|
||||
$this->assertEquals('first day of last month', $sut->getTimesheetLockdownPeriodStart());
|
||||
$this->assertEquals('last day of last month', $sut->getTimesheetLockdownPeriodEnd());
|
||||
$this->assertEquals('+5 days', $sut->getTimesheetLockdownGracePeriod());
|
||||
$this->assertEquals('', $sut->isTimesheetAllowOverlappingRecords());
|
||||
$this->assertEquals('', $sut->getTimesheetDefaultRoundingDays());
|
||||
$this->assertEquals('', $sut->getTimesheetDefaultRoundingMode());
|
||||
@@ -288,7 +272,6 @@ class SystemConfigurationTest extends TestCase
|
||||
$this->assertEquals(0, $sut->getTimesheetDefaultRoundingEnd());
|
||||
$this->assertEquals(0, $sut->getTimesheetDefaultRoundingBegin());
|
||||
$this->assertEquals(10, $sut->getTimesheetIncrementDuration());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementBegin());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementEnd());
|
||||
$this->assertEquals(5, $sut->getTimesheetIncrementMinutes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@ use App\Entity\Configuration;
|
||||
*/
|
||||
class TestConfigLoader implements ConfigLoaderInterface
|
||||
{
|
||||
private $configs = [];
|
||||
/**
|
||||
* @var Configuration[]
|
||||
*/
|
||||
private array $configs;
|
||||
|
||||
/**
|
||||
* @param Configuration[] $configs
|
||||
@@ -27,11 +30,19 @@ class TestConfigLoader implements ConfigLoaderInterface
|
||||
$this->configs = $configs;
|
||||
}
|
||||
|
||||
public function getConfiguration(string $name): ?Configuration
|
||||
{
|
||||
if (!\array_key_exists($name, $this->configs)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->configs[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $prefix
|
||||
* @return Configuration[]
|
||||
*/
|
||||
public function getConfiguration(?string $prefix = null): array
|
||||
public function getConfigurations(): array
|
||||
{
|
||||
return $this->configs;
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<?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\Configuration;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Configuration\ThemeConfiguration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\ThemeConfiguration
|
||||
* @covers \App\Configuration\SystemConfiguration
|
||||
*/
|
||||
class ThemeConfigurationTest extends TestCase
|
||||
{
|
||||
protected function getSut(array $settings, array $loaderSettings = []): ThemeConfiguration
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
$config = new SystemConfiguration($loader, ['theme' => $settings]);
|
||||
|
||||
return new ThemeConfiguration($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'active_warning' => 3,
|
||||
'box_color' => 'green',
|
||||
'select_type' => null,
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => 'rgba(0,115,183,0.7)',
|
||||
'border_color' => '#3b8bba',
|
||||
'grid_color' => 'rgba(0,0,0,.05)',
|
||||
'height' => '200'
|
||||
],
|
||||
'branding' => [
|
||||
'logo' => null,
|
||||
'mini' => null,
|
||||
'company' => null,
|
||||
'title' => null,
|
||||
],
|
||||
'tags_create' => true,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testDeprecations()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertTrue($sut->isAllowTagCreation());
|
||||
$this->assertNull($sut->getTitle());
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
<?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\Configuration;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Configuration\TimesheetConfiguration;
|
||||
use App\Entity\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Configuration\TimesheetConfiguration
|
||||
* @group legacy
|
||||
*/
|
||||
class TimesheetConfigurationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param array $settings
|
||||
* @param array $loaderSettings
|
||||
* @return TimesheetConfiguration
|
||||
*/
|
||||
protected function getSut(array $settings, array $loaderSettings = [])
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
$config = new SystemConfiguration($loader, ['timesheet' => $settings]);
|
||||
|
||||
return new TimesheetConfiguration($config);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings()
|
||||
{
|
||||
return [
|
||||
'rules' => [
|
||||
'allow_future_times' => false,
|
||||
'lockdown_period_start' => null,
|
||||
'lockdown_period_end' => null,
|
||||
'lockdown_grace_period' => null,
|
||||
],
|
||||
'mode' => 'duration_only',
|
||||
'markdown_content' => false,
|
||||
'active_entries' => [
|
||||
'hard_limit' => 99,
|
||||
],
|
||||
'default_begin' => 'now',
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultLoaderSettings()
|
||||
{
|
||||
return [
|
||||
(new Configuration())->setName('timesheet.rules.allow_future_times')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.rules.lockdown_period_start')->setValue('first day of last month'),
|
||||
(new Configuration())->setName('timesheet.rules.lockdown_period_end')->setValue('last day of last month'),
|
||||
(new Configuration())->setName('timesheet.rules.lockdown_grace_period')->setValue('+5 days'),
|
||||
(new Configuration())->setName('timesheet.mode')->setValue('default'),
|
||||
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.default_begin')->setValue('07:00'),
|
||||
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
|
||||
];
|
||||
}
|
||||
|
||||
public function testPrefix()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertEquals('timesheet', $sut->getPrefix());
|
||||
}
|
||||
|
||||
public function testDefaultWithoutLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
|
||||
$this->assertEquals(99, $sut->getActiveEntriesHardLimit());
|
||||
$this->assertEquals(99, $sut->getActiveEntriesSoftLimit());
|
||||
$this->assertFalse($sut->isAllowFutureTimes());
|
||||
$this->assertFalse($sut->isMarkdownEnabled());
|
||||
$this->assertEquals('duration_only', $sut->getTrackingMode());
|
||||
$this->assertEquals('now', $sut->getDefaultBeginTime());
|
||||
$this->assertFalse($sut->isLockdownActive());
|
||||
$this->assertEquals('', $sut->getLockdownPeriodStart());
|
||||
$this->assertEquals('', $sut->getLockdownPeriodEnd());
|
||||
$this->assertEquals('', $sut->getLockdownGracePeriod());
|
||||
$this->assertEquals('', $sut->isAllowOverlappingRecords());
|
||||
$this->assertEquals('', $sut->getDefaultRoundingDays());
|
||||
$this->assertEquals('', $sut->getDefaultRoundingMode());
|
||||
$this->assertEquals(0, $sut->getDefaultRoundingBegin());
|
||||
$this->assertEquals(0, $sut->getDefaultRoundingEnd());
|
||||
$this->assertEquals(0, $sut->getDefaultRoundingDuration());
|
||||
}
|
||||
|
||||
public function testDefaultWithLoader()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
$this->assertEquals(7, $sut->getActiveEntriesHardLimit());
|
||||
$this->assertEquals(7, $sut->getActiveEntriesSoftLimit());
|
||||
$this->assertTrue($sut->isAllowFutureTimes());
|
||||
$this->assertTrue($sut->isMarkdownEnabled());
|
||||
$this->assertEquals('default', $sut->getTrackingMode());
|
||||
$this->assertEquals('07:00', $sut->getDefaultBeginTime());
|
||||
$this->assertTrue($sut->isLockdownActive());
|
||||
$this->assertEquals('first day of last month', $sut->getLockdownPeriodStart());
|
||||
$this->assertEquals('last day of last month', $sut->getLockdownPeriodEnd());
|
||||
$this->assertEquals('+5 days', $sut->getLockdownGracePeriod());
|
||||
}
|
||||
|
||||
public function testDefaultWithMixedConfigs()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.mode')->setValue('sdf'),
|
||||
]);
|
||||
$this->assertEquals('sdf', $sut->getTrackingMode());
|
||||
}
|
||||
|
||||
public function testFindByKey()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
$this->assertFalse($sut->find('rules.allow_future_times'));
|
||||
$this->assertFalse($sut->find('timesheet.rules.allow_future_times'));
|
||||
}
|
||||
|
||||
public function testUnknownConfigAreImported()
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), [
|
||||
(new Configuration())->setName('timesheet.foo')->setValue('hello'),
|
||||
]);
|
||||
$this->assertEquals('hello', $sut->find('foo'));
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,16 @@ use App\Entity\User;
|
||||
*/
|
||||
class AboutControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIndexAction()
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/about');
|
||||
|
||||
$result = $client->getCrawler()->filter('ul.nav.nav-stacked li a');
|
||||
$this->assertEquals(4, \count($result));
|
||||
$result = $client->getCrawler()->filter('.content a.card-btn');
|
||||
self::assertCount(4, $result);
|
||||
|
||||
$result = $client->getCrawler()->filter('div.box-body pre');
|
||||
$this->assertEquals(1, \count($result));
|
||||
$this->assertStringContainsString('MIT License', $result->text(null, true));
|
||||
$result = $client->getCrawler()->filter('div.card-body.card_details');
|
||||
self::assertCount(1, $result);
|
||||
self::assertStringContainsString('GNU Affero General Public License v3.0', $result->text(null, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -43,11 +44,8 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/activity/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/activity/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/activity.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -58,12 +56,8 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/activity/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/activity/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/activity'),
|
||||
'help' => 'https://www.kimai.org/documentation/activity.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -159,15 +153,21 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/activity/1/details');
|
||||
|
||||
$this->assertDetailsPage($client);
|
||||
}
|
||||
|
||||
private function assertDetailsPage(HttpKernelBrowser $client)
|
||||
{
|
||||
self::assertHasProgressbar($client);
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#activity_details_box');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_details_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#time_budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#time_budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#activity_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
}
|
||||
|
||||
@@ -178,15 +178,14 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
$form = $client->getCrawler()->filter('form[name=activity_rate_form]')->form();
|
||||
$client->submit($form, [
|
||||
'activity_rate_form' => [
|
||||
'user' => null,
|
||||
'rate' => 123.45,
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#activity_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#activity_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertStringContainsString('123.45', $node->text(null, true));
|
||||
}
|
||||
@@ -202,9 +201,12 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
'project' => '1',
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/'));
|
||||
$client->followRedirect();
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$location = $this->assertIsModalRedirect($client, '/details');
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$this->assertDetailsPage($client);
|
||||
$this->assertHasFlashSuccess($client);
|
||||
|
||||
$activities = $this->getEntityManager()->getRepository(Activity::class)->findAll();
|
||||
$activity = array_pop($activities);
|
||||
@@ -218,7 +220,7 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
public function testCreateActionShowsMetaFields()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
$this->assertAccessIsGranted($client, '/admin/activity/create');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
@@ -296,15 +298,15 @@ class ActivityControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/activity/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body');
|
||||
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text(null, true));
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body');
|
||||
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text());
|
||||
|
||||
$this->request($client, '/admin/activity/1/create_team');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/activity/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-title');
|
||||
self::assertStringContainsString('Only visible to the following teams and all admins.', $node->text(null, true));
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-title');
|
||||
self::assertStringContainsString('Only visible to the following teams and all admins.', $node->text());
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body table tbody tr');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
// creating the default team a second time fails, as the name already exists
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Controller\Auth\SamlController;
|
||||
use App\Saml\SamlAuthFactory;
|
||||
use App\Tests\Configuration\TestConfigLoader;
|
||||
use App\Tests\Mocks\Saml\SamlAuthFactoryFactory;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use OneLogin\Saml2\Auth;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -37,7 +38,7 @@ class SamlControllerTest extends TestCase
|
||||
{
|
||||
$loader = new TestConfigLoader($loaderSettings);
|
||||
|
||||
return new SystemConfiguration($loader, $settings);
|
||||
return SystemConfigurationFactory::create($loader, $settings);
|
||||
}
|
||||
|
||||
protected function getDefaultSettings(bool $activated = true)
|
||||
@@ -70,42 +71,31 @@ class SamlControllerTest extends TestCase
|
||||
$sut->assertionConsumerServiceAction();
|
||||
}
|
||||
|
||||
public function testLogoutAction()
|
||||
{
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('You must configure the logout path in your firewall.');
|
||||
|
||||
$factory = $this->getMockBuilder(SamlAuthFactory::class)->disableOriginalConstructor()->getMock();
|
||||
|
||||
$sut = new SamlController($factory, $this->getSamlConfiguration());
|
||||
$sut->logoutAction();
|
||||
}
|
||||
|
||||
public function testMetadataAction()
|
||||
{
|
||||
$expectedXmlString = <<<EOD
|
||||
<?xml version="1.0"?>
|
||||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2020-07-23T10:26:50Z" cacheDuration="PT604800S" entityID="https://127.0.0.1:8010/auth/saml/metadata">
|
||||
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://127.0.0.1:8010/auth/saml/logout" />
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
||||
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://127.0.0.1:8010/auth/saml/acs" index="1" />
|
||||
</md:SPSSODescriptor>
|
||||
<md:Organization>
|
||||
<md:OrganizationName xml:lang="en">Kimai</md:OrganizationName>
|
||||
<md:OrganizationDisplayName xml:lang="en">Kimai</md:OrganizationDisplayName>
|
||||
<md:OrganizationURL xml:lang="en">https://www.kimai.org</md:OrganizationURL>
|
||||
</md:Organization>
|
||||
<md:ContactPerson contactType="technical">
|
||||
<md:GivenName>Kimai Admin</md:GivenName>
|
||||
<md:EmailAddress>kimai-tech@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
<md:ContactPerson contactType="support">
|
||||
<md:GivenName>Kimai Support</md:GivenName>
|
||||
<md:EmailAddress>kimai-support@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
</md:EntityDescriptor>
|
||||
EOD;
|
||||
<?xml version="1.0"?>
|
||||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2020-07-23T10:26:50Z" cacheDuration="PT604800S" entityID="https://127.0.0.1:8010/auth/saml/metadata">
|
||||
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://127.0.0.1:8010/auth/saml/logout" />
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
||||
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://127.0.0.1:8010/auth/saml/acs" index="1" />
|
||||
</md:SPSSODescriptor>
|
||||
<md:Organization>
|
||||
<md:OrganizationName xml:lang="en">Kimai</md:OrganizationName>
|
||||
<md:OrganizationDisplayName xml:lang="en">Kimai</md:OrganizationDisplayName>
|
||||
<md:OrganizationURL xml:lang="en">https://www.kimai.org</md:OrganizationURL>
|
||||
</md:Organization>
|
||||
<md:ContactPerson contactType="technical">
|
||||
<md:GivenName>Kimai Admin</md:GivenName>
|
||||
<md:EmailAddress>kimai-tech@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
<md:ContactPerson contactType="support">
|
||||
<md:GivenName>Kimai Support</md:GivenName>
|
||||
<md:EmailAddress>kimai-support@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
</md:EntityDescriptor>
|
||||
EOD;
|
||||
|
||||
$oauth = $this->getAuth();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Tests\Configuration\TestConfigLoader;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -34,15 +35,10 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/calendar/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'create modal-ajax-form' => $this->createUrl('/timesheet/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/calendar.html'
|
||||
]);
|
||||
|
||||
$crawler = $client->getCrawler();
|
||||
$calendar = $crawler->filter('div#timesheet_calendar');
|
||||
$this->assertEquals(1, $calendar->count());
|
||||
$dragAndDropBoxes = $crawler->filter('div.box-body.drag-and-drop-source');
|
||||
$dragAndDropBoxes = $crawler->filter('div.card-body.drag-and-drop-source');
|
||||
$this->assertEquals(1, $dragAndDropBoxes->count());
|
||||
}
|
||||
|
||||
@@ -50,21 +46,15 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/calendar/');
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'create modal-ajax-form' => $this->createUrl('/timesheet/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/calendar'),
|
||||
'help' => 'https://www.kimai.org/documentation/calendar.html'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCalendarActionWithGoogleSource()
|
||||
{
|
||||
$loader = new TestConfigLoader([]);
|
||||
$config = new SystemConfiguration($loader, $this->getDefaultSettings());
|
||||
$config = SystemConfigurationFactory::create($loader, $this->getDefaultSettings());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
static::$kernel->getContainer()->set(SystemConfiguration::class, $config);
|
||||
self::getContainer()->set(SystemConfiguration::class, $config);
|
||||
$this->request($client, '/calendar/');
|
||||
$this->assertSuccessResponse($client);
|
||||
|
||||
@@ -83,9 +73,6 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
{
|
||||
return [
|
||||
'theme' => [
|
||||
'active_warning' => 3,
|
||||
'box_color' => 'blue',
|
||||
'select_type' => 'selectpicker',
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => '#3c8dbc',
|
||||
@@ -100,8 +87,6 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
'title' => null,
|
||||
'translation' => null,
|
||||
],
|
||||
'autocomplete_chars' => 3,
|
||||
'tags_create' => true,
|
||||
'calendar' => [
|
||||
'background_color' => '#d2d6de'
|
||||
],
|
||||
|
||||
@@ -12,14 +12,21 @@ namespace App\Tests\Controller;
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\Configuration;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use App\Tests\KernelTestTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||
use Symfony\Component\HttpFoundation\Test\Constraint as ResponseConstraint;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
|
||||
/**
|
||||
* ControllerBaseTest adds some useful functions for writing integration tests.
|
||||
@@ -29,6 +36,8 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
use KernelTestTrait;
|
||||
|
||||
public const DEFAULT_LANGUAGE = 'en';
|
||||
public const DEFAULT_DATE_FORMAT = 'n/j/Y';
|
||||
public const DEFAULT_TIME_FORMAT = 'h:mm a';
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
@@ -36,6 +45,26 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
protected function formatDateRange(\DateTime $begin, \DateTime $end): string
|
||||
{
|
||||
return $begin->format(self::DEFAULT_DATE_FORMAT) . DateRangeType::DATE_SPACER . $end->format(self::DEFAULT_DATE_FORMAT);
|
||||
}
|
||||
|
||||
protected function formatDate(\DateTime $date): string
|
||||
{
|
||||
return $date->format(self::DEFAULT_DATE_FORMAT);
|
||||
}
|
||||
|
||||
protected function formatDateTime(\DateTime $date): string
|
||||
{
|
||||
return $this->formatDate($date) . ' ' . $this->formatTime($date);
|
||||
}
|
||||
|
||||
protected function formatTime(\DateTime $date): string
|
||||
{
|
||||
return $date->format(self::DEFAULT_TIME_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using a special container, to access private services as well.
|
||||
*
|
||||
@@ -45,15 +74,14 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
*/
|
||||
protected function getPrivateService(string $service)
|
||||
{
|
||||
return self::$container->get($service);
|
||||
return self::getContainer()->get($service);
|
||||
}
|
||||
|
||||
protected function loadUserFromDatabase(string $username)
|
||||
{
|
||||
$container = self::$kernel->getContainer();
|
||||
/** @var UserRepository $userRepository */
|
||||
$userRepository = $container->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByUsername($username);
|
||||
$userRepository = self::getContainer()->get('doctrine')->getRepository(User::class);
|
||||
$user = $userRepository->loadUserByIdentifier($username);
|
||||
self::assertInstanceOf(User::class, $user);
|
||||
|
||||
return $user;
|
||||
@@ -61,7 +89,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
|
||||
protected function setSystemConfiguration(string $name, $value): void
|
||||
{
|
||||
$repository = static::$kernel->getContainer()->get(ConfigurationRepository::class);
|
||||
$repository = self::getContainer()->get(ConfigurationRepository::class);
|
||||
|
||||
$entity = $repository->findOneBy(['name' => $name]);
|
||||
if ($entity === null) {
|
||||
@@ -76,7 +104,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
protected function clearConfigCache()
|
||||
{
|
||||
/** @var ConfigurationRepository $repository */
|
||||
$repository = static::$kernel->getContainer()->get(ConfigurationRepository::class);
|
||||
$repository = self::getContainer()->get(ConfigurationRepository::class);
|
||||
$repository->clearCache();
|
||||
}
|
||||
|
||||
@@ -121,7 +149,13 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
|
||||
protected function createUrl(string $url): string
|
||||
{
|
||||
return '/' . self::DEFAULT_LANGUAGE . '/' . ltrim($url, '/');
|
||||
$prefix = '/' . self::DEFAULT_LANGUAGE;
|
||||
|
||||
if (!str_starts_with($url, $prefix)) {
|
||||
$url = $prefix . '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,11 +166,16 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
* @param string $content
|
||||
* @return \Symfony\Component\DomCrawler\Crawler
|
||||
*/
|
||||
protected function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null)
|
||||
public function request(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null)
|
||||
{
|
||||
return $client->request($method, $this->createUrl($url), $parameters, [], [], $content);
|
||||
}
|
||||
|
||||
public function requestPure(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null)
|
||||
{
|
||||
return $client->request($method, $url, $parameters, [], [], $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HttpKernelBrowser $client
|
||||
* @param string $url
|
||||
@@ -168,22 +207,13 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
self::assertThat($response, new ResponseConstraint\ResponseIsSuccessful(), 'Response is not successful, got code: ' . $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertUrlIsSecured(string $url, $method = 'GET')
|
||||
protected function assertUrlIsSecured(string $url, string $method = 'GET'): void
|
||||
{
|
||||
$client = self::createClient();
|
||||
$this->assertRequestIsSecured($client, $url, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET')
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET'): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$client->request($method, $this->createUrl($url));
|
||||
@@ -194,14 +224,14 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
$this->assertAccessDenied($client);
|
||||
}
|
||||
|
||||
protected function assertAccessDenied(HttpKernelBrowser $client)
|
||||
protected function assertAccessDenied(HttpKernelBrowser $client): void
|
||||
{
|
||||
self::assertFalse(
|
||||
$client->getResponse()->isSuccessful(),
|
||||
'Access is not denied for URL: ' . $client->getRequest()->getUri()
|
||||
);
|
||||
self::assertStringContainsString(
|
||||
'Symfony\Component\Security\Core\Exception\AccessDeniedException',
|
||||
'Page is restricted',
|
||||
$client->getResponse()->getContent(),
|
||||
'Could not find AccessDeniedException in response'
|
||||
);
|
||||
@@ -216,12 +246,20 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
protected function assertRouteNotFound(HttpKernelBrowser $client)
|
||||
{
|
||||
self::assertFalse($client->getResponse()->isSuccessful());
|
||||
self::assertEquals(404, $client->getResponse()->getStatusCode());
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
protected function assert404(Response $response, ?string $message = null)
|
||||
{
|
||||
$message = 'Page not found';
|
||||
self::assertFalse($response->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode());
|
||||
self::assertStringContainsString($message, $response->getContent());
|
||||
}
|
||||
|
||||
protected function assertMainContentClass(HttpKernelBrowser $client, string $classname)
|
||||
{
|
||||
self::assertStringContainsString('<section class="content ' . $classname . '">', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<section id="" class="content ' . $classname . '">', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +276,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
protected static function assertHasProgressbar(HttpKernelBrowser $client)
|
||||
{
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertStringContainsString('<div class="progress-bar progress-bar-', $content);
|
||||
self::assertStringContainsString('<div class="progress-bar', $content);
|
||||
self::assertStringContainsString('" role="progressbar" aria-valuenow="', $content);
|
||||
self::assertStringContainsString('" aria-valuemin="0" aria-valuemax="100" style="width: ', $content);
|
||||
}
|
||||
@@ -260,11 +298,11 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
*/
|
||||
protected function assertPageActions(HttpKernelBrowser $client, array $buttons)
|
||||
{
|
||||
$node = $client->getCrawler()->filter('section.content-header div.breadcrumb div.box-tools div.btn-group a');
|
||||
$node = $client->getCrawler()->filter('div.page-header div.page-actions .pa-desktop a');
|
||||
|
||||
/** @var \DOMElement $element */
|
||||
foreach ($node->getIterator() as $element) {
|
||||
$expectedClass = str_replace('btn btn-default btn-', '', $element->getAttribute('class'));
|
||||
$expectedClass = trim(str_replace(['btn action-', ' btn-icon', 'btn btn-primary action-', 'btn btn-dark action-', 'btn btn-white action-', 'btn action-'], '', $element->getAttribute('class')));
|
||||
self::assertArrayHasKey($expectedClass, $buttons);
|
||||
$expectedUrl = $buttons[$expectedClass];
|
||||
self::assertEquals($expectedUrl, $element->getAttribute('href'));
|
||||
@@ -291,7 +329,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
$result = $client->submit($form, $formData);
|
||||
|
||||
$submittedForm = $result->filter($formSelector);
|
||||
$validationErrors = $submittedForm->filter('li.text-danger');
|
||||
$validationErrors = $submittedForm->filter('div.invalid-feedback.d-block');
|
||||
|
||||
self::assertEquals(
|
||||
\count($fieldNames),
|
||||
@@ -307,11 +345,11 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
|
||||
$validation = $list->filter('li.text-danger');
|
||||
if (\count($validation) < 1) {
|
||||
// decorated form fields with icon have a different html structure, see kimai-theme.html.twig
|
||||
// decorated form fields with icon have a different html structure
|
||||
/** @var \DOMElement $listMsg */
|
||||
$listMsg = $field->parents()->getNode(1);
|
||||
$listMsg = $field->getNode(0); //->parents()->getNode(1);
|
||||
$classes = $listMsg->getAttribute('class');
|
||||
self::assertStringContainsString('has-error', $classes, 'Form field has no validation message: ' . $name);
|
||||
self::assertStringContainsString('is-invalid', $classes, 'Form field has no validation message: ' . $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,7 +382,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
*/
|
||||
protected function assertCalloutWidgetWithMessage(HttpKernelBrowser $client, string $message)
|
||||
{
|
||||
$node = $client->getCrawler()->filter('div.callout.callout-warning.lead');
|
||||
$node = $client->getCrawler()->filter('div.alert.alert-warning.alert-important');
|
||||
self::assertStringContainsString($message, $node->text(null, true));
|
||||
}
|
||||
|
||||
@@ -392,7 +430,7 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
* @param HttpKernelBrowser $client
|
||||
* @param string $url
|
||||
*/
|
||||
protected function assertIsRedirect(HttpKernelBrowser $client, $url = null)
|
||||
protected function assertIsRedirect(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true)
|
||||
{
|
||||
self::assertResponseRedirects();
|
||||
|
||||
@@ -400,14 +438,39 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertRedirectUrl($client, $url);
|
||||
$this->assertRedirectUrl($client, $url, $endsWith);
|
||||
}
|
||||
|
||||
protected function assertRedirectUrl(HttpKernelBrowser $client, $url = null, $endsWith = true)
|
||||
protected function assertIsModalRedirect(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true): string
|
||||
{
|
||||
self::assertEquals(201, $client->getResponse()->getStatusCode());
|
||||
self::assertTrue($client->getResponse()->headers->has('x-modal-redirect'), 'Could not find "x-modal-redirect" header');
|
||||
$location = $client->getResponse()->headers->get('x-modal-redirect');
|
||||
|
||||
// check for meta refresh
|
||||
$expectedMeta = sprintf('<meta http-equiv="refresh" content="0;url=\'%1$s\'" />', $location);
|
||||
self::assertStringContainsString($expectedMeta, $client->getResponse()->getContent());
|
||||
|
||||
if ($url !== null) {
|
||||
if ($endsWith) {
|
||||
self::assertStringEndsWith($url, $location, 'Redirect URL does not match');
|
||||
} else {
|
||||
self::assertStringContainsString($url, $location, 'Redirect URL does not match');
|
||||
}
|
||||
}
|
||||
|
||||
return $location;
|
||||
}
|
||||
|
||||
protected function assertRedirectUrl(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true)
|
||||
{
|
||||
self::assertTrue($client->getResponse()->headers->has('Location'), 'Could not find "Location" header');
|
||||
$location = $client->getResponse()->headers->get('Location');
|
||||
|
||||
if ($url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($endsWith) {
|
||||
self::assertStringEndsWith($url, $location, 'Redirect URL does not match');
|
||||
} else {
|
||||
@@ -435,4 +498,13 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
$client->followRedirect();
|
||||
$this->assertHasFlashError($client, 'The action could not be performed: invalid security token.');
|
||||
}
|
||||
|
||||
protected function getCsrfToken(HttpKernelBrowser $client, string $name): CsrfToken
|
||||
{
|
||||
$request = new Request();
|
||||
$request->setSession(new Session(new MockArraySessionStorage()));
|
||||
self::getContainer()->get(RequestStack::class)->push($request);
|
||||
|
||||
return self::getContainer()->get('security.csrf.token_manager')->getToken($name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerComment;
|
||||
use App\Entity\CustomerMeta;
|
||||
@@ -22,6 +21,7 @@ use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -45,10 +45,7 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/customer/export'),
|
||||
'help' => 'https://www.kimai.org/documentation/customer.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -59,12 +56,8 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/customer/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/customer/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/customer'),
|
||||
'help' => 'https://www.kimai.org/documentation/customer.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -85,11 +78,8 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/');
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/customer/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/customer/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/customer.html'
|
||||
]);
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
@@ -140,23 +130,28 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/details');
|
||||
$this->assertDetailsPage($client);
|
||||
}
|
||||
|
||||
private function assertDetailsPage(HttpKernelBrowser $client)
|
||||
{
|
||||
self::assertHasProgressbar($client);
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#customer_details_box');
|
||||
$node = $client->getCrawler()->filter('div.card#customer_details_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#project_list_box');
|
||||
$node = $client->getCrawler()->filter('div.card#project_list_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#time_budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#time_budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box a.btn.btn-default');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-actions a.btn');
|
||||
self::assertEquals(2, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#customer_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#customer_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
}
|
||||
|
||||
@@ -167,15 +162,14 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$form = $client->getCrawler()->filter('form[name=customer_rate_form]')->form();
|
||||
$client->submit($form, [
|
||||
'customer_rate_form' => [
|
||||
'user' => null,
|
||||
'rate' => 123.45,
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#customer_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#customer_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#customer_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
$node = $client->getCrawler()->filter('div.card#customer_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertStringContainsString('123.45', $node->text(null, true));
|
||||
}
|
||||
@@ -183,6 +177,7 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
public function testAddCommentAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/details');
|
||||
$form = $client->getCrawler()->filter('form[name=customer_comment_form]')->form();
|
||||
$client->submit($form, [
|
||||
@@ -192,14 +187,13 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('A beautiful and short comment **with some** markdown formatting', $node->html());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService->offsetSet('timesheet.markdown_content', true);
|
||||
$this->setSystemConfiguration('timesheet.markdown_content', true);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('<p>A beautiful and short comment <strong>with some</strong> markdown formatting</p>', $node->html());
|
||||
}
|
||||
|
||||
@@ -216,20 +210,14 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('customer.delete_comment');
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-msg');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('Blah foo bar', $node->html());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body a.confirmation-link');
|
||||
self::assertStringEndsWith('/comment_delete/' . $token, $node->attr('href'));
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.delete-comment-link');
|
||||
|
||||
$comments = $this->getEntityManager()->getRepository(CustomerComment::class)->findAll();
|
||||
$id = $comments[0]->getId();
|
||||
|
||||
$this->request($client, '/admin/customer/' . $id . '/comment_delete/' . $token);
|
||||
$this->request($client, $node->attr('href'));
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('There were no comments posted yet', $node->html());
|
||||
}
|
||||
|
||||
@@ -266,39 +254,34 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('Blah foo bar', $node->html());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text a.btn.active');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link.active');
|
||||
self::assertEquals(0, $node->count());
|
||||
|
||||
$comments = $this->getEntityManager()->getRepository(CustomerComment::class)->findAll();
|
||||
$id = $comments[0]->getId();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('customer.pin_comment');
|
||||
|
||||
$this->request($client, '/admin/customer/' . $id . '/comment_pin/' . $token);
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link');
|
||||
self::assertEquals(1, $node->count());
|
||||
$this->request($client, $node->attr('href'));
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body a.btn.active');
|
||||
$token2 = self::$container->get('security.csrf.token_manager')->getToken('customer.pin_comment');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link.active');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertEquals($this->createUrl('/admin/customer/' . $id . '/comment_pin/' . $token2), $node->attr('href'));
|
||||
self::assertNotEquals($token, $token2);
|
||||
self::assertStringContainsString('/admin/customer/', $node->attr('href'));
|
||||
self::assertStringContainsString('/comment_pin/', $node->attr('href'));
|
||||
}
|
||||
|
||||
public function testCreateDefaultTeamAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body');
|
||||
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text(null, true));
|
||||
|
||||
$this->request($client, '/admin/customer/1/create_team');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-title');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-title');
|
||||
self::assertStringContainsString('Only visible to the following teams and all admins.', $node->text(null, true));
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body table tbody tr');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
// creating the default team a second time fails, as the name already exists
|
||||
@@ -312,7 +295,7 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/projects/1');
|
||||
$node = $client->getCrawler()->filter('div.box#project_list_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#project_list_box .card-body table tbody tr');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
/** @var EntityManager $em */
|
||||
@@ -326,10 +309,10 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/projects/1');
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#project_list_box .box-tools ul.pagination li');
|
||||
$node = $client->getCrawler()->filter('div.card#project_list_box .card-footer ul.pagination li');
|
||||
self::assertEquals(4, $node->count());
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#project_list_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#project_list_box .card-body table tbody tr');
|
||||
self::assertEquals(5, $node->count());
|
||||
}
|
||||
|
||||
@@ -339,14 +322,7 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/create');
|
||||
$form = $client->getCrawler()->filter('form[name=customer_edit_form]')->form();
|
||||
|
||||
$kernel = self::bootKernel();
|
||||
$container = $kernel->getContainer();
|
||||
$defaults = $container->getParameter('kimai.defaults')['customer'];
|
||||
$this->assertNull($defaults['timezone']);
|
||||
|
||||
$editForm = $client->getCrawler()->filter('form[name=customer_edit_form]')->form();
|
||||
$this->assertEquals($defaults['country'], $editForm->get('customer_edit_form[country]')->getValue());
|
||||
$this->assertEquals($defaults['currency'], $editForm->get('customer_edit_form[currency]')->getValue());
|
||||
$this->assertEquals(date_default_timezone_get(), $editForm->get('customer_edit_form[timezone]')->getValue());
|
||||
|
||||
$client->submit($form, [
|
||||
@@ -354,15 +330,18 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
'name' => 'Test Customer',
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, '/details');
|
||||
$client->followRedirect();
|
||||
|
||||
$location = $this->assertIsModalRedirect($client, '/details');
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$this->assertDetailsPage($client);
|
||||
$this->assertHasFlashSuccess($client);
|
||||
}
|
||||
|
||||
public function testCreateActionShowsMetaFields()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/create');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
@@ -28,26 +25,6 @@ class DashboardControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/dashboard/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertMainContentClass($client, 'dashboard');
|
||||
}
|
||||
|
||||
public function testIndexActionForUserWithTeams()
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
'PHP_AUTH_USER' => 'test_user_1',
|
||||
'PHP_AUTH_PW' => UserFixtures::DEFAULT_PASSWORD,
|
||||
]);
|
||||
$this->request($client, '/dashboard/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertEquals(1, $client->getCrawler()->filter('section.content #WidgetUserTeams')->count());
|
||||
// team 1 has no project assignment right now
|
||||
self::assertEquals(0, $client->getCrawler()->filter('section.content #WidgetUserTeamProjects')->count());
|
||||
}
|
||||
|
||||
public function testIndexActionForAdmin()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/dashboard/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertMainContentClass($client, 'dashboard');
|
||||
self::assertEquals(1, $client->getCrawler()->filter('div#PaginatedWorkingTimeChartBox canvas')->count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ class DoctorControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/doctor');
|
||||
|
||||
$result = $client->getCrawler()->filter('.content .box-header');
|
||||
self::assertCount(6, $result);
|
||||
$result = $client->getCrawler()->filter('.content .card-header');
|
||||
self::assertCount(5, $result);
|
||||
}
|
||||
|
||||
public function testFlushLogWithInvalidCsrf()
|
||||
|
||||
@@ -48,8 +48,7 @@ class ExportControllerTest extends ControllerBaseTest
|
||||
$teamlead = $this->getUserByRole(User::ROLE_TEAMLEAD);
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
/** @var Team $team */
|
||||
$team = new Team();
|
||||
$team->setName('fooo');
|
||||
$team = new Team('fooo');
|
||||
$team->addTeamlead($teamlead);
|
||||
$team->addUser($user);
|
||||
$em->persist($team);
|
||||
@@ -175,9 +174,7 @@ class ExportControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/export/data', 'POST');
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertStringContainsString('Missing export renderer', $response->getContent());
|
||||
$this->assert404($response, 'Missing export renderer');
|
||||
}
|
||||
|
||||
public function testExportActionWithInvalidRenderer()
|
||||
@@ -197,9 +194,7 @@ class ExportControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$this->assertStringContainsString('Unknown export renderer', $response->getContent());
|
||||
$this->assert404($response, 'Unknown export renderer');
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
@@ -238,9 +233,11 @@ class ExportControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(1, $node->count());
|
||||
|
||||
// poor mans assertions ;-)
|
||||
$this->assertStringContainsString('export_print', $node->getIterator()[0]->getAttribute('class'));
|
||||
/** @var \DOMElement $element */
|
||||
$element = $node->getIterator()[0];
|
||||
$this->assertStringContainsString('export_print', $element->getAttribute('class'));
|
||||
$this->assertStringContainsString('<h2 id="doc-title" contenteditable="true"', $content);
|
||||
$this->assertStringContainsString('<h3 id="doc-summary" contenteditable="true" data-original="Summary">Summary</h3>', $content);
|
||||
$this->assertStringContainsString('<h3 class="card-title" id="doc-summary" contenteditable="true" data-original="Summary">Summary</h3>', $content);
|
||||
|
||||
$node = $client->getCrawler()->filter('section.export div#export-records table.dataTable tbody tr');
|
||||
// 20 rows + the summary footer
|
||||
|
||||
44
tests/Controller/FavoriteControllerTest.php
Normal file
44
tests/Controller/FavoriteControllerTest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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\TimesheetFixtures;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class FavoriteControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/favorite/timesheet/');
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$start = new \DateTime('first day of this month');
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(25);
|
||||
$fixture->setAmountRunning(2);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
$fixture->setStartDate($start);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->request($client, '/favorite/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertStringContainsString('<div class="nav-item dropdown d-none d-md-flex me-3 notifications-menu" data-reload="/en/favorite/timesheet/">', $content);
|
||||
self::assertStringContainsString('<div class="card-header">Restart one of your last activities</div>', $content);
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,7 @@ class HomepageControllerTest extends ControllerBaseTest
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$pref = (new UserPreference())
|
||||
->setName('login.initial_view')
|
||||
->setValue('my_profile')
|
||||
$pref = (new UserPreference('login_initial_view', 'my_profile'))
|
||||
->setType(InitialViewType::class);
|
||||
|
||||
$em->persist($pref);
|
||||
|
||||
@@ -13,11 +13,9 @@ use App\Entity\Invoice;
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Tests\DataFixtures\InvoiceTemplateFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -36,7 +34,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->clearInvoiceFiles();
|
||||
}
|
||||
|
||||
private function clearInvoiceFiles()
|
||||
private function clearInvoiceFiles(): void
|
||||
{
|
||||
$path = __DIR__ . '/../_data/invoices/';
|
||||
|
||||
@@ -48,17 +46,17 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testIsSecure()
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/invoice/');
|
||||
}
|
||||
|
||||
public function testIsSecureForRole()
|
||||
public function testIsSecureForRole(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/invoice/');
|
||||
}
|
||||
|
||||
public function testIndexActionRedirectsToCreateTemplate()
|
||||
public function testIndexActionRedirectsToCreateTemplate(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -66,7 +64,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertIsRedirect($client, '/invoice/template/create');
|
||||
}
|
||||
|
||||
public function testIndexActionHasErrorMessageOnEmptyQuery()
|
||||
public function testIndexActionHasErrorMessageOnEmptyQuery(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
@@ -80,7 +78,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertHasNoEntriesWithFilter($client);
|
||||
}
|
||||
|
||||
public function testListTemplateAction()
|
||||
public function testListTemplateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -93,7 +91,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
}
|
||||
|
||||
public function testCreateTemplateAction()
|
||||
public function testCreateTemplateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/invoice/template/create');
|
||||
@@ -125,7 +123,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
self::assertEquals('27.937', $template->getVat());
|
||||
}
|
||||
|
||||
public function testCopyTemplateAction()
|
||||
public function testCopyTemplateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -139,7 +137,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=invoice_template_form]')->form();
|
||||
$values = $form->getPhpValues()['invoice_template_form'];
|
||||
$this->assertEquals('Copy of ' . $template->getName(), $values['name']);
|
||||
$this->assertEquals($template->getName() . ' (1)', $values['name']);
|
||||
$this->assertEquals($template->getTitle(), $values['title']);
|
||||
$this->assertEquals($template->getDueDays(), $values['dueDays']);
|
||||
$this->assertEquals($template->getCalculator(), $values['calculator']);
|
||||
@@ -150,12 +148,13 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals($template->getPaymentTerms(), $values['paymentTerms']);
|
||||
}
|
||||
|
||||
public function testCreateAction()
|
||||
public function testCreateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$fixture = new InvoiceTemplateFixtures();
|
||||
$templates = $this->importFixture($fixture);
|
||||
/** @var InvoiceTemplate $template */
|
||||
$template = $templates[0];
|
||||
|
||||
$begin = new \DateTime('first day of this month');
|
||||
@@ -174,7 +173,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/invoice/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = $begin->format('Y-m-d') . DateRangeType::DATE_SPACER . $end->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange($begin, $end);
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
@@ -192,21 +191,19 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$node = $client->getCrawler()->filter('div.callout.callout-warning.lead');
|
||||
$this->assertEquals(0, $node->count());
|
||||
// but the datatable with all timesheets
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice', 20);
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice_create', 20);
|
||||
|
||||
$urlParams = [
|
||||
'daterange' => $dateRange,
|
||||
'projects[]' => 1,
|
||||
'markAsExported' => 1,
|
||||
'template' => $template->getId(),
|
||||
];
|
||||
|
||||
/** @var CsrfToken $token */
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.create');
|
||||
$token = $client->getCrawler()->filter('div#create-token')->attr('data-value');
|
||||
|
||||
$action = '/invoice/save-invoice/1/' . $template->getId() . '/' . $token->getValue() . '?' . http_build_query($urlParams);
|
||||
$action = '/invoice/save-invoice/1/' . $token . '?' . http_build_query($urlParams);
|
||||
$this->request($client, $action);
|
||||
$this->assertIsRedirect($client);
|
||||
$this->assertRedirectUrl($client, '/invoice/show?id=', false);
|
||||
$this->assertIsRedirect($client, '/invoice/show?id=', false);
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoices', 1);
|
||||
@@ -221,7 +218,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testPreviewAction()
|
||||
public function testPreviewAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
@@ -242,7 +239,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/invoice/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = $begin->format('Y-m-d') . DateRangeType::DATE_SPACER . $end->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange($begin, $end);
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
@@ -256,9 +253,6 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
/** @var CsrfToken $token */
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.preview');
|
||||
|
||||
$params = [
|
||||
'daterange' => $dateRange,
|
||||
'projects' => [1],
|
||||
@@ -266,15 +260,20 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
'customers[]' => 1
|
||||
];
|
||||
|
||||
$action = '/invoice/preview/1/' . $token->getValue() . '?' . http_build_query($params);
|
||||
$token = $client->getCrawler()->filter('div#preview-token')->attr('data-value');
|
||||
$action = '/invoice/preview/1/' . $token . '?' . http_build_query($params);
|
||||
|
||||
$this->request($client, $action);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$node = $client->getCrawler()->filter('body');
|
||||
$this->assertEquals(1, $node->count());
|
||||
$this->assertEquals('invoice_print', $node->getIterator()[0]->getAttribute('class'));
|
||||
|
||||
/** @var \DOMElement $element */
|
||||
$element = $node->getIterator()[0];
|
||||
$this->assertEquals('invoice_print', $element->getAttribute('class'));
|
||||
}
|
||||
|
||||
public function testCreateActionAsAdminWithDownloadAndStatusChange()
|
||||
public function testCreateActionAsAdminWithDownloadAndStatusChange(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -295,11 +294,11 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/invoice/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = $begin->format('Y-m-d') . DateRangeType::DATE_SPACER . $end->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange($begin, $end);
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/?preview='));
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/'));
|
||||
$node->setAttribute('method', 'GET');
|
||||
$client->submit($form, [
|
||||
'template' => $template->getId(),
|
||||
@@ -313,29 +312,28 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$node = $client->getCrawler()->filter('div.callout.callout-warning.lead');
|
||||
$this->assertEquals(0, $node->count());
|
||||
// but the datatable with all timesheets
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice', 20);
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice_create', 20);
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.create');
|
||||
$token = $client->getCrawler()->filter('div#create-token')->attr('data-value');
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/?createInvoice=true&token=' . $token->getValue()));
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/save-invoice/1/' . $token));
|
||||
$node->setAttribute('method', 'GET');
|
||||
$client->submit($form, [
|
||||
'template' => $template->getId(),
|
||||
'daterange' => $dateRange,
|
||||
'customers' => [1],
|
||||
'projects' => [1],
|
||||
'markAsExported' => 1,
|
||||
]);
|
||||
|
||||
$invoices = $this->getEntityManager()->getRepository(Invoice::class)->findAll();
|
||||
$id = $invoices[0]->getId();
|
||||
|
||||
$this->assertIsRedirect($client, '/invoice/show?id=' . $id);
|
||||
$this->assertIsRedirect($client, '/invoice/show?id=', false);
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$invoices = $this->getEntityManager()->getRepository(Invoice::class)->findAll();
|
||||
self::assertCount(1, $invoices);
|
||||
$id = $invoices[0]->getId();
|
||||
|
||||
$this->assertHasFlashSuccess($client);
|
||||
|
||||
$this->assertHasDataTable($client);
|
||||
@@ -348,20 +346,23 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
self::assertInstanceOf(BinaryFileResponse::class, $response);
|
||||
self::assertFileExists($response->getFile());
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.status');
|
||||
$this->request($client, '/invoice/change-status/' . $id . '/pending/' . $token->getValue());
|
||||
$this->request($client, '/invoice/show');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$link = $client->getCrawler()->selectLink('Waiting for payment');
|
||||
|
||||
$this->request($client, $link->attr('href'));
|
||||
$this->assertIsRedirect($client, '/invoice/show');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.status');
|
||||
$this->request($client, '/invoice/change-status/' . $id . '/paid/' . $token->getValue());
|
||||
$link = $client->getCrawler()->selectLink('Invoice paid');
|
||||
$url = $link->attr('href');
|
||||
$this->request($client, $url);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.status');
|
||||
$this->assertHasValidationError(
|
||||
$client,
|
||||
'/invoice/change-status/' . $id . '/paid/' . $token->getValue(),
|
||||
$url,
|
||||
'form[name=invoice_edit_form]',
|
||||
[
|
||||
'invoice_edit_form' => [
|
||||
@@ -376,7 +377,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$form = $client->getCrawler()->filter('form[name=invoice_edit_form]')->form();
|
||||
$client->submit($form, [
|
||||
'invoice_edit_form' => [
|
||||
'paymentDate' => (new \DateTime())->format('Y-m-d')
|
||||
'paymentDate' => (new \DateTime())->format(self::DEFAULT_DATE_FORMAT)
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -384,14 +385,14 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.status');
|
||||
$token = $this->getCsrfToken($client, 'invoice.status');
|
||||
$this->request($client, '/invoice/change-status/' . $id . '/new/' . $token->getValue());
|
||||
$this->assertIsRedirect($client, '/invoice/show');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
public function testEditTemplateAction()
|
||||
public function testEditTemplateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -418,7 +419,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertHasFlashSuccess($client);
|
||||
}
|
||||
|
||||
public function testDeleteTemplateAction()
|
||||
public function testDeleteTemplateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -426,9 +427,11 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$template = $this->importFixture($fixture);
|
||||
$id = $template[0]->getId();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('invoice.delete_template');
|
||||
$this->request($client, '/invoice/template');
|
||||
$url = $this->createUrl('/invoice/template/' . $id . '/delete/');
|
||||
$links = $client->getCrawler()->filterXPath("//a[starts-with(@href, '" . $url . "')]");
|
||||
|
||||
$this->request($client, '/invoice/template/' . $id . '/delete/' . $token);
|
||||
$this->requestPure($client, $links->attr('href'));
|
||||
$this->assertIsRedirect($client, '/invoice/template');
|
||||
$client->followRedirect();
|
||||
|
||||
@@ -438,7 +441,7 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(0, $this->getEntityManager()->getRepository(InvoiceTemplate::class)->count([]));
|
||||
}
|
||||
|
||||
public function testUploadDocumentAction()
|
||||
public function testUploadDocumentAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -453,12 +456,12 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
// we do not test the upload here, just make sure that the action can be rendered properly
|
||||
}
|
||||
|
||||
public function testExportIsSecureForRole()
|
||||
public function testExportIsSecureForRole(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/invoice/export');
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
public function testExportAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->assertAccessIsGranted($client, '/invoice/export');
|
||||
|
||||
@@ -21,56 +21,35 @@ class LayoutControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->request($client, '/dashboard/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->assertHasMainHeader($client, $user);
|
||||
$this->assertHasSidebar($client, $user);
|
||||
$this->assertHasNavigation($client);
|
||||
}
|
||||
|
||||
protected function assertHasMainHeader(HttpKernelBrowser $client, User $user)
|
||||
{
|
||||
// TODO improve me
|
||||
// main-header > a.logo
|
||||
// # href = homepage
|
||||
// && > span.logo-mini
|
||||
// && > span.logo-lg
|
||||
// && > nav.navbar.navbar-static-top
|
||||
// && div.navbar-custom-menu
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
|
||||
$this->assertStringContainsString('<li class="dropdown user-menu">', $content);
|
||||
$this->assertStringContainsString('<a href="/en/profile/' . $user->getUsername() . '">', $content);
|
||||
$this->assertStringContainsString('<a href="/en/profile/' . $user->getUsername() . '/prefs">', $content);
|
||||
$this->assertStringContainsString('<a href="/en/logout">', $content);
|
||||
$this->assertStringContainsString('data-bs-toggle="dropdown" aria-label="Open user menu"', $content);
|
||||
$this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '"', $content);
|
||||
$this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '/edit"', $content);
|
||||
$this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '/prefs"', $content);
|
||||
$this->assertStringContainsString('href="/en/logout"', $content);
|
||||
}
|
||||
|
||||
protected function assertHasSidebar(HttpKernelBrowser $client, User $user)
|
||||
protected function assertHasNavigation(HttpKernelBrowser $client)
|
||||
{
|
||||
// TODO improve me
|
||||
// aside.main-sidebar
|
||||
// && section.sidebar
|
||||
// && ul.sidebar-menu tree
|
||||
// && li#dashboard > a href=dashboard
|
||||
// && li#... with links
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
|
||||
$this->assertStringContainsString('<li id="dashboard"', $content);
|
||||
$this->assertStringContainsString('<a href="/en/dashboard/">', $content);
|
||||
$this->assertStringContainsString('<span>Dashboard</span>', $content);
|
||||
|
||||
$this->assertStringContainsString('<li id="timesheet"', $content);
|
||||
$this->assertStringContainsString('<a href="/en/timesheet/">', $content);
|
||||
$this->assertStringContainsString('<span>My times</span>', $content);
|
||||
|
||||
$this->assertStringContainsString('<li id="calendar"', $content);
|
||||
$this->assertStringContainsString('<a href="/en/calendar/">', $content);
|
||||
$this->assertStringContainsString('<span>Calendar</span>', $content);
|
||||
$this->assertStringContainsString('href="/en/dashboard/"', $content);
|
||||
$this->assertStringContainsString('href="/en/timesheet/"', $content);
|
||||
$this->assertStringContainsString('My times', $content);
|
||||
$this->assertStringContainsString('href="/en/calendar/"', $content);
|
||||
$this->assertStringContainsString('Calendar', $content);
|
||||
}
|
||||
|
||||
public function testActiveEntries()
|
||||
@@ -82,12 +61,6 @@ class LayoutControllerTest extends ControllerBaseTest
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
|
||||
self::assertStringContainsString('<li class="messages-menu', $content);
|
||||
self::assertStringContainsString('<div class="ddt-small ticktac-single ', $content);
|
||||
self::assertStringContainsString('data-api="', $content);
|
||||
self::assertStringContainsString('data-href="', $content);
|
||||
self::assertStringContainsString('data-icon=', $content);
|
||||
self::assertStringContainsString('<ul class="menu">', $content);
|
||||
self::assertStringContainsString('<li class="messages-menu-empty" style="">', $content);
|
||||
$this->assertStringContainsString('<a href="/en/timesheet/create" class="modal-ajax-form ticktac-start btn', $content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\Role;
|
||||
use App\Entity\RolePermission;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -35,19 +34,22 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/permissions');
|
||||
$this->assertHasDataTable($client);
|
||||
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 132);
|
||||
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 129);
|
||||
$this->assertPageActions($client, [
|
||||
//'back' => $this->createUrl('/admin/user/'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/permissions/roles/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/permissions.html'
|
||||
]);
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
$this->assertTableHeader($content);
|
||||
}
|
||||
|
||||
private function assertTableHeader(string $content): void
|
||||
{
|
||||
// the english translation instead of the real system user role names
|
||||
self::assertStringContainsString('<th data-field="User" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="Teamlead" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="Administrator" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="System-Admin" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="ROLE_USER" class="alwaysVisible text-center bg-green-lt col_ROLE_USER">', $content);
|
||||
self::assertStringContainsString('<th data-field="ROLE_TEAMLEAD" class="alwaysVisible text-center col_ROLE_TEAMLEAD">', $content);
|
||||
self::assertStringContainsString('<th data-field="ROLE_ADMIN" class="alwaysVisible text-center col_ROLE_ADMIN">', $content);
|
||||
self::assertStringContainsString('<th data-field="ROLE_SUPER_ADMIN" class="alwaysVisible text-center bg-orange-lt col_ROLE_SUPER_ADMIN">', $content);
|
||||
}
|
||||
|
||||
public function testCreateRoleIsSecured()
|
||||
@@ -74,12 +76,7 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
$client->followRedirect();
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
// the english translation instead of the real system user role names
|
||||
self::assertStringContainsString('<th data-field="User" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="Teamlead" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="Administrator" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="System-Admin" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="TEST_ROLE" class="alwaysVisible text-center">', $content);
|
||||
$this->assertTableHeader($content);
|
||||
}
|
||||
|
||||
public function testDeleteRoleIsSecured()
|
||||
@@ -115,7 +112,7 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
}
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertStringContainsString('<th data-field="TEST_ROLE" class="alwaysVisible text-center">', $content);
|
||||
self::assertStringContainsString('<th data-field="TEST_ROLE" class="alwaysVisible text-center col_TEST_ROLE">', $content);
|
||||
|
||||
// add user to role
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/roles');
|
||||
@@ -134,9 +131,11 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
$user = $this->getUserByName(UserFixtures::USERNAME_USER);
|
||||
$this->assertEquals(['ROLE_TEAMLEAD', 'ROLE_SUPER_ADMIN', 'TEST_ROLE', 'ROLE_USER'], $user->getRoles());
|
||||
|
||||
/** @var CsrfToken $token */
|
||||
$token = static::$kernel->getContainer()->get('security.csrf.token_manager')->getToken('user_role_permissions');
|
||||
$this->request($client, '/admin/permissions/roles/' . $id . '/delete/' . $token->getValue());
|
||||
$this->request($client, '/admin/permissions');
|
||||
$node = $client->getCrawler()->filter('table.dataTable thead th a.confirmation-link');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
$this->request($client, $node->attr('href'));
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/permissions'));
|
||||
$client->followRedirect();
|
||||
|
||||
@@ -185,17 +184,17 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
// create the permission
|
||||
$token = static::$kernel->getContainer()->get('security.csrf.token_manager')->getToken('user_role_permissions');
|
||||
$this->request($client, '/admin/permissions/roles/' . $id . '/view_user/1/' . $token->getValue(), 'POST');
|
||||
$token = $client->getCrawler()->filter('div#permission-token')->attr('data-value');
|
||||
|
||||
// create the permission
|
||||
$this->request($client, '/admin/permissions/roles/' . $id . '/view_user/1/' . $token, 'POST');
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertArrayHasKey('token', $result);
|
||||
|
||||
$rolePermissions = $em->getRepository(RolePermission::class)->findAll();
|
||||
$this->assertEquals(1, \count($rolePermissions));
|
||||
$this->assertCount(1, $rolePermissions);
|
||||
$permission = $rolePermissions[0];
|
||||
self::assertInstanceOf(RolePermission::class, $permission);
|
||||
self::assertEquals('view_user', $permission->getPermission());
|
||||
@@ -207,8 +206,7 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
$em->clear();
|
||||
|
||||
// update the permission
|
||||
$token = static::$kernel->getContainer()->get('security.csrf.token_manager')->getToken('user_role_permissions');
|
||||
$this->request($client, '/admin/permissions/roles/' . $id . '/view_user/0/' . $token->getValue(), 'POST');
|
||||
$this->request($client, '/admin/permissions/roles/' . $id . '/view_user/0/' . $result['token'], 'POST');
|
||||
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -33,7 +33,6 @@ class PluginControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/plugins/');
|
||||
$this->assertCalloutWidgetWithMessage($client, 'You have no plugins installed yet');
|
||||
$this->assertPageActions($client, ['shop' => 'https://www.kimai.org/store/', 'help' => 'https://www.kimai.org/documentation/plugins.html']);
|
||||
}
|
||||
|
||||
public function testIndexActionWithInstalledPlugins()
|
||||
@@ -41,8 +40,7 @@ class PluginControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
/** @var PluginManager $manager */
|
||||
$manager = self::$container->get(PluginManager::class);
|
||||
$manager->addPlugin(new TestPlugin());
|
||||
$manager = self::getContainer()->set(PluginManager::class, new PluginManager([new TestPlugin()]));
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/plugins/');
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
@@ -16,19 +16,26 @@ use App\Tests\DataFixtures\TeamFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class ProfileControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER);
|
||||
}
|
||||
|
||||
public function testIndexActionWithoutData()
|
||||
public function testMyProfileActionRedirects(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/profile/');
|
||||
$this->assertIsRedirect($client, '/en/profile/' . UserFixtures::USERNAME_USER);
|
||||
}
|
||||
|
||||
public function testIndexActionWithoutData(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER);
|
||||
@@ -39,12 +46,12 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
$year = (new \DateTime())->format('Y');
|
||||
$this->assertStringContainsString('<h3 class="box-title">' . $year . '</h3>', $content);
|
||||
$this->assertStringContainsString('<h3 class="card-title">' . $year, $content);
|
||||
$this->assertStringContainsString('new Chart(', $content);
|
||||
$this->assertStringContainsString('<canvas id="userProfileChart' . $year . '"', $content);
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
@@ -67,7 +74,7 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
foreach ($dates as $start) {
|
||||
$year = $start->format('Y');
|
||||
$this->assertStringContainsString('<h3 class="box-title">' . $year . '</h3>', $content);
|
||||
$this->assertStringContainsString('<h3 class="card-title">' . $year, $content);
|
||||
$this->assertStringContainsString('<canvas id="userProfileChart' . $year . '"', $content);
|
||||
}
|
||||
|
||||
@@ -75,25 +82,22 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
$this->assertHasAboutMeBox($client, UserFixtures::USERNAME_USER);
|
||||
}
|
||||
|
||||
protected function assertHasProfileBox(HttpKernelBrowser $client, string $username)
|
||||
protected function assertHasProfileBox(HttpKernelBrowser $client, string $username): void
|
||||
{
|
||||
$profileBox = $client->getCrawler()->filter('div.box-user-profile');
|
||||
$this->assertEquals(1, $profileBox->count());
|
||||
$profileAvatar = $profileBox->filter('span.avatar');
|
||||
$this->assertEquals(1, $profileAvatar->count());
|
||||
$alt = $profileAvatar->attr('title');
|
||||
|
||||
$this->assertEquals($username, $alt);
|
||||
}
|
||||
|
||||
protected function assertHasAboutMeBox(HttpKernelBrowser $client, string $username)
|
||||
protected function assertHasAboutMeBox(HttpKernelBrowser $client, string $username): void
|
||||
{
|
||||
$content = $client->getResponse()->getContent();
|
||||
|
||||
$this->assertStringContainsString('About me', $content);
|
||||
$this->assertStringContainsString('<div class="datagrid-content">' . $username . '</div>', $content);
|
||||
}
|
||||
|
||||
public function getTabTestData()
|
||||
public function getTabTestData(): array
|
||||
{
|
||||
return [
|
||||
[User::ROLE_USER, UserFixtures::USERNAME_USER],
|
||||
@@ -104,21 +108,21 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getTabTestData
|
||||
*/
|
||||
public function testEditActionTabs($role, $username)
|
||||
public function testEditActionTabs($role, $username): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->request($client, '/profile/' . $username . '/edit');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
public function testIndexActionWithDifferentUsername()
|
||||
public function testIndexActionWithDifferentUsername(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_TEAMLEAD);
|
||||
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
public function testEditAction()
|
||||
public function testEditAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/edit');
|
||||
@@ -126,7 +130,7 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
/** @var User $user */
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUserIdentifier());
|
||||
$this->assertEquals('John Doe', $user->getAlias());
|
||||
$this->assertEquals('Developer', $user->getTitle());
|
||||
$this->assertEquals('john_user@example.com', $user->getEmail());
|
||||
@@ -149,14 +153,14 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUserIdentifier());
|
||||
$this->assertEquals('Johnny', $user->getAlias());
|
||||
$this->assertEquals('Code Monkey', $user->getTitle());
|
||||
$this->assertEquals('updated@example.com', $user->getEmail());
|
||||
$this->assertTrue($user->isEnabled());
|
||||
}
|
||||
|
||||
public function testEditActionWithActiveFlag()
|
||||
public function testEditActionWithActiveFlag(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/edit');
|
||||
@@ -179,14 +183,14 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUserIdentifier());
|
||||
$this->assertEquals('Johnny', $user->getAlias());
|
||||
$this->assertEquals('Code Monkey', $user->getTitle());
|
||||
$this->assertEquals('updated@example.com', $user->getEmail());
|
||||
$this->assertFalse($user->isEnabled());
|
||||
}
|
||||
|
||||
public function testPasswordAction()
|
||||
public function testPasswordAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/password');
|
||||
@@ -194,12 +198,12 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
/** @var User $user */
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
/** @var EncoderFactoryInterface $passwordEncoder */
|
||||
$passwordEncoder = static::$kernel->getContainer()->get('test.PasswordEncoder');
|
||||
/** @var PasswordHasherFactoryInterface $passwordEncoder */
|
||||
$passwordEncoder = self::getContainer()->get('security.password_hasher_factory');
|
||||
|
||||
$this->assertTrue($passwordEncoder->getEncoder($user)->isPasswordValid($user->getPassword(), UserFixtures::DEFAULT_PASSWORD, $user->getSalt()));
|
||||
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getPassword(), 'test123', $user->getSalt()));
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
|
||||
$this->assertTrue($passwordEncoder->getPasswordHasher($user)->verify($user->getPassword(), UserFixtures::DEFAULT_PASSWORD));
|
||||
$this->assertFalse($passwordEncoder->getPasswordHasher($user)->verify($user->getPassword(), 'test123'));
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUserIdentifier());
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=user_password]')->form();
|
||||
$client->submit($form, [
|
||||
@@ -212,18 +216,24 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, $this->createUrl('/profile/' . urlencode(UserFixtures::USERNAME_USER) . '/password'));
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
// cannot follow redirect here, because the password was changed and the user/password registered in the client
|
||||
// are the old ones, so following the redirect would fail with "Unauthorized".
|
||||
|
||||
$this->assertHasFlashSuccess($client);
|
||||
$this->tearDown();
|
||||
$client = self::createClient([], [
|
||||
'PHP_AUTH_USER' => UserFixtures::USERNAME_USER,
|
||||
'PHP_AUTH_PW' => 'test1234',
|
||||
]);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/password');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getPassword(), UserFixtures::DEFAULT_PASSWORD, $user->getSalt()));
|
||||
$this->assertTrue($passwordEncoder->getEncoder($user)->isPasswordValid($user->getPassword(), 'test1234', $user->getSalt()));
|
||||
$this->assertFalse($passwordEncoder->getPasswordHasher($user)->verify($user->getPassword(), UserFixtures::DEFAULT_PASSWORD));
|
||||
$this->assertTrue($passwordEncoder->getPasswordHasher($user)->verify($user->getPassword(), 'test1234'));
|
||||
}
|
||||
|
||||
public function testPasswordActionFailsIfPasswordLengthToShort()
|
||||
public function testPasswordActionFailsIfPasswordLengthToShort(): void
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_USER,
|
||||
@@ -241,19 +251,19 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testApiTokenAction()
|
||||
public function testApiTokenAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/api-token');
|
||||
|
||||
/** @var User $user */
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
/** @var EncoderFactoryInterface $passwordEncoder */
|
||||
$passwordEncoder = static::$kernel->getContainer()->get('test.PasswordEncoder');
|
||||
/** @var PasswordHasherFactoryInterface $passwordEncoder */
|
||||
$passwordEncoder = self::getContainer()->get('security.password_hasher_factory');
|
||||
|
||||
$this->assertTrue($passwordEncoder->getEncoder($user)->isPasswordValid($user->getApiToken(), UserFixtures::DEFAULT_API_TOKEN, $user->getSalt()));
|
||||
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getApiToken(), 'test1234', $user->getSalt()));
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUsername());
|
||||
$this->assertTrue($passwordEncoder->getPasswordHasher($user)->verify($user->getApiToken(), UserFixtures::DEFAULT_API_TOKEN));
|
||||
$this->assertFalse($passwordEncoder->getPasswordHasher($user)->verify($user->getApiToken(), 'test1234'));
|
||||
$this->assertEquals(UserFixtures::USERNAME_USER, $user->getUserIdentifier());
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=user_api_token]')->form();
|
||||
$client->submit($form, [
|
||||
@@ -273,11 +283,11 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
|
||||
$this->assertFalse($passwordEncoder->getEncoder($user)->isPasswordValid($user->getApiToken(), UserFixtures::DEFAULT_API_TOKEN, $user->getSalt()));
|
||||
$this->assertTrue($passwordEncoder->getEncoder($user)->isPasswordValid($user->getApiToken(), 'test1234', $user->getSalt()));
|
||||
$this->assertFalse($passwordEncoder->getPasswordHasher($user)->verify($user->getApiToken(), UserFixtures::DEFAULT_API_TOKEN));
|
||||
$this->assertTrue($passwordEncoder->getPasswordHasher($user)->verify($user->getApiToken(), 'test1234'));
|
||||
}
|
||||
|
||||
public function testApiTokenActionFailsIfPasswordLengthToShort()
|
||||
public function testApiTokenActionFailsIfPasswordLengthToShort(): void
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_USER,
|
||||
@@ -295,14 +305,14 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testRolesActionIsSecured()
|
||||
public function testRolesActionIsSecured(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/roles');
|
||||
$this->assertFalse($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
public function testRolesAction()
|
||||
public function testRolesAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/roles');
|
||||
@@ -331,17 +341,17 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(['ROLE_TEAMLEAD', 'ROLE_SUPER_ADMIN', 'ROLE_USER'], $user->getRoles());
|
||||
}
|
||||
|
||||
public function testTeamsActionIsSecured()
|
||||
public function testTeamsActionIsSecured(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/teams');
|
||||
}
|
||||
|
||||
public function testTeamsActionIsSecuredForRole()
|
||||
public function testTeamsActionIsSecuredForRole(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/profile/' . UserFixtures::USERNAME_USER . '/teams');
|
||||
}
|
||||
|
||||
public function testTeamsAction()
|
||||
public function testTeamsAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -379,26 +389,26 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
$this->assertCount(1, $user->getTeams());
|
||||
}
|
||||
|
||||
public function getPreferencesTestData()
|
||||
public function getPreferencesTestData(): array
|
||||
{
|
||||
return [
|
||||
// assert that the user doesn't have the "hourly-rate_own_profile" permission
|
||||
[User::ROLE_USER, UserFixtures::USERNAME_USER, 82, 82, 'ar', null],
|
||||
[User::ROLE_USER, UserFixtures::USERNAME_USER, 82, 82, 'ar', null, false],
|
||||
// teamleads are allowed to update their own hourly rate, but not other peoples hourly rate
|
||||
[User::ROLE_TEAMLEAD, UserFixtures::USERNAME_TEAMLEAD, 35, 37.5, 'ar', 19.54],
|
||||
[User::ROLE_TEAMLEAD, UserFixtures::USERNAME_TEAMLEAD, 35, 37.5, 'ar', 19.54, true],
|
||||
// admins are allowed to update their own hourly rate, but not other peoples hourly rate
|
||||
[User::ROLE_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'ar', 19.54],
|
||||
[User::ROLE_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'ar', 19.54, true],
|
||||
// super-admins are allowed to update other peoples hourly rate
|
||||
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'en', 19.54],
|
||||
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_ADMIN, 81, 37.5, 'en', 19.54, true],
|
||||
// super-admins are allowed to update their own hourly rate
|
||||
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_SUPER_ADMIN, 46, 37.5, 'ar', 19.54],
|
||||
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_SUPER_ADMIN, 46, 37.5, 'ar', 19.54, true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getPreferencesTestData
|
||||
*/
|
||||
public function testPreferencesAction($role, $username, $hourlyRateOriginal, $hourlyRate, $expectedLocale, $expectedInternalRate)
|
||||
public function testPreferencesAction($role, $username, $hourlyRateOriginal, $hourlyRate, string $expectedLocale, float|null $expectedInternalRate, bool $withRateSettings): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->request($client, '/profile/' . $username . '/prefs');
|
||||
@@ -408,19 +418,24 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
|
||||
$this->assertEquals($hourlyRateOriginal, $user->getPreferenceValue(UserPreference::HOURLY_RATE));
|
||||
$this->assertNull($user->getPreferenceValue(UserPreference::INTERNAL_RATE));
|
||||
$this->assertNull($user->getPreferenceValue(UserPreference::SKIN));
|
||||
$this->assertEquals('default', $user->getPreferenceValue(UserPreference::SKIN));
|
||||
|
||||
$data = [
|
||||
UserPreference::TIMEZONE => ['value' => 'America/Creston'],
|
||||
UserPreference::LOCALE => ['value' => 'ar'],
|
||||
UserPreference::FIRST_WEEKDAY => ['value' => 'sunday'],
|
||||
UserPreference::SKIN => ['value' => 'dark'],
|
||||
];
|
||||
|
||||
if ($withRateSettings) {
|
||||
$data[UserPreference::HOURLY_RATE] = ['value' => 37.5];
|
||||
$data[UserPreference::INTERNAL_RATE] = ['value' => 19.54];
|
||||
}
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=user_preferences_form]')->form();
|
||||
$client->submit($form, [
|
||||
'user_preferences_form' => [
|
||||
'preferences' => [
|
||||
0 => ['name' => UserPreference::HOURLY_RATE, 'value' => 37.5],
|
||||
1 => ['name' => UserPreference::INTERNAL_RATE, 'value' => 19.54],
|
||||
2 => ['name' => UserPreference::TIMEZONE, 'value' => 'America/Creston'],
|
||||
3 => ['name' => UserPreference::LOCALE, 'value' => 'ar'],
|
||||
4 => ['name' => UserPreference::FIRST_WEEKDAY, 'value' => 'sunday'],
|
||||
6 => ['name' => UserPreference::SKIN, 'value' => 'blue'],
|
||||
]
|
||||
'preferences' => $data
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -441,8 +456,98 @@ class ProfileControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('ar', $user->getPreferenceValue(UserPreference::LOCALE));
|
||||
$this->assertEquals('ar', $user->getLanguage());
|
||||
$this->assertEquals('ar', $user->getLocale());
|
||||
$this->assertEquals('blue', $user->getPreferenceValue(UserPreference::SKIN));
|
||||
$this->assertEquals('dark', $user->getPreferenceValue(UserPreference::SKIN));
|
||||
$this->assertEquals('sunday', $user->getPreferenceValue(UserPreference::FIRST_WEEKDAY));
|
||||
$this->assertEquals('sunday', $user->getFirstDayOfWeek());
|
||||
}
|
||||
|
||||
public function testIsTwoFactorSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/2fa');
|
||||
}
|
||||
|
||||
public function testTwoFactor(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$user = $this->getUserByName(UserFixtures::USERNAME_USER);
|
||||
self::assertFalse($user->hasTotpSecret());
|
||||
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/2fa');
|
||||
|
||||
$user = $this->getUserByName(UserFixtures::USERNAME_USER);
|
||||
self::assertTrue($user->hasTotpSecret());
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertNotFalse($content);
|
||||
|
||||
$imgUrl = $this->createUrl('/profile/' . UserFixtures::USERNAME_USER . '/totp.png');
|
||||
$this->assertStringContainsString('<img src="' . $imgUrl . '" alt="TOTP QR Code" style="max-width: 200px; max-height: 200px;" />', $content);
|
||||
|
||||
$formUrl = $this->createUrl('/profile/' . UserFixtures::USERNAME_USER . '/2fa');
|
||||
$this->assertStringContainsString('<form name="user_two_factor" method="post" action="' . $formUrl . '" id="user_two_factor_form">', $content);
|
||||
}
|
||||
|
||||
public function testActivateTwoFactorWithEmptyToken(): void
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_USER,
|
||||
'/profile/' . UserFixtures::USERNAME_USER . '/2fa',
|
||||
'form[name=user_two_factor]',
|
||||
[
|
||||
'user_two_factor' => [
|
||||
'code' => ''
|
||||
]
|
||||
],
|
||||
['#user_two_factor_code']
|
||||
);
|
||||
}
|
||||
|
||||
public function testActivateTwoFactorWithWrongToken(): void
|
||||
{
|
||||
$this->assertFormHasValidationError(
|
||||
User::ROLE_USER,
|
||||
'/profile/' . UserFixtures::USERNAME_USER . '/2fa',
|
||||
'form[name=user_two_factor]',
|
||||
[
|
||||
'user_two_factor' => [
|
||||
'code' => '1234567890oikjhb'
|
||||
]
|
||||
],
|
||||
['#user_two_factor_code']
|
||||
);
|
||||
}
|
||||
|
||||
public function testIsTwoFactorDeactivateSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/2fa_deactivate', 'POST');
|
||||
}
|
||||
|
||||
public function testIsTwoFactorImageSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/totp.png');
|
||||
}
|
||||
|
||||
public function testTwoFactorImageFailsOnMissingSecret(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/totp.png');
|
||||
$this->assertRouteNotFound($client);
|
||||
}
|
||||
|
||||
public function testTwoFactorImage(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$user = $this->getUserByName(UserFixtures::USERNAME_USER);
|
||||
self::assertFalse($user->hasTotpSecret());
|
||||
|
||||
// this is required, so the totp secret is stored in the user entity
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/2fa');
|
||||
|
||||
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/totp.png');
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertEquals('image/png', $client->getResponse()->headers->get('Content-Type'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\ActivityMeta;
|
||||
use App\Entity\ActivityRate;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\ProjectComment;
|
||||
use App\Entity\ProjectMeta;
|
||||
use App\Entity\ProjectRate;
|
||||
use App\Entity\Team;
|
||||
@@ -51,10 +49,7 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/project/export'),
|
||||
'help' => 'https://www.kimai.org/documentation/project.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -65,12 +60,8 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/project/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/project/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/project'),
|
||||
'help' => 'https://www.kimai.org/documentation/project.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -91,11 +82,8 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/admin/project/');
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/project/export'),
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/project/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/project.html'
|
||||
]);
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
@@ -174,23 +162,28 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/details');
|
||||
$this->assertDetailsPage($client);
|
||||
}
|
||||
|
||||
private function assertDetailsPage(HttpKernelBrowser $client)
|
||||
{
|
||||
self::assertHasProgressbar($client);
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#project_details_box');
|
||||
$node = $client->getCrawler()->filter('div.card#project_details_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#activity_list_box');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_list_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#time_budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#time_budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#budget_box');
|
||||
$node = $client->getCrawler()->filter('div.card#budget_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box a.btn.btn-default');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-actions a.btn');
|
||||
self::assertEquals(2, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#project_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#project_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
}
|
||||
|
||||
@@ -206,15 +199,14 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$form = $client->getCrawler()->filter('form[name=project_rate_form]')->form();
|
||||
$client->submit($form, [
|
||||
'project_rate_form' => [
|
||||
'user' => null,
|
||||
'rate' => $rate,
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/' . $projectId . '/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#project_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#project_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#project_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
$node = $client->getCrawler()->filter('div.card#project_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertStringContainsString($rate, $node->text(null, true));
|
||||
}
|
||||
@@ -228,10 +220,9 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$project->setMetaField((new ProjectMeta())->setName('foo')->setValue('bar'));
|
||||
$project->setEnd(new \DateTime());
|
||||
$em->persist($project);
|
||||
$team = new Team();
|
||||
$team = new Team('project 1');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_ADMIN));
|
||||
$team->addProject($project);
|
||||
$team->setName('project 1');
|
||||
$em->persist($team);
|
||||
$rate = new ProjectRate();
|
||||
$rate->setProject($project);
|
||||
@@ -248,14 +239,14 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$em->persist($rate);
|
||||
$em->flush();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('project.duplicate');
|
||||
$token = $this->getCsrfToken($client, 'project.duplicate');
|
||||
|
||||
$this->request($client, '/admin/project/1/duplicate/' . $token);
|
||||
$this->assertIsRedirect($client, '/details');
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#project_rates_box');
|
||||
$node = $client->getCrawler()->filter('div.card#project_rates_box');
|
||||
self::assertEquals(1, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#project_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
$node = $client->getCrawler()->filter('div.card#project_rates_box table.dataTable tbody tr:not(.summary)');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertStringContainsString('123.45', $node->text(null, true));
|
||||
}
|
||||
@@ -291,14 +282,12 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('A beautiful and long comment **with some** markdown formatting', $node->html());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService->offsetSet('timesheet.markdown_content', true);
|
||||
$this->setSystemConfiguration('timesheet.markdown_content', true);
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('<p>A beautiful and long comment <strong>with some</strong> markdown formatting</p>', $node->html());
|
||||
}
|
||||
|
||||
@@ -314,20 +303,14 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('Foo bar blub', $node->html());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body a.confirmation-link');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.delete-comment-link');
|
||||
|
||||
$comments = $this->getEntityManager()->getRepository(ProjectComment::class)->findAll();
|
||||
$id = $comments[0]->getId();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('project.delete_comment');
|
||||
|
||||
self::assertEquals($this->createUrl('/admin/project/' . $id . '/comment_delete/' . $token), $node->attr('href'));
|
||||
$this->request($client, '/admin/project/' . $id . '/comment_delete/' . $token);
|
||||
$this->request($client, $node->attr('href'));
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('There were no comments posted yet', $node->html());
|
||||
}
|
||||
|
||||
@@ -343,39 +326,35 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body');
|
||||
self::assertStringContainsString('Foo bar blub', $node->html());
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body a.btn.active');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link.active');
|
||||
self::assertEquals(0, $node->count());
|
||||
|
||||
$comments = $this->getEntityManager()->getRepository(ProjectComment::class)->findAll();
|
||||
$id = $comments[0]->getId();
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('project.pin_comment');
|
||||
|
||||
$this->request($client, '/admin/project/' . $id . '/comment_pin/' . $token);
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link');
|
||||
self::assertEquals(1, $node->count());
|
||||
$this->request($client, $node->attr('href'));
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .box-body a.btn.active');
|
||||
$token2 = self::$container->get('security.csrf.token_manager')->getToken('project.pin_comment');
|
||||
$node = $client->getCrawler()->filter('div.card#comments_box .card-body a.pin-comment-link.active');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertEquals($this->createUrl('/admin/project/' . $id . '/comment_pin/' . $token2), $node->attr('href'));
|
||||
self::assertNotEquals($token, $token2);
|
||||
self::assertStringContainsString('/admin/project/', $node->attr('href'));
|
||||
self::assertStringContainsString('/comment_pin/', $node->attr('href'));
|
||||
}
|
||||
|
||||
public function testCreateDefaultTeamAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body');
|
||||
self::assertStringContainsString('Visible to everyone, as no team was assigned yet.', $node->text(null, true));
|
||||
|
||||
$this->request($client, '/admin/project/1/create_team');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-title');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-title');
|
||||
self::assertStringContainsString('Only visible to the following teams and all admins.', $node->text(null, true));
|
||||
$node = $client->getCrawler()->filter('div.box#team_listing_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#team_listing_box .card-body table tbody tr');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
// creating the default team a second time fails, as the name already exists
|
||||
@@ -389,9 +368,9 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/activities/1');
|
||||
$node = $client->getCrawler()->filter('div.box#activity_list_box .box-tools ul.pagination li');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_list_box .card-actions ul.pagination li');
|
||||
self::assertEquals(0, $node->count());
|
||||
$node = $client->getCrawler()->filter('div.box#activity_list_box .box-tools a.modal-ajax-form.open-edit');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_list_box .card-actions a.modal-ajax-form.open-edit');
|
||||
self::assertEquals(1, $node->count());
|
||||
|
||||
/** @var EntityManager $em */
|
||||
@@ -404,10 +383,10 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/activities/1');
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#activity_list_box .box-tools ul.pagination li');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_list_box .card-footer ul.pagination li');
|
||||
self::assertEquals(4, $node->count());
|
||||
|
||||
$node = $client->getCrawler()->filter('div.box#activity_list_box .box-body table tbody tr');
|
||||
$node = $client->getCrawler()->filter('div.card#activity_list_box .card-body table tbody tr');
|
||||
self::assertEquals(5, $node->count());
|
||||
}
|
||||
|
||||
@@ -422,15 +401,18 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
'customer' => 1,
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, '/details');
|
||||
$client->followRedirect();
|
||||
|
||||
$location = $this->assertIsModalRedirect($client, '/details');
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$this->assertDetailsPage($client);
|
||||
$this->assertHasFlashSuccess($client);
|
||||
}
|
||||
|
||||
public function testCreateActionShowsMetaFields()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
$this->assertAccessIsGranted($client, '/admin/project/create');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
|
||||
@@ -50,10 +50,6 @@ class QuickEntryControllerTest extends ControllerBaseTest
|
||||
}
|
||||
// project + activity + 7 days (duration) + row totals
|
||||
self::assertCount(10, $columns);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'help' => 'https://www.kimai.org/documentation/weekly-times.html'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIndexActionWith()
|
||||
@@ -92,9 +88,5 @@ class QuickEntryControllerTest extends ControllerBaseTest
|
||||
}
|
||||
// project + activity + 7 days (duration) + row totals
|
||||
self::assertCount(10, $columns);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'help' => 'https://www.kimai.org/documentation/weekly-times.html'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ abstract class AbstractUserPeriodControllerTest extends ControllerBaseTest
|
||||
return [
|
||||
[4, 'duration', 'Working hours total'],
|
||||
[4, 'rate', 'Total revenue'],
|
||||
[4, 'internalRate', 'Internal rate'],
|
||||
[4, 'internalRate', 'Internal price'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ abstract class AbstractUserPeriodControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->importReportingFixture(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, sprintf('%s?user=%s&date=12999119191&sumType=%s', $this->getReportUrl(), $user, $dataType));
|
||||
self::assertStringContainsString(sprintf('<div class="box-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
self::assertStringContainsString(sprintf('<div class="card-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
$option = $client->getCrawler()->filterXPath("//select[@id='user']/option[@selected]");
|
||||
self::assertEquals($user, $option->attr('value'));
|
||||
$cell = $client->getCrawler()->filterXPath("//th[contains(@class, 'reportDataTypeTitle')]");
|
||||
@@ -66,7 +66,7 @@ abstract class AbstractUserPeriodControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importReportingFixture(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, sprintf('%s?date=12999119191', $this->getReportUrl()));
|
||||
self::assertStringContainsString(sprintf('<div class="box-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
self::assertStringContainsString(sprintf('<div class="card-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
$select = $client->getCrawler()->filterXPath("//select[@id='user']");
|
||||
self::assertEquals(0, $select->count());
|
||||
$cell = $client->getCrawler()->filterXPath("//th[contains(@class, 'reportDataTypeTitle')]");
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract class AbstractUsersPeriodControllerTest extends ControllerBaseTest
|
||||
return [
|
||||
['duration', 'Working hours total'],
|
||||
['rate', 'Total revenue'],
|
||||
['internalRate', 'Internal rate'],
|
||||
['internalRate', 'Internal price'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ abstract class AbstractUsersPeriodControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->importReportingFixture(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, sprintf('%s?date=12999119191&sumType=%s', $this->getReportUrl(), $dataType));
|
||||
self::assertStringContainsString(sprintf('<div class="box-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
self::assertStringContainsString(sprintf('<div class="card-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
$cell = $client->getCrawler()->filterXPath("//th[contains(@class, 'reportDataTypeTitle')]");
|
||||
self::assertEquals($title, $cell->text());
|
||||
}
|
||||
@@ -70,7 +70,7 @@ abstract class AbstractUsersPeriodControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importReportingFixture(User::ROLE_TEAMLEAD);
|
||||
$this->assertAccessIsGranted($client, sprintf('%s?date=12999119191&sumType=%s', $this->getReportUrl(), $dataType));
|
||||
self::assertStringContainsString(sprintf('<div class="box-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
self::assertStringContainsString(sprintf('<div class="card-body %s', $this->getBoxId()), $client->getResponse()->getContent());
|
||||
$select = $client->getCrawler()->filterXPath("//select[@id='user']");
|
||||
self::assertEquals(0, $select->count());
|
||||
$cell = $client->getCrawler()->filterXPath("//th[contains(@class, 'reportDataTypeTitle')]");
|
||||
|
||||
@@ -78,7 +78,7 @@ class CustomerMonthlyProjectsControllerTest extends ControllerBaseTest
|
||||
$client = $this->prepareReport();
|
||||
|
||||
$this->assertAccessIsGranted($client, '/reporting/customer/monthly_projects/view');
|
||||
self::assertStringContainsString('<form method="get" class="form-inline form-reporting" id="report-toolbar-form">', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<form method="get" class="form-reporting" id="report-form">', $client->getResponse()->getContent());
|
||||
$rows = $client->getCrawler()->filterXPath("//table[contains(@class, 'dataTable')]/tbody/tr[not(@class='summary')]");
|
||||
self::assertGreaterThan(0, $rows->count());
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Tests\DataFixtures\ActivityFixtures;
|
||||
use App\Tests\DataFixtures\CustomerFixtures;
|
||||
use App\Tests\DataFixtures\ProjectFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -50,15 +51,20 @@ class ProjectDateRangeControllerTest extends ControllerBaseTest
|
||||
$activities->setIsGlobal(true);
|
||||
$activities = $this->importFixture($activities);
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_TEAMLEAD);
|
||||
$dateTimeFactory = DateTimeFactory::createByUser($user);
|
||||
$startMonth = $dateTimeFactory->getStartOfMonth();
|
||||
$startDate = $startMonth->add(new \DateInterval('P10D'));
|
||||
|
||||
$timesheets = new TimesheetFixtures();
|
||||
$timesheets->setStartDate(new \DateTime('first day of this month'));
|
||||
$timesheets->setStartDate($startDate);
|
||||
$timesheets->setAmount(50);
|
||||
$timesheets->setActivities($activities);
|
||||
$timesheets->setUser($this->getUserByRole(User::ROLE_TEAMLEAD));
|
||||
$timesheets->setUser($user);
|
||||
$this->importFixture($timesheets);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/reporting/project_daterange');
|
||||
self::assertStringContainsString('<div class="box-body project_daterange_reporting-box', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<div class="card-body project_daterange_reporting-box', $client->getResponse()->getContent());
|
||||
$rows = $client->getCrawler()->filterXPath("//table[contains(@class, 'dataTable')]/tbody/tr[not(@class='summary')]");
|
||||
self::assertGreaterThan(0, $rows->count());
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ class ProjectDetailsControllerTest extends ControllerBaseTest
|
||||
$this->assertHasNoEntriesWithFilter($client);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/reporting/project_details?project=' . $projects[0]->getId());
|
||||
$rows = $client->getCrawler()->filterXPath("//form[@id='project-details-form']");
|
||||
$rows = $client->getCrawler()->filterXPath("//form[@id='report-form']");
|
||||
self::assertEquals(1, $rows->count());
|
||||
|
||||
$rows = $client->getCrawler()->filterXPath("//div[@id='reporting-content']/div[@class='nav-tabs-custom']");
|
||||
$rows = $client->getCrawler()->filterXPath("//div[@id='reporting-content']//ul[contains(@class, 'nav-pills')]");
|
||||
self::assertGreaterThan(1, $rows->count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class ProjectInactiveControllerTest extends ControllerBaseTest
|
||||
$this->importFixture($timesheets);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/reporting/project_inactive');
|
||||
self::assertStringContainsString('<div class="box-body inactive_project_reporting-box', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<div class="card-body inactive_project_reporting-box', $client->getResponse()->getContent());
|
||||
$rows = $client->getCrawler()->filterXPath("//table[contains(@class, 'dataTable')]/tbody/tr[not(@class='summary')]");
|
||||
self::assertGreaterThan(0, $rows->count());
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class ProjectViewControllerTest extends ControllerBaseTest
|
||||
$this->importFixture($timesheets);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/reporting/project_view');
|
||||
self::assertStringContainsString('<div class="box-body project_view_reporting-box', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<div class="card-body project_view_reporting-box', $client->getResponse()->getContent());
|
||||
$rows = $client->getCrawler()->filterXPath("//table[contains(@class, 'dataTable')]/tbody/tr[not(@class='summary')]");
|
||||
self::assertGreaterThan(0, $rows->count());
|
||||
}
|
||||
|
||||
@@ -21,12 +21,19 @@ class ReportingControllerTest extends ControllerBaseTest
|
||||
$this->assertUrlIsSecured('/reporting');
|
||||
}
|
||||
|
||||
public function testRedirectForDefaultReportUrl()
|
||||
public function testOverviewPage()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/reporting/');
|
||||
$nodes = $client->getCrawler()->filter('section.content div.card');
|
||||
$this->assertCount(11, $nodes);
|
||||
}
|
||||
|
||||
public function testOverviewPageAsUser()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->request($client, '/reporting/');
|
||||
$this->assertIsRedirect($client, $this->createUrl('/reporting/user/week'));
|
||||
$client->followRedirect();
|
||||
self::assertStringContainsString('<div class="box-body user-week-reporting-box', $client->getResponse()->getContent());
|
||||
$nodes = $client->getCrawler()->filter('section.content div.card');
|
||||
$this->assertCount(3, $nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,12 +57,12 @@ class PasswordResetControllerTest extends ControllerBaseTest
|
||||
$content = $response->getContent();
|
||||
$this->assertStringContainsString('<title>Kimai – Time Tracking</title>', $content);
|
||||
$this->assertStringContainsString('Reset your password', $content);
|
||||
$this->assertStringContainsString('<form action="/en/resetting/send-email" method="POST" class="fos_user_resetting_request">', $content);
|
||||
$this->assertStringContainsString('<form class="card-body security-password-reset" action="/en/resetting/send-email" method="post" autocomplete="off">', $content);
|
||||
$this->assertStringContainsString('<input type="text"', $content);
|
||||
$this->assertStringContainsString('id="username" name="username" required="required"', $content);
|
||||
$this->assertStringContainsString('>Reset your password</button>', $content);
|
||||
$this->assertStringContainsString('Reset your password', $content);
|
||||
|
||||
$form = $client->getCrawler()->filter('form.fos_user_resetting_request')->form();
|
||||
$form = $client->getCrawler()->filter('form')->form();
|
||||
$client->submit($form, [
|
||||
'username' => 'john_user',
|
||||
]);
|
||||
|
||||
@@ -45,10 +45,9 @@ class SecurityControllerTest extends ControllerBaseTest
|
||||
|
||||
$content = $response->getContent();
|
||||
$this->assertStringContainsString('<title>Kimai – Time Tracking</title>', $content);
|
||||
$this->assertStringContainsString('<form action="/en/login_check" method="post">', $content);
|
||||
$this->assertStringContainsString('<form action="/en/login_check" method="post"', $content);
|
||||
$this->assertStringContainsString('<input type="text" name="_username"', $content);
|
||||
$this->assertStringContainsString('<input name="_password" type="password"', $content);
|
||||
$this->assertStringContainsString('<input id="remember_me" name="_remember_me" type="checkbox"', $content);
|
||||
$this->assertStringContainsString('">Login</button>', $content);
|
||||
$this->assertStringContainsString('<input type="hidden" name="_csrf_token" value="', $content);
|
||||
$this->assertStringNotContainsString('<a href="/en/register/"', $content);
|
||||
@@ -112,7 +111,7 @@ class SecurityControllerTest extends ControllerBaseTest
|
||||
$client->followRedirect();
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertStringContainsString('<div class="alert alert-danger">Invalid credentials.</div>', $client->getResponse()->getContent());
|
||||
self::assertStringContainsString('<div class="alert alert-important alert-danger">Invalid credentials.</div>', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testCheckAction()
|
||||
@@ -120,7 +119,7 @@ class SecurityControllerTest extends ControllerBaseTest
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
|
||||
|
||||
$client = self::createClient(); // just to bootstrap the container
|
||||
self::createClient(); // just to bootstrap the container
|
||||
$csrf = $this->createMock(CsrfTokenManagerInterface::class);
|
||||
$systemConfig = new SystemConfiguration(new TestConfigLoader([]), ['saml' => ['activate' => true]]);
|
||||
$samlConfig = new SamlConfiguration($systemConfig);
|
||||
@@ -133,7 +132,7 @@ class SecurityControllerTest extends ControllerBaseTest
|
||||
$this->expectException(\RuntimeException::class);
|
||||
$this->expectExceptionMessage('You must activate the logout in your security firewall configuration.');
|
||||
|
||||
$client = self::createClient(); // just to bootstrap the container
|
||||
self::createClient(); // just to bootstrap the container
|
||||
$csrf = $this->createMock(CsrfTokenManagerInterface::class);
|
||||
$systemConfig = new SystemConfiguration(new TestConfigLoader([]), ['saml' => ['activate' => true]]);
|
||||
$samlConfig = new SamlConfiguration($systemConfig);
|
||||
|
||||
@@ -58,16 +58,16 @@ class SelfRegistrationControllerTest extends ControllerBaseTest
|
||||
$content = $response->getContent();
|
||||
$this->assertStringContainsString('<title>Kimai – Time Tracking</title>', $content);
|
||||
$this->assertStringContainsString('Register a new account', $content);
|
||||
$this->assertStringContainsString('<form name="fos_user_registration_form" method="post" action="/en/register/" class="fos_user_registration_register">', $content);
|
||||
$this->assertStringContainsString('<form name="user_registration_form" method="post" action="/en/register/"', $content);
|
||||
$this->assertStringContainsString('<input type="email"', $content);
|
||||
$this->assertStringContainsString('id="fos_user_registration_form_email" name="fos_user_registration_form[email]" required="required"', $content);
|
||||
$this->assertStringContainsString('id="user_registration_form_email" name="user_registration_form[email]" required="required"', $content);
|
||||
$this->assertStringContainsString('<input type="text"', $content);
|
||||
$this->assertStringContainsString('id="fos_user_registration_form_username" name="fos_user_registration_form[username]" required="required" maxlength="60" pattern=".{2,}"', $content);
|
||||
$this->assertStringContainsString('id="user_registration_form_username" name="user_registration_form[username]" required="required" maxlength="60" pattern=".{2,}"', $content);
|
||||
$this->assertStringContainsString('<input type="password"', $content);
|
||||
$this->assertStringContainsString('id="fos_user_registration_form_plainPassword_first" name="fos_user_registration_form[plainPassword][first]" required="required"', $content);
|
||||
$this->assertStringContainsString('id="fos_user_registration_form_plainPassword_second" name="fos_user_registration_form[plainPassword][second]" required="required"', $content);
|
||||
$this->assertStringContainsString('id="user_registration_form_plainPassword_first" name="user_registration_form[plainPassword][first]" required="required"', $content);
|
||||
$this->assertStringContainsString('id="user_registration_form_plainPassword_second" name="user_registration_form[plainPassword][second]" required="required"', $content);
|
||||
$this->assertStringContainsString('<input type="hidden"', $content);
|
||||
$this->assertStringContainsString('id="fos_user_registration_form__token" name="fos_user_registration_form[_token]"', $content);
|
||||
$this->assertStringContainsString('id="user_registration_form__token" name="user_registration_form[_token]"', $content);
|
||||
$this->assertStringContainsString('>Register</button>', $content);
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ class SelfRegistrationControllerTest extends ControllerBaseTest
|
||||
$response = $client->getResponse();
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=fos_user_registration_form]')->form();
|
||||
$form = $client->getCrawler()->filter('form[name=user_registration_form]')->form();
|
||||
$client->submit($form, [
|
||||
'fos_user_registration_form' => [
|
||||
'user_registration_form' => [
|
||||
'email' => $email,
|
||||
'username' => $username,
|
||||
'plainPassword' => [
|
||||
@@ -171,7 +171,7 @@ class SelfRegistrationControllerTest extends ControllerBaseTest
|
||||
$client = self::createClient();
|
||||
$this->setSystemConfiguration('user.registration', true);
|
||||
|
||||
$this->assertHasValidationError($client, '/register/', 'form[name=fos_user_registration_form]', $formData, $validationFields);
|
||||
$this->assertHasValidationError($client, '/register/', 'form[name=user_registration_form]', $formData, $validationFields);
|
||||
}
|
||||
|
||||
public function getValidationTestData()
|
||||
@@ -180,44 +180,44 @@ class SelfRegistrationControllerTest extends ControllerBaseTest
|
||||
[
|
||||
// invalid fields: username, password_second, email
|
||||
[
|
||||
'fos_user_registration_form' => [
|
||||
'user_registration_form' => [
|
||||
'username' => '',
|
||||
'plainPassword' => ['first' => 'sdfsdf123'],
|
||||
'email' => '',
|
||||
]
|
||||
],
|
||||
[
|
||||
'#fos_user_registration_form_username',
|
||||
'#fos_user_registration_form_plainPassword_first',
|
||||
'#fos_user_registration_form_email',
|
||||
'#user_registration_form_username',
|
||||
'#user_registration_form_plainPassword_first',
|
||||
'#user_registration_form_email',
|
||||
]
|
||||
],
|
||||
// invalid fields: username, password, email
|
||||
[
|
||||
[
|
||||
'fos_user_registration_form' => [
|
||||
'user_registration_form' => [
|
||||
'username' => 'x',
|
||||
'plainPassword' => ['first' => 'sdfsdf123', 'second' => 'sdfxxxxxxx'],
|
||||
'email' => 'ydfbvsdfgs',
|
||||
]
|
||||
],
|
||||
[
|
||||
'#fos_user_registration_form_username',
|
||||
'#fos_user_registration_form_plainPassword_first',
|
||||
'#fos_user_registration_form_email',
|
||||
'#user_registration_form_username',
|
||||
'#user_registration_form_plainPassword_first',
|
||||
'#user_registration_form_email',
|
||||
]
|
||||
],
|
||||
// invalid fields: password (too short)
|
||||
[
|
||||
[
|
||||
'fos_user_registration_form' => [
|
||||
'user_registration_form' => [
|
||||
'username' => 'test123',
|
||||
'plainPassword' => ['first' => 'test123', 'second' => 'test123'],
|
||||
'email' => 'ydfbvsdfgs@example.com',
|
||||
]
|
||||
],
|
||||
[
|
||||
'#fos_user_registration_form_plainPassword_first',
|
||||
'#user_registration_form_plainPassword_first',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
@@ -27,17 +27,23 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/admin/system-config/');
|
||||
}
|
||||
|
||||
private function getSystemConfiguration(): SystemConfiguration
|
||||
{
|
||||
return static::getContainer()->get(SystemConfiguration::class);
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$expectedForms = $this->getTestDataForms();
|
||||
$expectedCount = \count($expectedForms) + 1; // the menu is another card
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary');
|
||||
$this->assertEquals(\count($expectedForms), \count($result));
|
||||
$result = $client->getCrawler()->filter('section.content div.card');
|
||||
$this->assertEquals($expectedCount, \count($result));
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary form');
|
||||
$result = $client->getCrawler()->filter('section.content div.card form');
|
||||
$this->assertEquals(\count($expectedForms), \count($result));
|
||||
|
||||
foreach ($expectedForms as $formConfig) {
|
||||
@@ -54,10 +60,10 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/edit/timesheet');
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary');
|
||||
$result = $client->getCrawler()->filter('section.content div.card');
|
||||
$this->assertEquals(1, \count($result));
|
||||
|
||||
$result = $client->getCrawler()->filter('section.content div.box.box-primary form');
|
||||
$result = $client->getCrawler()->filter('section.content div.card form');
|
||||
$this->assertEquals(1, \count($result));
|
||||
|
||||
$result = $client->getCrawler()->filter('form[name=system_configuration_form_timesheet]');
|
||||
@@ -90,7 +96,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertEquals('default', $configService->find('timesheet.mode'));
|
||||
$this->assertTrue($configService->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertTrue($configService->find('timesheet.rules.allow_zero_duration'));
|
||||
@@ -100,8 +106,8 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client->submit($form, [
|
||||
'system_configuration_form_timesheet' => [
|
||||
'configuration' => [
|
||||
['name' => 'timesheet.mode', 'value' => 'duration_only'],
|
||||
['name' => 'timesheet.active_entries.default_begin', 'value' => '23:59'],
|
||||
['name' => 'timesheet.mode', 'value' => 'punch'],
|
||||
['name' => 'timesheet.default_begin', 'value' => '23:59'],
|
||||
['name' => 'timesheet.rules.allow_future_times', 'value' => false],
|
||||
['name' => 'timesheet.rules.allow_zero_duration', 'value' => true],
|
||||
['name' => 'timesheet.rules.allow_overlapping_records', 'value' => false],
|
||||
@@ -116,8 +122,8 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals('duration_only', $configService->find('timesheet.mode'));
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertEquals('punch', $configService->find('timesheet.mode'));
|
||||
$this->assertFalse($configService->find('timesheet.rules.allow_future_times'));
|
||||
$this->assertFalse($configService->find('timesheet.rules.allow_overlapping_records'));
|
||||
$this->assertEquals(99, $configService->find('timesheet.active_entries.hard_limit'));
|
||||
@@ -128,7 +134,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertNull($configService->find('timesheet.rules.lockdown_period_start'));
|
||||
$this->assertNull($configService->find('timesheet.rules.lockdown_period_end'));
|
||||
$this->assertNull($configService->find('timesheet.rules.lockdown_period_timezone'));
|
||||
@@ -151,7 +157,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertEquals('first day of last month 01:23:45', $configService->find('timesheet.rules.lockdown_period_start'));
|
||||
$this->assertEquals('last day of last month 23:01:45', $configService->find('timesheet.rules.lockdown_period_end'));
|
||||
$this->assertEquals('Africa/Bangui', $configService->find('timesheet.rules.lockdown_period_timezone'));
|
||||
@@ -168,7 +174,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
'system_configuration_form_timesheet' => [
|
||||
'configuration' => [
|
||||
['name' => 'timesheet.mode', 'value' => 'foo'],
|
||||
['name' => 'timesheet.active_entries.default_begin', 'value' => '23:59'],
|
||||
['name' => 'timesheet.default_begin', 'value' => '23:59'],
|
||||
['name' => 'timesheet.rules.allow_future_times', 'value' => 1],
|
||||
['name' => 'timesheet.rules.allow_zero_duration', 'value' => 1],
|
||||
['name' => 'timesheet.rules.allow_overlapping_records', 'value' => 1],
|
||||
@@ -190,7 +196,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertNull($configService->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('DE', $configService->find('defaults.customer.country'));
|
||||
$this->assertEquals('EUR', $configService->find('defaults.customer.currency'));
|
||||
@@ -211,7 +217,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertEquals('Atlantic/Canary', $configService->find('defaults.customer.timezone'));
|
||||
$this->assertEquals('BB', $configService->find('defaults.customer.country'));
|
||||
$this->assertEquals('GBP', $configService->find('defaults.customer.currency'));
|
||||
@@ -245,9 +251,9 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/edit/user');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertNull($configService->find('defaults.user.timezone'));
|
||||
$this->assertNull($configService->find('defaults.user.theme'));
|
||||
$this->assertEquals('default', $configService->find('defaults.user.theme'));
|
||||
$this->assertEquals('en', $configService->find('defaults.user.language'));
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=system_configuration_form_user]')->form();
|
||||
@@ -256,7 +262,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
'configuration' => [
|
||||
['name' => 'defaults.user.timezone', 'value' => 'Pacific/Tahiti'],
|
||||
['name' => 'defaults.user.language', 'value' => 'ru'],
|
||||
['name' => 'defaults.user.theme', 'value' => 'purple'],
|
||||
['name' => 'defaults.user.theme', 'value' => 'dark'],
|
||||
]
|
||||
]
|
||||
]);
|
||||
@@ -266,9 +272,9 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertEquals('Pacific/Tahiti', $configService->find('defaults.user.timezone'));
|
||||
$this->assertEquals('purple', $configService->find('defaults.user.theme'));
|
||||
$this->assertEquals('dark', $configService->find('defaults.user.theme'));
|
||||
$this->assertEquals('ru', $configService->find('defaults.user.language'));
|
||||
}
|
||||
|
||||
@@ -301,15 +307,13 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertFalse($configService->find('timesheet.markdown_content'));
|
||||
$this->assertEquals('selectpicker', $configService->find('theme.select_type'));
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=system_configuration_form_theme]')->form();
|
||||
$client->submit($form, [
|
||||
'system_configuration_form_theme' => [
|
||||
'configuration' => [
|
||||
['name' => 'theme.autocomplete_chars', 'value' => 5],
|
||||
['name' => 'timesheet.markdown_content', 'value' => 1],
|
||||
]
|
||||
]
|
||||
@@ -320,8 +324,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$this->assertEquals('selectpicker', $configService->find('theme.select_type'));
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertTrue($configService->find('timesheet.markdown_content'));
|
||||
}
|
||||
|
||||
@@ -334,13 +337,13 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
[
|
||||
'system_configuration_form_theme' => [
|
||||
'configuration' => [
|
||||
['name' => 'theme.select_type', 'value' => 'foo'],
|
||||
['name' => 'timesheet.markdown_content', 'value' => 1],
|
||||
['name' => 'theme.color_choices', 'value' => '112324567865=)(/&%$§Silver|#c0c0c0'],
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'#system_configuration_form_theme_configuration_0_value',
|
||||
'#system_configuration_form_theme_configuration_1_value',
|
||||
],
|
||||
true
|
||||
);
|
||||
@@ -351,7 +354,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertTrue($configService->find('calendar.week_numbers'));
|
||||
$this->assertTrue($configService->find('calendar.weekends'));
|
||||
$this->assertEquals('08:00', $configService->find('calendar.businessHours.begin'));
|
||||
@@ -378,7 +381,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$this->assertHasFlashSaveSuccess($client);
|
||||
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService = $this->getSystemConfiguration();
|
||||
$this->assertFalse($configService->find('calendar.week_numbers'));
|
||||
$this->assertFalse($configService->find('calendar.weekends'));
|
||||
$this->assertEquals('10:00', $configService->find('calendar.businessHours.begin'));
|
||||
|
||||
@@ -32,12 +32,12 @@ class TagControllerTest extends ControllerBaseTest
|
||||
return $this->importFixture($fixture);
|
||||
}
|
||||
|
||||
public function testIsSecure()
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/admin/tags/');
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importTags();
|
||||
@@ -47,7 +47,7 @@ class TagControllerTest extends ControllerBaseTest
|
||||
$this->assertDataTableRowCount($client, 'datatable_admin_tags', 10);
|
||||
}
|
||||
|
||||
public function testIndexActionWithSearchTermQuery()
|
||||
public function testIndexActionWithSearchTermQuery(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importTags();
|
||||
@@ -65,7 +65,7 @@ class TagControllerTest extends ControllerBaseTest
|
||||
$this->assertDataTableRowCount($client, 'datatable_admin_tags', 2);
|
||||
}
|
||||
|
||||
public function testCreateAction()
|
||||
public function testCreateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/tags/create');
|
||||
@@ -86,7 +86,7 @@ class TagControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('A tAG Name!', $editForm->get('tag_edit_form[name]')->getValue());
|
||||
}
|
||||
|
||||
public function testEditAction()
|
||||
public function testEditAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$tags = $this->importTags();
|
||||
@@ -105,7 +105,7 @@ class TagControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('Test 2 updated', $editForm->get('tag_edit_form[name]')->getValue());
|
||||
}
|
||||
|
||||
public function testMultiDeleteAction()
|
||||
public function testMultiDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importTags();
|
||||
@@ -127,7 +127,6 @@ class TagControllerTest extends ControllerBaseTest
|
||||
|
||||
$client->submit($form, [
|
||||
'multi_update_table' => [
|
||||
'action' => $this->createUrl('/admin/tags/multi-delete'),
|
||||
'entities' => implode(',', $ids)
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -41,9 +41,7 @@ class TeamControllerTest extends ControllerBaseTest
|
||||
|
||||
$this->assertAccessIsGranted($client, '/admin/teams/');
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'create' => $this->createUrl('/admin/teams/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/teams.html'
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/teams/create'),
|
||||
]);
|
||||
$this->assertHasDataTable($client);
|
||||
$this->assertDataTableRowCount($client, 'datatable_admin_teams', 6);
|
||||
@@ -52,7 +50,6 @@ class TeamControllerTest extends ControllerBaseTest
|
||||
public function testIndexActionWithSearchTermQuery()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$em = $this->getEntityManager();
|
||||
$fixture = new TeamFixtures();
|
||||
$fixture->setAmount(5);
|
||||
$fixture->setCallback(function (Team $team) {
|
||||
@@ -86,8 +83,9 @@ class TeamControllerTest extends ControllerBaseTest
|
||||
$values['team_edit_form']['members'][0]['teamlead'] = 1;
|
||||
$client->request($form->getMethod(), $form->getUri(), $values, $form->getPhpFiles());
|
||||
|
||||
$this->assertIsRedirect($client, '/edit');
|
||||
$client->followRedirect();
|
||||
$location = $this->assertIsModalRedirect($client, '/edit');
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$this->assertHasFlashSuccess($client);
|
||||
$this->assertHasCustomerAndProjectPermissionBoxes($client);
|
||||
}
|
||||
@@ -207,19 +205,15 @@ class TeamControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$token = self::$container->get('security.csrf.token_manager')->getToken('team.duplicate');
|
||||
$this->request($client, '/admin/teams/1/duplicate');
|
||||
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
|
||||
|
||||
$this->request($client, '/admin/teams/1/duplicate/' . $token);
|
||||
$this->assertIsRedirect($client, '/edit');
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('#team_edit_form_name');
|
||||
self::assertEquals(1, $node->count());
|
||||
self::assertEquals('Test team [COPY]', $node->attr('value'));
|
||||
}
|
||||
$client->submit($form);
|
||||
|
||||
public function testDuplicateActionWithInvalidCsrf()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertInvalidCsrfToken($client, '/admin/teams/1/duplicate/rsetdzfukgli78t6r5uedtjfzkugl', $this->createUrl('/admin/teams/1/edit'));
|
||||
$location = $this->assertIsModalRedirect($client);
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$editForm = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
|
||||
$this->assertEquals('Test team (1)', $editForm->get('team_edit_form[name]')->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,10 @@ use App\Entity\Configuration;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\TimesheetMeta;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use App\Repository\TagRepository;
|
||||
use App\Tests\DataFixtures\ActivityFixtures;
|
||||
use App\Tests\DataFixtures\TagFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Tests\Mocks\TimesheetTestMetaFieldSubscriberMock;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
@@ -26,12 +27,12 @@ use App\Timesheet\DateTimeFactory;
|
||||
*/
|
||||
class TimesheetControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/timesheet/');
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/timesheet/');
|
||||
@@ -40,15 +41,12 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
// there are no records by default in the test database
|
||||
$this->assertHasNoEntriesWithFilter($client);
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action modal-ajax-form' => $this->createUrl('/timesheet/export/'),
|
||||
'download modal-ajax-form' => $this->createUrl('/timesheet/export/'),
|
||||
'create modal-ajax-form' => $this->createUrl('/timesheet/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/timesheet.html'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIndexActionWithQuery()
|
||||
public function testIndexActionWithQuery(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$start = new \DateTime('first day of this month');
|
||||
@@ -63,7 +61,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = ($start)->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime('last day of this month'))->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange($start, new \DateTime('last day of this month'));
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
$client->submit($form, [
|
||||
@@ -84,7 +82,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
self::assertEquals(2, $node->count());
|
||||
}
|
||||
|
||||
public function testIndexActionWithSearchTermQuery()
|
||||
public function testIndexActionWithSearchTermQuery(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$start = new \DateTime('first day of this month');
|
||||
@@ -109,8 +107,6 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = ($start)->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime('last day of this month'))->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
$client->submit($form, [
|
||||
'searchTerm' => 'location:homeoffice foobar',
|
||||
@@ -121,10 +117,23 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertDataTableRowCount($client, 'datatable_timesheet', 5);
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
public function testExportAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(15);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
$fixture->setCallback(function (Timesheet $timesheet) {
|
||||
$duration = rand(3600, 36000);
|
||||
$begin = new \DateTime('-15 days');
|
||||
$end = clone $begin;
|
||||
$end->modify('+' . $duration . ' seconds');
|
||||
$timesheet->setBegin($begin);
|
||||
$timesheet->setEnd($end);
|
||||
});
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(5);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
@@ -134,14 +143,12 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/timesheet/export/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange(new \DateTime('-10 days'), new \DateTime());
|
||||
|
||||
$client->submitForm('export-btn-print', [
|
||||
'export' => [
|
||||
'state' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customers' => [],
|
||||
]
|
||||
'state' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customers' => [],
|
||||
]);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -155,7 +162,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(5, \count($result));
|
||||
}
|
||||
|
||||
public function testCreateAction()
|
||||
public function testCreateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/timesheet/create');
|
||||
@@ -168,8 +175,8 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
// begin is always pre-filled with the current datetime
|
||||
// 'begin' => null,
|
||||
// end must be allowed to be null, to start a record
|
||||
// there was a bug with end begin required, so we manually set this field to be empty
|
||||
'end' => null,
|
||||
// there was a bug with end a mandatory field, so we manually set this field to be empty
|
||||
'end_time' => null,
|
||||
'project' => 1,
|
||||
'activity' => 1,
|
||||
]
|
||||
@@ -194,7 +201,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getTestDataForDurationValues
|
||||
*/
|
||||
public function testCreateActionWithDurationValues($begin, $end, $duration, $expectedDuration, $expectedEnd)
|
||||
public function testCreateActionWithDurationValues($beginDate, $beginTime, $end, $duration, $expectedDuration, $expectedEnd): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/timesheet/create');
|
||||
@@ -204,8 +211,9 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$client->submit($form, [
|
||||
'timesheet_edit_form' => [
|
||||
'description' => 'Testing is fun!',
|
||||
'begin' => $begin,
|
||||
'end' => $end,
|
||||
'begin_date' => $beginDate,
|
||||
'begin_time' => $beginTime,
|
||||
'end_time' => $end,
|
||||
'duration' => $duration,
|
||||
'project' => 1,
|
||||
'activity' => 1,
|
||||
@@ -227,32 +235,32 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('Testing is fun!', $timesheet->getDescription());
|
||||
}
|
||||
|
||||
public function getTestDataForDurationValues()
|
||||
public function getTestDataForDurationValues(): \Generator
|
||||
{
|
||||
// duration is ignored, because end is set and the duration might come from a rounding rule (by default seconds are rounded down with 1)
|
||||
yield ['2018-12-31 00:00:00', '2018-12-31 02:10:10', '01:00', 7800, '2018-12-31 02:10:00'];
|
||||
yield ['2018-12-31 00:00:00', '2018-12-31 02:09:59', '01:00', 7740, '2018-12-31 02:09:00'];
|
||||
// if seconds are given, they are first rounded up (default for duration rounding is 1)
|
||||
yield ['2018-12-31 00:00:00', null, '01:00', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '01:00:10', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1h', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1h10m', 4200, '2018-12-31 01:10:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1h10s', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '60m', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '60M1s', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '3600s', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '59m60s', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1,0', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1.0', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1.5', 5400, '2018-12-31 01:30:00'];
|
||||
yield ['2018-12-31 00:00:00', null, '1,25', 4500, '2018-12-31 01:15:00'];
|
||||
yield ['12/31/2018', '12:00 AM', '02:10 AM', '01:00', 7800, '2018-12-31 02:10:00'];
|
||||
yield ['12/31/2018', '12:00 AM', '02:09 AM', '01:00', 7740, '2018-12-31 02:09:00'];
|
||||
// if seconds are given: they are first rounded up (default for duration rounding is 1)
|
||||
yield ['12/31/2018', '12:00 AM', null, '01:00', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '01:00:10', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1h', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1h10m', 4200, '2018-12-31 01:10:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1h10s', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '60m', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '60M1s', 3660, '2018-12-31 01:01:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '3600s', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '59m60s', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1,0', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1.0', 3600, '2018-12-31 01:00:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1.5', 5400, '2018-12-31 01:30:00'];
|
||||
yield ['12/31/2018', '12:00 AM', null, '1,25', 4500, '2018-12-31 01:15:00'];
|
||||
}
|
||||
|
||||
public function testCreateActionShowsMetaFields()
|
||||
public function testCreateActionShowsMetaFields(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
$this->request($client, '/timesheet/create');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
@@ -262,7 +270,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertFalse($form->has('timesheet_edit_form[metaFields][0][value]'));
|
||||
}
|
||||
|
||||
public function testCreateActionDoesNotShowRateFieldsForUser()
|
||||
public function testCreateActionDoesNotShowRateFieldsForUser(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
$this->request($client, '/timesheet/create');
|
||||
@@ -273,7 +281,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertFalse($form->has('fixedRate'));
|
||||
}
|
||||
|
||||
public function testCreateActionWithFromAndToValues()
|
||||
public function testCreateActionWithFromAndToValues(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/timesheet/create?from=2018-08-02T20%3A00%3A00&to=2018-08-02T20%3A30%3A00');
|
||||
@@ -307,7 +315,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals($expected->format(\DateTimeInterface::ATOM), $timesheet->getEnd()->format(\DateTimeInterface::ATOM));
|
||||
}
|
||||
|
||||
public function testCreateActionWithFromAndToValuesTwice()
|
||||
public function testCreateActionWithFromAndToValuesTwice(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/timesheet/create?from=2018-08-02T20%3A00%3A00&to=2018-08-02T20%3A30%3A00');
|
||||
@@ -358,7 +366,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertHasFlashSuccess($client);
|
||||
}
|
||||
|
||||
public function testCreateActionWithFromAndToValuesTwiceFailsOnOverlappingRecord()
|
||||
public function testCreateActionWithFromAndToValuesTwiceFailsOnOverlappingRecord(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/system-config/');
|
||||
@@ -368,7 +376,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
'system_configuration_form_timesheet' => [
|
||||
'configuration' => [
|
||||
['name' => 'timesheet.mode', 'value' => 'default'],
|
||||
['name' => 'timesheet.active_entries.default_begin', 'value' => '08:00'],
|
||||
['name' => 'timesheet.default_begin', 'value' => '08:00'],
|
||||
['name' => 'timesheet.rules.allow_future_times', 'value' => true],
|
||||
['name' => 'timesheet.rules.allow_zero_duration', 'value' => true],
|
||||
['name' => 'timesheet.rules.allow_overlapping_records', 'value' => false],
|
||||
@@ -402,11 +410,11 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
'activity' => 1,
|
||||
]
|
||||
],
|
||||
['#timesheet_edit_form_begin']
|
||||
['#timesheet_edit_form_begin_date']
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateActionWithOverbookedActivity()
|
||||
public function testCreateActionWithOverbookedActivity(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -448,8 +456,9 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
[
|
||||
'timesheet_edit_form' => [
|
||||
'hourlyRate' => 100,
|
||||
'begin' => '2020-02-18 01:00',
|
||||
'end' => '2020-02-18 02:10',
|
||||
'begin_date' => '02/18/2020',
|
||||
'begin_time' => '01:00 AM',
|
||||
'end_time' => '02:10 AM',
|
||||
'duration' => '01:10',
|
||||
'project' => 1,
|
||||
'activity' => $activity->getId(),
|
||||
@@ -459,7 +468,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateActionWithEmptyDuration()
|
||||
public function testCreateActionWithEmptyDuration(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -501,8 +510,9 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
[
|
||||
'timesheet_edit_form' => [
|
||||
'hourlyRate' => 100,
|
||||
'begin' => '2020-02-18 01:00',
|
||||
'end' => '2020-02-18 01:00',
|
||||
'begin_date' => '02/18/2020',
|
||||
'begin_time' => '01:00 AM',
|
||||
'end_time' => '01:00 AM',
|
||||
'duration' => '00:00',
|
||||
'project' => 1,
|
||||
'activity' => $activity->getId(),
|
||||
@@ -512,9 +522,14 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreateActionWithBeginAndEndAndTagValues()
|
||||
public function testCreateActionWithBeginAndEndAndTagValues(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->request($client, '/timesheet/create?begin=2018-08-02&end=2018-08-02&tags=one,two,three');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
@@ -548,7 +563,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(['one', 'two', 'three'], $timesheet->getTagsAsArray());
|
||||
}
|
||||
|
||||
public function testCreateActionWithDescription()
|
||||
public function testCreateActionWithDescription(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -575,7 +590,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('Lorem Ipsum', $timesheet->getDescription());
|
||||
}
|
||||
|
||||
public function testCreateActionWithDescriptionHtmlInjection()
|
||||
public function testCreateActionWithDescriptionHtmlInjection(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
@@ -602,10 +617,12 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('Some text"><bold>HelloWorld</bold>', $timesheet->getDescription());
|
||||
}
|
||||
|
||||
public function testEditAction()
|
||||
public function testEditAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser();
|
||||
|
||||
$this->setSystemConfiguration('timesheet.rules.long_running_duration', '1440');
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(1);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
@@ -613,6 +630,10 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$timesheets = $this->importFixture($fixture);
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->request($client, '/timesheet/' . $id . '/edit');
|
||||
|
||||
$response = $client->getResponse();
|
||||
@@ -643,7 +664,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals('foo-bar', $timesheet->getDescription());
|
||||
}
|
||||
|
||||
public function testMultiDeleteAction()
|
||||
public function testMultiDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
@@ -670,7 +691,6 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
|
||||
$client->submit($form, [
|
||||
'multi_update_table' => [
|
||||
'action' => $this->createUrl('/timesheet/multi-delete'),
|
||||
'entities' => implode(',', $ids)
|
||||
]
|
||||
]);
|
||||
@@ -681,7 +701,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
self::assertEquals(0, $em->getRepository(Timesheet::class)->count([]));
|
||||
}
|
||||
|
||||
public function testMultiUpdate()
|
||||
public function testMultiUpdate(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -691,6 +711,10 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$fixture->setUser($user);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/timesheet/');
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=multi_update_table]')->form();
|
||||
@@ -707,10 +731,9 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
self::assertFalse($timesheet->isExported());
|
||||
$ids[] = $timesheet->getId();
|
||||
}
|
||||
|
||||
// FIXME
|
||||
$client->submit($form, [
|
||||
'multi_update_table' => [
|
||||
'action' => $this->createUrl('/timesheet/multi-update'),
|
||||
'entities' => implode(',', $ids)
|
||||
]
|
||||
]);
|
||||
@@ -737,7 +760,7 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testDuplicateAction()
|
||||
public function testDuplicateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$dateTime = new DateTimeFactory(new \DateTimeZone('Europe/London'));
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace App\Tests\Controller;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\TimesheetMeta;
|
||||
use App\Entity\User;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Repository\TagRepository;
|
||||
use App\Tests\DataFixtures\TagFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use App\Timesheet\DateTimeFactory;
|
||||
use App\Timesheet\Util;
|
||||
@@ -22,17 +23,17 @@ use App\Timesheet\Util;
|
||||
*/
|
||||
class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
public function testIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/team/timesheet/');
|
||||
}
|
||||
|
||||
public function testIsSecureForRole()
|
||||
public function testIsSecureForRole(): void
|
||||
{
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/team/timesheet/');
|
||||
}
|
||||
|
||||
public function testIndexAction()
|
||||
public function testIndexAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->assertAccessIsGranted($client, '/team/timesheet/');
|
||||
@@ -42,22 +43,18 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->assertHasNoEntriesWithFilter($client);
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action modal-ajax-form' => $this->createUrl('/team/timesheet/export/'),
|
||||
'create-ts modal-ajax-form' => $this->createUrl('/team/timesheet/create'),
|
||||
'create-ts-mu modal-ajax-form' => $this->createUrl('/team/timesheet/create_mu'),
|
||||
'help' => 'https://www.kimai.org/documentation/timesheet.html'
|
||||
'download modal-ajax-form' => $this->createUrl('/team/timesheet/export/'),
|
||||
'create create-ts modal-ajax-form' => $this->createUrl('/team/timesheet/create'),
|
||||
'multi-user create-ts-mu modal-ajax-form' => $this->createUrl('/team/timesheet/create_mu'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIndexActionWithQuery()
|
||||
public function testIndexActionWithQuery(): void
|
||||
{
|
||||
// Switching the user is not allowed for TEAMLEADs but ONLLY for admin and super-admins
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$start = new \DateTime('first day of this month');
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(10);
|
||||
@@ -69,7 +66,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/team/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = ($start)->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime('last day of this month'))->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange($start, new \DateTime('last day of this month'));
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
$client->submit($form, [
|
||||
@@ -89,12 +86,11 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
self::assertEquals(3, $node->count());
|
||||
}
|
||||
|
||||
public function testIndexActionWithSearchTermQuery()
|
||||
public function testIndexActionWithSearchTermQuery(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$start = new \DateTime('first day of this month');
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(5);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
@@ -115,8 +111,6 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/team/timesheet/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = ($start)->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime('last day of this month'))->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('form.searchform')->form();
|
||||
$client->submit($form, [
|
||||
'searchTerm' => 'location:homeoffice foobar',
|
||||
@@ -127,11 +121,10 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->assertDataTableRowCount($client, 'datatable_timesheet_admin', 5);
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
public function testExportAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(7);
|
||||
$fixture->setUser($this->getUserByRole(User::ROLE_USER));
|
||||
@@ -146,14 +139,12 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/team/timesheet/export/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = (new \DateTime('-10 days'))->format('Y-m-d') . DateRangeType::DATE_SPACER . (new \DateTime())->format('Y-m-d');
|
||||
$dateRange = $this->formatDateRange(new \DateTime('-10 days'), new \DateTime());
|
||||
|
||||
$client->submitForm('export-btn-print', [
|
||||
'export' => [
|
||||
'state' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customers' => [],
|
||||
]
|
||||
'state' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customers' => [],
|
||||
]);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -167,7 +158,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(10, \count($result));
|
||||
}
|
||||
|
||||
public function testCreateAction()
|
||||
public function testCreateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->request($client, '/team/timesheet/create');
|
||||
@@ -198,9 +189,14 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->assertNull($timesheet->getFixedRate());
|
||||
}
|
||||
|
||||
public function testCreateForMultipleUsersAction()
|
||||
public function testCreateForMultipleUsersAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->request($client, '/team/timesheet/create_mu');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
@@ -235,16 +231,19 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreateForMultipleUsersActionWithoutUserOrTeam()
|
||||
public function testCreateForMultipleUsersActionWithoutUserOrTeam(): void
|
||||
{
|
||||
$begin = new \DateTime();
|
||||
$end = new \DateTime('+1 hour');
|
||||
$data = [
|
||||
'timesheet_multi_user_edit_form' => [
|
||||
'description' => 'Testing is more fun!',
|
||||
'project' => 1,
|
||||
'activity' => 1,
|
||||
// make sure the default validation for timesheets is applied as well
|
||||
'begin' => (new \DateTime())->format('Y-m-d H:i'),
|
||||
'end' => (new \DateTime('-1 hour'))->format('Y-m-d H:i'),
|
||||
'begin_date' => $this->formatDate($begin),
|
||||
'begin_time' => $this->formatTime($begin),
|
||||
'end_time' => $this->formatTime($end),
|
||||
]
|
||||
];
|
||||
|
||||
@@ -253,14 +252,16 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
'/team/timesheet/create_mu',
|
||||
'form[name=timesheet_multi_user_edit_form]',
|
||||
$data,
|
||||
['#timesheet_multi_user_edit_form_users', '#timesheet_multi_user_edit_form_teams', '#timesheet_multi_user_edit_form_end']
|
||||
['#timesheet_multi_user_edit_form_users', '#timesheet_multi_user_edit_form_teams']
|
||||
);
|
||||
}
|
||||
|
||||
public function testEditAction()
|
||||
public function testEditAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$this->setSystemConfiguration('timesheet.rules.long_running_duration', '1440');
|
||||
|
||||
$user = $this->getUserByRole(User::ROLE_USER);
|
||||
$teamlead = $this->getUserByRole(User::ROLE_TEAMLEAD);
|
||||
$fixture = new TimesheetFixtures();
|
||||
@@ -270,6 +271,10 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$timesheets = $this->importFixture($fixture);
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->request($client, '/team/timesheet/' . $id . '/edit');
|
||||
|
||||
$response = $client->getResponse();
|
||||
@@ -302,11 +307,10 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals($teamlead->getId(), $timesheet->getUser()->getId());
|
||||
}
|
||||
|
||||
public function testMultiDeleteAction()
|
||||
public function testMultiDeleteAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole(User::ROLE_TEAMLEAD);
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(10);
|
||||
@@ -330,7 +334,6 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
|
||||
$client->submit($form, [
|
||||
'multi_update_table' => [
|
||||
'action' => $this->createUrl('/team/timesheet/multi-delete'),
|
||||
'entities' => implode(',', $ids)
|
||||
]
|
||||
]);
|
||||
@@ -341,17 +344,20 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
self::assertEquals(0, $em->getRepository(Timesheet::class)->count([]));
|
||||
}
|
||||
|
||||
public function testMultiUpdate()
|
||||
public function testMultiUpdate(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$user = $this->getUserByRole(User::ROLE_TEAMLEAD);
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture->setAmount(10);
|
||||
$fixture->setUser($user);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$fixture = new TagFixtures();
|
||||
$fixture->importAmount(TagRepository::MAX_AMOUNT_SELECT);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/team/timesheet/');
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=multi_update_table]')->form();
|
||||
@@ -368,10 +374,9 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
self::assertEquals($user->getId(), $timesheet->getUser()->getId());
|
||||
$ids[] = $timesheet->getId();
|
||||
}
|
||||
|
||||
// FIXME
|
||||
$client->submit($form, [
|
||||
'multi_update_table' => [
|
||||
'action' => $this->createUrl('/team/timesheet/multi-update'),
|
||||
'entities' => implode(',', $ids)
|
||||
]
|
||||
]);
|
||||
@@ -402,7 +407,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testDuplicateAction()
|
||||
public function testDuplicateAction(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$dateTime = new DateTimeFactory(new \DateTimeZone('Europe/London'));
|
||||
|
||||
@@ -35,12 +35,11 @@ class UserControllerTest extends ControllerBaseTest
|
||||
$this->assertHasDataTable($client);
|
||||
$this->assertDataTableRowCount($client, 'datatable_user_admin', 7);
|
||||
$this->assertPageActions($client, [
|
||||
'search' => '#',
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/user/export'),
|
||||
'create' => $this->createUrl('/admin/user/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/user'),
|
||||
'help' => 'https://www.kimai.org/documentation/users.html'
|
||||
'create modal-ajax-form' => $this->createUrl('/admin/user/create'),
|
||||
'dropdown-item action-weekly' => $this->createUrl('/reporting/users/week'),
|
||||
'dropdown-item action-monthly' => $this->createUrl('/reporting/users/month'),
|
||||
'dropdown-item action-yearly' => $this->createUrl('/reporting/users/year'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -103,8 +102,6 @@ class UserControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/user/create');
|
||||
$form = $client->getCrawler()->filter('form[name=user_create]')->form();
|
||||
$this->assertTrue($form->has('user_create[create_more]'));
|
||||
$this->assertFalse($form->get('user_create[create_more]')->hasValue());
|
||||
$client->submit($form, [
|
||||
'user_create' => [
|
||||
'username' => $username,
|
||||
@@ -114,36 +111,14 @@ class UserControllerTest extends ControllerBaseTest
|
||||
'enabled' => 1,
|
||||
]
|
||||
]);
|
||||
$this->assertIsRedirect($client, $this->createUrl('/profile/' . urlencode($username) . '/edit'));
|
||||
$client->followRedirect();
|
||||
|
||||
$location = $this->assertIsModalRedirect($client, '/profile/' . urlencode($username) . '/edit');
|
||||
$this->requestPure($client, $location);
|
||||
|
||||
$form = $client->getCrawler()->filter('form[name=user_edit]')->form();
|
||||
$this->assertEquals($username, $form->get('user_edit[alias]')->getValue());
|
||||
}
|
||||
|
||||
public function testCreateActionWithCreateMore()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/user/create');
|
||||
$form = $client->getCrawler()->filter('form[name=user_create]')->form();
|
||||
$this->assertTrue($form->has('user_create[create_more]'));
|
||||
$client->submit($form, [
|
||||
'user_create' => [
|
||||
'username' => 'foobar@example.com',
|
||||
'plainPassword' => ['first' => 'abcdef', 'second' => 'abcdef'],
|
||||
'email' => 'foobar@example.com',
|
||||
'enabled' => 1,
|
||||
'create_more' => true,
|
||||
]
|
||||
]);
|
||||
$this->assertFalse($client->getResponse()->isRedirect());
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$form = $client->getCrawler()->filter('form[name=user_create]')->form();
|
||||
$this->assertTrue($form->has('user_create[create_more]'));
|
||||
$this->assertTrue($form->get('user_create[create_more]')->hasValue());
|
||||
$this->assertEquals(1, $form->get('user_create[create_more]')->getValue());
|
||||
}
|
||||
|
||||
public function testDeleteAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
@@ -27,8 +27,8 @@ class WidgetControllerTest extends ControllerBaseTest
|
||||
$this->assertAccessIsGranted($client, '/widgets/working-time/2020/1');
|
||||
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertStringContainsString('id="PaginatedWorkingTimeChart"', $content);
|
||||
self::assertStringContainsString('id="PaginatedWorkingTimeChartBox"', $content);
|
||||
self::assertStringContainsString('myChart = new Chart', $content);
|
||||
self::assertStringContainsString("KimaiPaginatedBoxWidget.create('#PaginatedWorkingTimeChart');", $content);
|
||||
self::assertStringContainsString("KimaiPaginatedBoxWidget.create('#PaginatedWorkingTimeChartBox');", $content);
|
||||
}
|
||||
}
|
||||
|
||||
47
tests/Controller/WizardControllerTest.php
Normal file
47
tests/Controller/WizardControllerTest.php
Normal 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;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class WizardControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testUnknownWizard()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->request($client, '/wizard/foo');
|
||||
$this->assertRouteNotFound($client);
|
||||
}
|
||||
|
||||
public function testIntroWizard()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/wizard/intro');
|
||||
}
|
||||
|
||||
public function testProfileWizard()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/wizard/profile');
|
||||
}
|
||||
|
||||
public function testDoneWizard()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->assertAccessIsGranted($client, '/wizard/done');
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ use App\Event\CustomerMetaDefinitionEvent;
|
||||
use App\Event\CustomerUpdatePostEvent;
|
||||
use App\Event\CustomerUpdatePreEvent;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use App\Validator\ValidationFailedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
@@ -43,6 +44,9 @@ class CustomerServiceTest extends TestCase
|
||||
|
||||
if ($dispatcher === null) {
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->method('dispatch')->willReturnCallback(function ($event) {
|
||||
return $event;
|
||||
});
|
||||
}
|
||||
|
||||
if ($validator === null) {
|
||||
@@ -51,15 +55,18 @@ class CustomerServiceTest extends TestCase
|
||||
}
|
||||
|
||||
if ($configuration === null) {
|
||||
$configuration = $this->createMock(SystemConfiguration::class);
|
||||
$configuration->method('getCustomerDefaultTimezone')->willReturn('Europe/Vienna');
|
||||
$configuration->method('getCustomerDefaultCountry')->willReturn('IN');
|
||||
$configuration->method('getCustomerDefaultCurrency')->willReturn('RUB');
|
||||
$configuration = SystemConfigurationFactory::createStub([
|
||||
'defaults' => [
|
||||
'customer' => [
|
||||
'timezone' => 'Europe/Vienna',
|
||||
'country' => 'IN',
|
||||
'currency' => 'RUB',
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
$service = new CustomerService($repository, $configuration, $validator, $dispatcher);
|
||||
|
||||
return $service;
|
||||
return new CustomerService($repository, $configuration, $validator, $dispatcher);
|
||||
}
|
||||
|
||||
public function testCannotSavePersistedCustomerAsNew()
|
||||
@@ -88,7 +95,7 @@ class CustomerServiceTest extends TestCase
|
||||
$this->expectException(ValidationFailedException::class);
|
||||
$this->expectExceptionMessage('Validation Failed');
|
||||
|
||||
$sut->saveNewCustomer(new Customer());
|
||||
$sut->saveNewCustomer(new Customer('foo'));
|
||||
}
|
||||
|
||||
public function testUpdateDispatchesEvents()
|
||||
@@ -105,6 +112,8 @@ class CustomerServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
@@ -123,11 +132,13 @@ class CustomerServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
|
||||
$customer = $sut->createNewCustomer();
|
||||
$customer = $sut->createNewCustomer('');
|
||||
|
||||
self::assertInstanceOf(Customer::class, $customer);
|
||||
self::assertEquals('Europe/Vienna', $customer->getTimezone());
|
||||
@@ -146,11 +157,13 @@ class CustomerServiceTest extends TestCase
|
||||
} else {
|
||||
$this->fail('Invalid event received');
|
||||
}
|
||||
|
||||
return $event;
|
||||
});
|
||||
|
||||
$sut = $this->getSut($dispatcher);
|
||||
|
||||
$Customer = new Customer();
|
||||
$Customer = new Customer('foo');
|
||||
$sut->saveNewCustomer($Customer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,26 +19,22 @@ use Faker\Factory;
|
||||
*/
|
||||
final class ActivityFixtures implements TestFixture
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $amount = 0;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isGlobal = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isVisible = null;
|
||||
private int $amount = 0;
|
||||
private bool $isGlobal = false;
|
||||
private ?bool $isVisible = null;
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $callback;
|
||||
/**
|
||||
* @var Project[]
|
||||
* @var array<Project>
|
||||
*/
|
||||
private $projects = [];
|
||||
private array $projects = [];
|
||||
|
||||
public function __construct(int $amount = 0)
|
||||
{
|
||||
$this->amount = $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called prior to persisting the object.
|
||||
@@ -53,9 +49,6 @@ final class ActivityFixtures implements TestFixture
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAmount(): int
|
||||
{
|
||||
return $this->amount;
|
||||
@@ -117,12 +110,10 @@ final class ActivityFixtures implements TestFixture
|
||||
$visible = $this->isVisible;
|
||||
}
|
||||
$activity = new Activity();
|
||||
$activity
|
||||
->setProject($project)
|
||||
->setName($faker->company() . ($visible ? '' : ' (x)'))
|
||||
->setComment($faker->text())
|
||||
->setVisible($visible)
|
||||
;
|
||||
$activity->setProject($project);
|
||||
$activity->setName($faker->company() . ($visible ? '' : ' (x)'));
|
||||
$activity->setComment($faker->text());
|
||||
$activity->setVisible($visible);
|
||||
|
||||
if (null !== $this->callback) {
|
||||
\call_user_func($this->callback, $activity);
|
||||
|
||||
@@ -18,19 +18,18 @@ use Faker\Factory;
|
||||
*/
|
||||
final class CustomerFixtures implements TestFixture
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $amount = 0;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isVisible = null;
|
||||
private int $amount = 0;
|
||||
private ?bool $isVisible = null;
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $callback;
|
||||
|
||||
public function __construct(int $amount = 0)
|
||||
{
|
||||
$this->amount = $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be called prior to persisting the object.
|
||||
*
|
||||
@@ -78,18 +77,15 @@ final class CustomerFixtures implements TestFixture
|
||||
if (null !== $this->isVisible) {
|
||||
$visible = $this->isVisible;
|
||||
}
|
||||
$customer = new Customer();
|
||||
$customer
|
||||
->setCurrency($faker->currencyCode())
|
||||
->setName($faker->company() . ($visible ? '' : ' (x)'))
|
||||
->setAddress($faker->address())
|
||||
->setEmail($faker->safeEmail())
|
||||
->setComment($faker->text())
|
||||
->setNumber('C-' . $faker->ean8())
|
||||
->setCountry($faker->countryCode())
|
||||
->setTimezone($faker->timezone())
|
||||
->setVisible($visible)
|
||||
;
|
||||
$customer = new Customer($faker->company() . ($visible ? '' : ' (x)'));
|
||||
$customer->setCurrency($faker->currencyCode());
|
||||
$customer->setAddress($faker->address());
|
||||
$customer->setEmail($faker->safeEmail());
|
||||
$customer->setComment($faker->text());
|
||||
$customer->setNumber('C-' . $faker->ean8());
|
||||
$customer->setCountry($faker->countryCode());
|
||||
$customer->setTimezone($faker->timezone());
|
||||
$customer->setVisible($visible);
|
||||
|
||||
if (null !== $this->callback) {
|
||||
\call_user_func($this->callback, $customer);
|
||||
|
||||
@@ -29,26 +29,25 @@ class InvoiceTemplateFixtures implements TestFixture
|
||||
$faker = Factory::create();
|
||||
|
||||
$template = new InvoiceTemplate();
|
||||
$template
|
||||
->setName('Invoice')
|
||||
->setTitle('Your company name')
|
||||
->setCompany($faker->company())
|
||||
->setVat(19)
|
||||
->setDueDays(14)
|
||||
->setPaymentTerms(
|
||||
'I would like to thank you for your confidence and will gladly be there for you in the future.' .
|
||||
PHP_EOL .
|
||||
'Please transfer the total amount within 14 days to the given account and use the invoice number ' .
|
||||
'as reference.'
|
||||
)
|
||||
->setAddress(
|
||||
$faker->streetAddress() . PHP_EOL .
|
||||
$faker->city() . ' ' . $faker->postcode() . ', ' . $faker->country() . PHP_EOL .
|
||||
'Phone: ' . $faker->phoneNumber() . PHP_EOL .
|
||||
'Email: ' . $faker->safeEmail()
|
||||
)
|
||||
->setLanguage('en')
|
||||
;
|
||||
$template->setName('Invoice');
|
||||
$template->setTitle('Your company name');
|
||||
$template->setCompany($faker->company());
|
||||
$template->setVat(19);
|
||||
$template->setDueDays(14);
|
||||
$template->setPaymentTerms(
|
||||
'I would like to thank you for your confidence and will gladly be there for you in the future.' .
|
||||
PHP_EOL .
|
||||
'Please transfer the total amount within 14 days to the given account and use the invoice number ' .
|
||||
'as reference.'
|
||||
);
|
||||
$template->setAddress(
|
||||
$faker->streetAddress() . PHP_EOL .
|
||||
$faker->city() . ' ' . $faker->postcode() . ', ' . $faker->country() . PHP_EOL .
|
||||
'Phone: ' . $faker->phoneNumber() . PHP_EOL .
|
||||
'Email: ' . $faker->safeEmail()
|
||||
);
|
||||
$template->setLanguage('en');
|
||||
$template->setRenderer('invoice');
|
||||
|
||||
$manager->persist($template);
|
||||
$manager->flush();
|
||||
|
||||
@@ -19,14 +19,8 @@ use Faker\Factory;
|
||||
*/
|
||||
final class ProjectFixtures implements TestFixture
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $amount = 0;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isVisible = null;
|
||||
private int $amount = 0;
|
||||
private ?bool $isVisible = null;
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
@@ -34,7 +28,12 @@ final class ProjectFixtures implements TestFixture
|
||||
/**
|
||||
* @var Customer[]
|
||||
*/
|
||||
private $customers = [];
|
||||
private array $customers = [];
|
||||
|
||||
public function __construct(int $amount = 0)
|
||||
{
|
||||
$this->amount = $amount;
|
||||
}
|
||||
|
||||
public function getAmount(): int
|
||||
{
|
||||
|
||||
@@ -58,6 +58,15 @@ final class TagFixtures implements TestFixture
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function importAmount(int $amount): void
|
||||
{
|
||||
$tags = [];
|
||||
for ($i = 0; $i <= $amount; $i++) {
|
||||
$tags[] = (string) $i;
|
||||
}
|
||||
$this->setTagArray($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectManager $manager
|
||||
* @return Tag[]
|
||||
|
||||
@@ -106,8 +106,7 @@ final class TeamFixtures implements TestFixture
|
||||
}
|
||||
}
|
||||
|
||||
$team = new Team();
|
||||
$team->setName('Testing: ' . uniqid());
|
||||
$team = new Team('Testing: ' . uniqid());
|
||||
$team->addTeamlead($lead);
|
||||
|
||||
if ($this->addUser) {
|
||||
|
||||
@@ -412,13 +412,12 @@ final class TimesheetFixtures implements TestFixture
|
||||
$rate = Util::calculateRate($hourlyRate, $duration);
|
||||
|
||||
$entry = new Timesheet();
|
||||
$entry
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setDescription($description)
|
||||
->setUser($user)
|
||||
->setRate($rate)
|
||||
->setBegin($start);
|
||||
$entry->setActivity($activity);
|
||||
$entry->setProject($project);
|
||||
$entry->setDescription($description);
|
||||
$entry->setUser($user);
|
||||
$entry->setRate($rate);
|
||||
$entry->setBegin($start);
|
||||
|
||||
if (\count($tagArray) > 0) {
|
||||
foreach ($tagArray as $item) {
|
||||
@@ -439,10 +438,8 @@ final class TimesheetFixtures implements TestFixture
|
||||
}
|
||||
|
||||
if ($setEndDate) {
|
||||
$entry
|
||||
->setEnd($end)
|
||||
->setDuration($duration)
|
||||
;
|
||||
$entry->setEnd($end);
|
||||
$entry->setDuration($duration);
|
||||
}
|
||||
|
||||
return $entry;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Tests\DependencyInjection;
|
||||
|
||||
use App\DependencyInjection\AppExtension;
|
||||
use App\Tests\Mocks\SystemConfigurationFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
@@ -18,10 +19,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
*/
|
||||
class AppExtensionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var AppExtension
|
||||
*/
|
||||
private $extension;
|
||||
private ?AppExtension $extension = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
@@ -29,31 +27,27 @@ class AppExtensionTest extends TestCase
|
||||
$this->extension = new AppExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContainerBuilder
|
||||
*/
|
||||
private function getContainer()
|
||||
private function getContainer(): ContainerBuilder
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('app_locales', 'de|en|tr|zh_CN');
|
||||
$container->setParameter('app_locales', 'de|en|he|tr|zh_CN');
|
||||
$container->setParameter('kernel.project_dir', realpath(__DIR__ . '/../../'));
|
||||
$container->setParameter('security.role_hierarchy.roles', [
|
||||
'ROLE_TEAMLEAD' => ['ROLE_USER'],
|
||||
'ROLE_ADMIN' => ['ROLE_TEAMLEAD'],
|
||||
'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN'],
|
||||
]);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
protected function getMinConfig()
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
protected function getMinConfig(): array
|
||||
{
|
||||
return [
|
||||
'kimai' => [
|
||||
'languages' => [
|
||||
'en' => [
|
||||
'date_type' => 'dd. MM. yyyy',
|
||||
'date' => 'A-m-d'
|
||||
],
|
||||
'tr' => [
|
||||
'date' => 'X-m-d'
|
||||
],
|
||||
],
|
||||
'data_dir' => '/tmp/',
|
||||
'timesheet' => [],
|
||||
'saml' => [
|
||||
@@ -63,169 +57,55 @@ class AppExtensionTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
public function testDefaultValues()
|
||||
public function testDefaultValues(): void
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
// these value list represents the default values with unmerged kimai.yaml
|
||||
$expected = [
|
||||
'kimai.data_dir' => '/tmp/',
|
||||
'kimai.plugin_dir' => realpath(__DIR__ . '/../../') . '/var/plugins',
|
||||
'kimai.languages' => [
|
||||
'en' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
'date_type' => 'dd. MM. yyyy',
|
||||
'date' => 'A-m-d',
|
||||
'date_time' => 'm-d H:i',
|
||||
'duration' => '%%h:%%m h',
|
||||
'time' => 'H:i',
|
||||
'24_hours' => true,
|
||||
'date' => 'M/d/yy',
|
||||
'time' => 'h:mm a',
|
||||
'rtl' => false,
|
||||
],
|
||||
'de' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
'date_type' => 'dd. MM. yyyy',
|
||||
'date' => 'A-m-d',
|
||||
'date_time' => 'm-d H:i',
|
||||
'duration' => '%%h:%%m h',
|
||||
'time' => 'H:i',
|
||||
'24_hours' => true,
|
||||
'date' => 'dd.MM.yy',
|
||||
'time' => 'HH:mm',
|
||||
'rtl' => false,
|
||||
],
|
||||
'he' => [
|
||||
'date' => 'd.M.y',
|
||||
'time' => 'H:mm',
|
||||
'rtl' => true,
|
||||
],
|
||||
'tr' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
// this value if pre-filled by the Configuration object, as "tr" is defined in the min config
|
||||
// and the other languages (not defined in min config) are "only" copied during runtime from "en"
|
||||
'date_type' => 'yyyy-MM-dd',
|
||||
'date' => 'X-m-d',
|
||||
'date_time' => 'm-d H:i',
|
||||
'duration' => '%%h:%%m h',
|
||||
'time' => 'H:i',
|
||||
'24_hours' => true,
|
||||
'date' => 'd.MM.y',
|
||||
'time' => 'HH:mm',
|
||||
'rtl' => false,
|
||||
],
|
||||
'zh_CN' => [
|
||||
'date_time_type' => 'yyyy-MM-dd HH:mm',
|
||||
'date_type' => 'dd. MM. yyyy',
|
||||
'date' => 'A-m-d',
|
||||
'date_time' => 'm-d H:i',
|
||||
'duration' => '%%h:%%m h',
|
||||
'time' => 'H:i',
|
||||
'24_hours' => true,
|
||||
'date' => 'y/M/d',
|
||||
'time' => 'HH:mm',
|
||||
'rtl' => false,
|
||||
],
|
||||
],
|
||||
'kimai.calendar' => [
|
||||
'week_numbers' => true,
|
||||
'day_limit' => 4,
|
||||
'slot_duration' => '00:30:00',
|
||||
'businessHours' => [
|
||||
'days' => [1, 2, 3, 4, 5],
|
||||
'begin' => '08:00',
|
||||
'end' => '20:00',
|
||||
],
|
||||
'visibleHours' => [
|
||||
'begin' => '00:00',
|
||||
'end' => '23:59',
|
||||
],
|
||||
'google' => [
|
||||
'api_key' => null,
|
||||
'sources' => [],
|
||||
],
|
||||
'weekends' => true,
|
||||
'dragdrop_amount' => 10,
|
||||
'dragdrop_data' => false,
|
||||
'title_pattern' => '{activity}',
|
||||
],
|
||||
'kimai.dashboard' => [],
|
||||
'kimai.widgets' => [],
|
||||
'kimai.invoice.documents' => [
|
||||
'var/invoices/',
|
||||
'templates/invoice/renderer/',
|
||||
],
|
||||
'kimai.defaults' => [
|
||||
'timesheet' => [
|
||||
'billable' => true,
|
||||
],
|
||||
'customer' => [
|
||||
'timezone' => null,
|
||||
'country' => 'DE',
|
||||
'currency' => 'EUR',
|
||||
],
|
||||
'user' => [
|
||||
'timezone' => null,
|
||||
'language' => 'en',
|
||||
'theme' => null,
|
||||
'currency' => 'EUR',
|
||||
]
|
||||
],
|
||||
'kimai.theme' => [
|
||||
'active_warning' => 3,
|
||||
'box_color' => 'blue',
|
||||
'select_type' => 'selectpicker',
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => '#3c8dbc',
|
||||
'border_color' => '#3b8bba',
|
||||
'grid_color' => 'rgba(0,0,0,.05)',
|
||||
'height' => '200'
|
||||
],
|
||||
'branding' => [
|
||||
'logo' => null,
|
||||
'mini' => null,
|
||||
'company' => null,
|
||||
'title' => null,
|
||||
'translation' => null,
|
||||
],
|
||||
'autocomplete_chars' => 3,
|
||||
'tags_create' => true,
|
||||
'calendar' => [
|
||||
'background_color' => '#d2d6de',
|
||||
],
|
||||
'colors_limited' => true,
|
||||
'color_choices' => 'Silver|#c0c0c0,Gray|#808080,Black|#000000,Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,Gold|#ffd700,Yellow|#ffff00,Peach|#ffdab9,Khaki|#f0e68c,Olive|#808000,Lime|#00ff00,Jelly|#9acd32,Green|#008000,Teal|#008080,Aqua|#00ffff,LightBlue|#add8e6,DeepSky|#00bfff,Dodger|#1e90ff,Blue|#0000ff,Navy|#000080,Purple|#800080,Fuchsia|#ff00ff,Violet|#ee82ee,Rose|#ffe4e1,Lavender|#E6E6FA',
|
||||
'random_colors' => true,
|
||||
'avatar_url' => false,
|
||||
],
|
||||
'kimai.timesheet' => [
|
||||
'mode' => 'default',
|
||||
'markdown_content' => false,
|
||||
'rounding' => [
|
||||
'default' => [
|
||||
'begin' => 1,
|
||||
'end' => 1,
|
||||
'duration' => 0,
|
||||
'mode' => 'default',
|
||||
'days' => 'monday,tuesday,wednesday,thursday,friday,saturday,sunday'
|
||||
]
|
||||
],
|
||||
'rates' => [],
|
||||
'active_entries' => [
|
||||
'soft_limit' => 1,
|
||||
'hard_limit' => 1,
|
||||
],
|
||||
'rules' => [
|
||||
'allow_future_times' => true,
|
||||
'allow_zero_duration' => true,
|
||||
'allow_overlapping_records' => true,
|
||||
'lockdown_period_start' => null,
|
||||
'lockdown_period_end' => null,
|
||||
'lockdown_grace_period' => null,
|
||||
'allow_overbooking_budget' => true,
|
||||
'lockdown_period_timezone' => null,
|
||||
'break_warning_duration' => 0,
|
||||
'long_running_duration' => 0,
|
||||
],
|
||||
'default_begin' => 'now',
|
||||
'duration_increment' => null,
|
||||
'time_increment' => null,
|
||||
],
|
||||
'kimai.timesheet.rates' => [],
|
||||
'kimai.timesheet.rounding' => [
|
||||
'default' => [
|
||||
'begin' => 1,
|
||||
'end' => 1,
|
||||
'duration' => 0,
|
||||
'mode' => 'default',
|
||||
'days' => 'monday,tuesday,wednesday,thursday,friday,saturday,sunday'
|
||||
]
|
||||
'default' => [
|
||||
'begin' => 1,
|
||||
'end' => 1,
|
||||
'duration' => 0,
|
||||
'mode' => 'default',
|
||||
'days' => 'monday,tuesday,wednesday,thursday,friday,saturday,sunday'
|
||||
]
|
||||
],
|
||||
'kimai.permissions' => [
|
||||
'ROLE_USER' => [],
|
||||
@@ -233,41 +113,46 @@ class AppExtensionTest extends TestCase
|
||||
'ROLE_ADMIN' => [],
|
||||
'ROLE_SUPER_ADMIN' => [],
|
||||
],
|
||||
'kimai.i18n_domains' => []
|
||||
];
|
||||
|
||||
$kimaiLdap = [
|
||||
'activate' => false,
|
||||
'user' => [
|
||||
'baseDn' => null,
|
||||
'filter' => '',
|
||||
'usernameAttribute' => 'uid',
|
||||
'attributesFilter' => '(objectClass=*)',
|
||||
'attributes' => [],
|
||||
],
|
||||
'role' => [
|
||||
'baseDn' => null,
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [],
|
||||
'usernameAttribute' => 'dn',
|
||||
],
|
||||
'connection' => [
|
||||
'baseDn' => null,
|
||||
'host' => null,
|
||||
'port' => 389,
|
||||
'useStartTls' => false,
|
||||
'useSsl' => false,
|
||||
'bindRequiresDn' => true,
|
||||
'accountFilterFormat' => '(&(uid=%s))',
|
||||
'ldap' => [
|
||||
'activate' => false,
|
||||
'user' => [
|
||||
'baseDn' => null,
|
||||
'filter' => '',
|
||||
'usernameAttribute' => 'uid',
|
||||
'attributesFilter' => '(objectClass=*)',
|
||||
'attributes' => [],
|
||||
],
|
||||
'role' => [
|
||||
'baseDn' => null,
|
||||
'nameAttribute' => 'cn',
|
||||
'userDnAttribute' => 'member',
|
||||
'groups' => [],
|
||||
'usernameAttribute' => 'dn',
|
||||
],
|
||||
'connection' => [
|
||||
'baseDn' => null,
|
||||
'host' => null,
|
||||
'port' => 389,
|
||||
'useStartTls' => false,
|
||||
'useSsl' => false,
|
||||
'bindRequiresDn' => true,
|
||||
'accountFilterFormat' => '(&(uid=%s))',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertTrue($container->hasParameter('kimai.config'));
|
||||
|
||||
/** @var array<string, mixed> $config */
|
||||
$config = $container->getParameter('kimai.config');
|
||||
$this->assertArrayHasKey('ldap', $config);
|
||||
$this->assertEquals($kimaiLdap, $config['ldap']);
|
||||
|
||||
foreach (SystemConfigurationFactory::flatten($kimaiLdap) as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $config);
|
||||
$this->assertEquals($value, $config[$key]);
|
||||
}
|
||||
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertTrue($container->hasParameter($key), 'Could not find config: ' . $key);
|
||||
@@ -275,60 +160,7 @@ class AppExtensionTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testAdditionalAuthenticationRoutes()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$adminLte = [
|
||||
'adminlte_registration' => 'foo',
|
||||
'adminlte_password_reset' => 'bar',
|
||||
];
|
||||
|
||||
$container = $this->getContainer();
|
||||
$container->setParameter('admin_lte_theme.routes', $adminLte);
|
||||
|
||||
$this->extension->load($minConfig, $container);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'adminlte_registration' => 'foo',
|
||||
'adminlte_password_reset' => 'bar',
|
||||
],
|
||||
$container->getParameter('admin_lte_theme.routes')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation Configuration "kimai.timesheet.duration_only" is deprecated, please remove it
|
||||
* @group legacy
|
||||
*/
|
||||
public function testDurationOnlyDeprecationIsTriggered()
|
||||
{
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Found ambiguous configuration: remove "kimai.timesheet.duration_only" and set "kimai.timesheet.mode" instead.');
|
||||
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['timesheet']['duration_only'] = true;
|
||||
$minConfig['kimai']['timesheet']['mode'] = 'punch';
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation Changing the plugin directory via "kimai.plugin_dir" is not supported since 1.9
|
||||
* @group legacy
|
||||
*/
|
||||
public function testChangingPluginsIsIgnoredAndTriggersDeprecation()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['plugin_dir'] = '/tmp/';
|
||||
$expected = realpath(__DIR__ . '/../../') . '/var/plugins';
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$this->assertEquals($expected, $container->getParameter('kimai.plugin_dir'), 'Invalid config: kimai.plugin_dir');
|
||||
}
|
||||
|
||||
public function testLdapDefaultValues()
|
||||
public function testLdapDefaultValues(): void
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
@@ -347,16 +179,15 @@ class AppExtensionTest extends TestCase
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$config = $container->getParameter('kimai.config');
|
||||
$ldapConfig = $config['ldap'];
|
||||
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('(..........)', $ldapConfig['user']['filter']);
|
||||
$this->assertEquals('xxx', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('lkhiuzhkj', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(uid=%s)', $ldapConfig['connection']['accountFilterFormat']);
|
||||
$this->assertEquals('123123123', $config['ldap.user.baseDn']);
|
||||
$this->assertEquals('(..........)', $config['ldap.user.filter']);
|
||||
$this->assertEquals('xxx', $config['ldap.user.usernameAttribute']);
|
||||
$this->assertEquals('lkhiuzhkj', $config['ldap.connection.baseDn']);
|
||||
$this->assertEquals('(uid=%s)', $config['ldap.connection.accountFilterFormat']);
|
||||
}
|
||||
|
||||
public function testLdapFallbackValue()
|
||||
public function testLdapFallbackValue(): void
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
@@ -372,16 +203,15 @@ class AppExtensionTest extends TestCase
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$config = $container->getParameter('kimai.config');
|
||||
$ldapConfig = $config['ldap'];
|
||||
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('xxx', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('123123123', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(&(xxx=%s))', $ldapConfig['connection']['accountFilterFormat']);
|
||||
$this->assertEquals('', $ldapConfig['user']['filter']);
|
||||
$this->assertEquals('123123123', $config['ldap.user.baseDn']);
|
||||
$this->assertEquals('xxx', $config['ldap.user.usernameAttribute']);
|
||||
$this->assertEquals('123123123', $config['ldap.connection.baseDn']);
|
||||
$this->assertEquals('(&(xxx=%s))', $config['ldap.connection.accountFilterFormat']);
|
||||
$this->assertEquals('', $config['ldap.user.filter']);
|
||||
}
|
||||
|
||||
public function testLdapMoreFallbackValue()
|
||||
public function testLdapMoreFallbackValue(): void
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['ldap'] = [
|
||||
@@ -399,57 +229,30 @@ class AppExtensionTest extends TestCase
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$config = $container->getParameter('kimai.config');
|
||||
$ldapConfig = $config['ldap'];
|
||||
|
||||
$this->assertEquals('123123123', $ldapConfig['user']['baseDn']);
|
||||
$this->assertEquals('zzzz', $ldapConfig['user']['usernameAttribute']);
|
||||
$this->assertEquals('7658765', $ldapConfig['connection']['baseDn']);
|
||||
$this->assertEquals('(&(&(objectClass=inetOrgPerson))(zzzz=%s))', $ldapConfig['connection']['accountFilterFormat']);
|
||||
$this->assertEquals('(&(objectClass=inetOrgPerson))', $ldapConfig['user']['filter']);
|
||||
$this->assertEquals('123123123', $config['ldap.user.baseDn']);
|
||||
$this->assertEquals('zzzz', $config['ldap.user.usernameAttribute']);
|
||||
$this->assertEquals('7658765', $config['ldap.connection.baseDn']);
|
||||
$this->assertEquals('(&(&(objectClass=inetOrgPerson))(zzzz=%s))', $config['ldap.connection.accountFilterFormat']);
|
||||
$this->assertEquals('(&(objectClass=inetOrgPerson))', $config['ldap.user.filter']);
|
||||
}
|
||||
|
||||
public function testTranslationOverwritesEmpty()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$config = $container->getParameter('kimai.i18n_domains');
|
||||
$this->assertEquals([], $config);
|
||||
}
|
||||
|
||||
public function testTranslationOverwrites()
|
||||
{
|
||||
$minConfig = $this->getMinConfig();
|
||||
$minConfig['kimai']['industry'] = [
|
||||
'translation' => 'xxxx',
|
||||
];
|
||||
$minConfig['kimai']['theme'] = [
|
||||
'branding' => [
|
||||
'translation' => 'yyyy',
|
||||
]
|
||||
];
|
||||
|
||||
$this->extension->load($minConfig, $container = $this->getContainer());
|
||||
|
||||
$config = $container->getParameter('kimai.i18n_domains');
|
||||
// oder is important, theme/installation specific translations win
|
||||
$this->assertEquals(['yyyy', 'xxxx'], $config);
|
||||
}
|
||||
|
||||
public function testWithBundleConfiguration()
|
||||
public function testWithBundleConfiguration(): void
|
||||
{
|
||||
$bundleConfig = [
|
||||
'foo-bundle' => ['test'],
|
||||
'foo-bundle' => [
|
||||
'bar' => 'test'
|
||||
],
|
||||
];
|
||||
$container = $this->getContainer();
|
||||
$container->setParameter('kimai.bundles.config', $bundleConfig);
|
||||
|
||||
$this->extension->load($this->getMinConfig(), $container);
|
||||
$config = $container->getParameter('kimai.config');
|
||||
self::assertEquals(['test'], $config['foo-bundle']);
|
||||
self::assertEquals('test', $config['foo-bundle.bar']);
|
||||
}
|
||||
|
||||
public function testWithBundleConfigurationFailsOnDuplicatedKey()
|
||||
public function testWithBundleConfigurationFailsOnDuplicatedKey(): void
|
||||
{
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Invalid bundle configuration "timesheet" found, skipping');
|
||||
@@ -463,7 +266,7 @@ class AppExtensionTest extends TestCase
|
||||
$this->extension->load($this->getMinConfig(), $container);
|
||||
}
|
||||
|
||||
public function testWithBundleConfigurationFailsOnNonArray()
|
||||
public function testWithBundleConfigurationFailsOnNonArray(): void
|
||||
{
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Invalid bundle configuration found, skipping all bundle configuration');
|
||||
|
||||
@@ -51,21 +51,6 @@ class ConfigurationTest extends TestCase
|
||||
$this->assertConfig($this->getMinConfig('sdfsdfsdfds'), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedDeprecation Changing the plugin directory via "kimai.plugin_dir" is not supported since 1.9
|
||||
* @group legacy
|
||||
*/
|
||||
public function testValidatePluginDir()
|
||||
{
|
||||
$config = $this->getMinConfig();
|
||||
$config['plugin_dir'] = '/tmp/';
|
||||
|
||||
$finalizedConfig = $this->getCompiledConfig($config);
|
||||
$finalizedConfig['plugin_dir'] = '/tmp/';
|
||||
|
||||
$this->assertConfig($config, $finalizedConfig);
|
||||
}
|
||||
|
||||
public function testValidateLdapConfigUserBaseDn()
|
||||
{
|
||||
$this->expectException(InvalidConfigurationException::class);
|
||||
@@ -291,7 +276,6 @@ class ConfigurationTest extends TestCase
|
||||
],
|
||||
'rates' => [],
|
||||
'active_entries' => [
|
||||
'soft_limit' => 1,
|
||||
'hard_limit' => 1,
|
||||
],
|
||||
'rules' => [
|
||||
@@ -304,10 +288,11 @@ class ConfigurationTest extends TestCase
|
||||
'allow_overbooking_budget' => true,
|
||||
'lockdown_period_timezone' => null,
|
||||
'break_warning_duration' => 0,
|
||||
'long_running_duration' => 0,
|
||||
'long_running_duration' => 480,
|
||||
'require_activity' => true,
|
||||
],
|
||||
'duration_increment' => null,
|
||||
'time_increment' => null,
|
||||
'duration_increment' => 15,
|
||||
'time_increment' => 15,
|
||||
],
|
||||
'user' => [
|
||||
'registration' => false,
|
||||
@@ -323,7 +308,6 @@ class ConfigurationTest extends TestCase
|
||||
0 => 'var/invoices/',
|
||||
1 => 'templates/invoice/renderer/',
|
||||
],
|
||||
'simple_form' => false,
|
||||
'number_format' => '{Y}/{cy,3}',
|
||||
],
|
||||
'export' => [
|
||||
@@ -334,19 +318,11 @@ class ConfigurationTest extends TestCase
|
||||
1 => 'templates/export/renderer/',
|
||||
],
|
||||
],
|
||||
'languages' => [],
|
||||
'calendar' => [
|
||||
'week_numbers' => true,
|
||||
'day_limit' => 4,
|
||||
'slot_duration' => '00:30:00',
|
||||
'businessHours' => [
|
||||
'days' => [
|
||||
0 => 1,
|
||||
1 => 2,
|
||||
2 => 3,
|
||||
3 => 4,
|
||||
4 => 5,
|
||||
],
|
||||
'begin' => '08:00',
|
||||
'end' => '20:00',
|
||||
],
|
||||
@@ -360,47 +336,23 @@ class ConfigurationTest extends TestCase
|
||||
],
|
||||
],
|
||||
'weekends' => true,
|
||||
'dragdrop_amount' => 10,
|
||||
'dragdrop_amount' => 5,
|
||||
'dragdrop_data' => false,
|
||||
'title_pattern' => '{activity}',
|
||||
],
|
||||
'theme' => [
|
||||
'active_warning' => 3,
|
||||
'box_color' => 'blue',
|
||||
'select_type' => 'selectpicker',
|
||||
'show_about' => true,
|
||||
'chart' => [
|
||||
'background_color' => '#3c8dbc',
|
||||
'border_color' => '#3b8bba',
|
||||
'grid_color' => 'rgba(0,0,0,.05)',
|
||||
'height' => '200',
|
||||
],
|
||||
'branding' => [
|
||||
'logo' => null,
|
||||
'mini' => null,
|
||||
'company' => null,
|
||||
'title' => null,
|
||||
'translation' => null,
|
||||
],
|
||||
'autocomplete_chars' => 3,
|
||||
'tags_create' => true,
|
||||
'calendar' => [
|
||||
'background_color' => '#d2d6de'
|
||||
],
|
||||
'colors_limited' => true,
|
||||
'color_choices' => 'Silver|#c0c0c0,Gray|#808080,Black|#000000,Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,Gold|#ffd700,Yellow|#ffff00,Peach|#ffdab9,Khaki|#f0e68c,Olive|#808000,Lime|#00ff00,Jelly|#9acd32,Green|#008000,Teal|#008080,Aqua|#00ffff,LightBlue|#add8e6,DeepSky|#00bfff,Dodger|#1e90ff,Blue|#0000ff,Navy|#000080,Purple|#800080,Fuchsia|#ff00ff,Violet|#ee82ee,Rose|#ffe4e1,Lavender|#E6E6FA',
|
||||
'random_colors' => true,
|
||||
'avatar_url' => false,
|
||||
],
|
||||
'industry' => [
|
||||
'translation' => null,
|
||||
],
|
||||
'dashboard' => [],
|
||||
'widgets' => [],
|
||||
'defaults' => [
|
||||
'timesheet' => [
|
||||
'billable' => true,
|
||||
],
|
||||
'customer' => [
|
||||
'timezone' => null,
|
||||
'country' => 'DE',
|
||||
@@ -409,7 +361,7 @@ class ConfigurationTest extends TestCase
|
||||
'user' => [
|
||||
'timezone' => null,
|
||||
'language' => 'en',
|
||||
'theme' => null,
|
||||
'theme' => 'default',
|
||||
'currency' => 'EUR',
|
||||
],
|
||||
],
|
||||
@@ -460,6 +412,7 @@ class ConfigurationTest extends TestCase
|
||||
'connection' => [
|
||||
'organization' => []
|
||||
],
|
||||
'provider' => null,
|
||||
],
|
||||
'company' => [
|
||||
'financial_year' => null,
|
||||
@@ -472,6 +425,12 @@ class ConfigurationTest extends TestCase
|
||||
'project' => [
|
||||
'copy_teams_on_create' => false,
|
||||
],
|
||||
'activity' => [
|
||||
'allow_inline_create' => false,
|
||||
],
|
||||
'customer' => [
|
||||
'number_format' => '{cc,4}',
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertConfig($this->getMinConfig(), $fullDefaultConfig);
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace App\Tests\Doctrine;
|
||||
|
||||
use App\Doctrine\UTCDateTimeType;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\MySqlPlatform;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -110,7 +110,7 @@ class UTCDateTimeTypeTest extends TestCase
|
||||
public function getPlatforms()
|
||||
{
|
||||
return [
|
||||
[new MySqlPlatform()],
|
||||
[new MySQLPlatform()],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class ActivityRateTest extends TestCase
|
||||
|
||||
$user = new User();
|
||||
$user->setAlias('foo');
|
||||
$user->setUsername('bar');
|
||||
$user->setUserIdentifier('bar');
|
||||
self::assertInstanceOf(ActivityRate::class, $sut->setUser($user));
|
||||
self::assertSame($user, $sut->getUser());
|
||||
$sut->setUser(null);
|
||||
|
||||
@@ -16,7 +16,6 @@ use App\Entity\Project;
|
||||
use App\Entity\Team;
|
||||
use App\Export\Spreadsheet\ColumnDefinition;
|
||||
use App\Export\Spreadsheet\Extractor\AnnotationExtractor;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
@@ -40,7 +39,6 @@ class ActivityTest extends AbstractEntityTest
|
||||
$this->assertInstanceOf(Collection::class, $sut->getMetaFields());
|
||||
$this->assertEquals(0, $sut->getMetaFields()->count());
|
||||
$this->assertNull($sut->getMetaField('foo'));
|
||||
$this->assertNull($sut->getMetaFieldValue('foo'));
|
||||
$this->assertInstanceOf(Collection::class, $sut->getTeams());
|
||||
}
|
||||
|
||||
@@ -95,7 +93,6 @@ class ActivityTest extends AbstractEntityTest
|
||||
self::assertSame($result, $meta);
|
||||
self::assertEquals('test', $result->getType());
|
||||
self::assertEquals('bar2', $result->getValue());
|
||||
self::assertEquals('bar2', $sut->getMetaFieldValue('foo'));
|
||||
|
||||
$meta2 = new ActivityMeta();
|
||||
$meta2->setName('foo')->setValue('bar')->setType('test2');
|
||||
@@ -106,7 +103,6 @@ class ActivityTest extends AbstractEntityTest
|
||||
$result = $sut->getMetaField('foo');
|
||||
self::assertSame($result, $meta);
|
||||
self::assertEquals('test2', $result->getType());
|
||||
self::assertEquals('bar2', $sut->getMetaFieldValue('foo'));
|
||||
|
||||
$sut->setMetaField((new ActivityMeta())->setName('blub')->setIsVisible(true));
|
||||
$sut->setMetaField((new ActivityMeta())->setName('blab')->setIsVisible(true));
|
||||
@@ -117,7 +113,7 @@ class ActivityTest extends AbstractEntityTest
|
||||
public function testTeams()
|
||||
{
|
||||
$sut = new Activity();
|
||||
$team = new Team();
|
||||
$team = new Team('foo');
|
||||
self::assertEmpty($sut->getTeams());
|
||||
self::assertEmpty($team->getActivities());
|
||||
|
||||
@@ -128,7 +124,7 @@ class ActivityTest extends AbstractEntityTest
|
||||
self::assertSame($sut, $team->getActivities()[0]);
|
||||
|
||||
// test remove unknown team doesn't do anything
|
||||
$sut->removeTeam(new Team());
|
||||
$sut->removeTeam(new Team('foo'));
|
||||
self::assertCount(1, $sut->getTeams());
|
||||
self::assertCount(1, $team->getActivities());
|
||||
|
||||
@@ -139,23 +135,23 @@ class ActivityTest extends AbstractEntityTest
|
||||
|
||||
public function testExportAnnotations()
|
||||
{
|
||||
$sut = new AnnotationExtractor(new AnnotationReader());
|
||||
$sut = new AnnotationExtractor();
|
||||
|
||||
$columns = $sut->extract(Activity::class);
|
||||
|
||||
self::assertIsArray($columns);
|
||||
|
||||
$expected = [
|
||||
['label.id', 'integer'],
|
||||
['label.name', 'string'],
|
||||
['label.project', 'string'],
|
||||
['label.budget', 'float'],
|
||||
['label.timeBudget', 'duration'],
|
||||
['label.budgetType', 'string'],
|
||||
['label.color', 'string'],
|
||||
['label.visible', 'boolean'],
|
||||
['label.comment', 'string'],
|
||||
['label.billable', 'boolean'],
|
||||
['id', 'integer'],
|
||||
['name', 'string'],
|
||||
['project', 'string'],
|
||||
['budget', 'float'],
|
||||
['timeBudget', 'duration'],
|
||||
['budgetType', 'string'],
|
||||
['color', 'string'],
|
||||
['visible', 'boolean'],
|
||||
['comment', 'string'],
|
||||
['billable', 'boolean'],
|
||||
];
|
||||
|
||||
self::assertCount(\count($expected), $columns);
|
||||
@@ -190,7 +186,7 @@ class ActivityTest extends AbstractEntityTest
|
||||
|
||||
$sut->setProject($project);
|
||||
|
||||
$team = new Team();
|
||||
$team = new Team('foo');
|
||||
$sut->addTeam($team);
|
||||
|
||||
$meta = new ActivityMeta();
|
||||
@@ -205,7 +201,6 @@ class ActivityTest extends AbstractEntityTest
|
||||
foreach ($sut->getMetaFields() as $metaField) {
|
||||
$cloneMeta = $clone->getMetaField($metaField->getName());
|
||||
self::assertEquals($cloneMeta->getValue(), $metaField->getValue());
|
||||
self::assertEquals($metaField->getValue(), $clone->getMetaFieldValue($metaField->getName()));
|
||||
}
|
||||
self::assertEquals($clone->getBudget(), $sut->getBudget());
|
||||
self::assertEquals($clone->getTimeBudget(), $sut->getTimeBudget());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user