Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)

This commit is contained in:
Kevin Papst
2022-12-31 21:19:55 +01:00
committed by GitHub
parent 95e06746bd
commit 90a0fd8a22
2164 changed files with 83700 additions and 86426 deletions

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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'
],

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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());
}
}

View File

@@ -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()

View File

@@ -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

View 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);
}
}

View File

@@ -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);

View File

@@ -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');

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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'));
}
}

View File

@@ -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());

View File

@@ -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'
]);
}
}

View File

@@ -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')]");

View File

@@ -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')]");

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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());
}
}

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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);
}
}

View File

@@ -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',
]);

View File

@@ -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);

View File

@@ -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',
]
],
];

View File

@@ -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'));

View File

@@ -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)
]
]);

View File

@@ -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());
}
}

View File

@@ -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'));

View File

@@ -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'));

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\User;
/**
* @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');
}
}