added supervisor setting for user (#4251)

This commit is contained in:
Kevin Papst
2023-08-20 13:18:35 +02:00
committed by GitHub
parent 9baa477bed
commit 3e61e0dce6
28 changed files with 358 additions and 612 deletions

View File

@@ -39,4 +39,22 @@
fieldset:empty { fieldset:empty {
display: none; display: none;
}
/* e.g. the work contract settings is user profile render rows with mb-3 and the last row doesn't need that */
fieldset > .mb-3.row:last-child {
margin-bottom: 0!important;
}
.form-fieldset-light {
border-radius: 0;
margin-bottom: 0;
border-top: none;
border-right: none;
border-left: none;
background-color: unset;
}
.form-fieldset-light:last-child {
border:none;
} }

View File

@@ -95,7 +95,7 @@ kimai:
TIMESHEET: ['view_own_timesheet','start_own_timesheet','stop_own_timesheet','create_own_timesheet','edit_own_timesheet','export_own_timesheet','delete_own_timesheet','weekly_own_timesheet'] TIMESHEET: ['view_own_timesheet','start_own_timesheet','stop_own_timesheet','create_own_timesheet','edit_own_timesheet','export_own_timesheet','delete_own_timesheet','weekly_own_timesheet']
TIMESHEET_OTHER: ['view_other_timesheet','start_other_timesheet','stop_other_timesheet','create_other_timesheet','edit_other_timesheet','export_other_timesheet','delete_other_timesheet'] TIMESHEET_OTHER: ['view_other_timesheet','start_other_timesheet','stop_other_timesheet','create_other_timesheet','edit_other_timesheet','export_other_timesheet','delete_other_timesheet']
PROFILE: ['view_own_profile','edit_own_profile','password_own_profile','preferences_own_profile','api-token_own_profile'] PROFILE: ['view_own_profile','edit_own_profile','password_own_profile','preferences_own_profile','api-token_own_profile']
PROFILE_OTHER: ['view_other_profile','edit_other_profile','password_other_profile','roles_other_profile','preferences_other_profile','api-token_other_profile','teams_other_profile'] PROFILE_OTHER: ['view_other_profile','edit_other_profile','password_other_profile','roles_other_profile','preferences_other_profile','api-token_other_profile','teams_other_profile','supervisor_other_profile']
TAGS: ['view_tag','manage_tag','create_tag','delete_tag'] TAGS: ['view_tag','manage_tag','create_tag','delete_tag']
USER: ['view_user','create_user','delete_user','role_permissions'] USER: ['view_user','create_user','delete_user','role_permissions']
RATE: ['view_rate_own_timesheet','edit_rate_own_timesheet'] RATE: ['view_rate_own_timesheet','edit_rate_own_timesheet']
@@ -119,7 +119,7 @@ kimai:
ROLE_USER: ['view_team_member','time_team_project','create_tag','view_reporting'] ROLE_USER: ['view_team_member','time_team_project','create_tag','view_reporting']
ROLE_TEAMLEAD: ['view_rate_own_timesheet','view_rate_other_timesheet','hourly-rate_own_profile','view_team_member'] ROLE_TEAMLEAD: ['view_rate_own_timesheet','view_rate_other_timesheet','hourly-rate_own_profile','view_team_member']
ROLE_ADMIN: ['hourly-rate_own_profile','edit_exported_timesheet','teams_own_profile','view_team_member','view_all_data','contract_other_profile'] ROLE_ADMIN: ['hourly-rate_own_profile','edit_exported_timesheet','teams_own_profile','view_team_member','view_all_data','contract_other_profile']
ROLE_SUPER_ADMIN: ['hourly-rate_own_profile','hourly-rate_other_profile','roles_own_profile','system_information','system_configuration','plugins','edit_exported_timesheet','teams_own_profile','view_team_member','upload_invoice_template','view_all_data','contract_other_profile'] ROLE_SUPER_ADMIN: ['hourly-rate_own_profile','hourly-rate_other_profile','roles_own_profile','supervisor_own_profile','system_information','system_configuration','plugins','edit_exported_timesheet','teams_own_profile','view_team_member','upload_invoice_template','view_all_data','contract_other_profile']
# -------------------------------------------------------------------------------- # --------------------------------------------------------------------------------

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* @version 2.0.31
*/
final class Version20230819090536 extends AbstractMigration
{
public function getDescription(): string
{
return 'Adds the supervisor columns to the user-table';
}
public function up(Schema $schema): void
{
$table = $schema->getTable('kimai2_users');
$table->addColumn('supervisor_id', 'integer', ['length' => 11, 'notnull' => false, 'default' => null]);
$table->addForeignKeyConstraint('kimai2_users', ['supervisor_id'], ['id'], ['onDelete' => 'SET NULL'], 'FK_B9AC5BCE19E9AC5F');
$table->addIndex(['supervisor_id'], 'IDX_B9AC5BCE19E9AC5F');
}
public function down(Schema $schema): void
{
$table = $schema->getTable('kimai2_users');
$table->removeForeignKey('FK_B9AC5BCE19E9AC5F');
$table->dropIndex('IDX_B9AC5BCE19E9AC5F');
$table->dropColumn('supervisor_id');
}
public function isTransactional(): bool
{
return false;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
"/build/app.9acfcfc8.js" "/build/app.9acfcfc8.js"
], ],
"css": [ "css": [
"/build/app.eb575c90.css" "/build/app.405a508c.css"
] ]
}, },
"export-pdf": { "export-pdf": {
@@ -64,7 +64,7 @@
"integrity": { "integrity": {
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV", "/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
"/build/app.9acfcfc8.js": "sha384-tzbF0uEmHghIo+D5IuKarLYkwB3AyrLMTEFqR5mqaXCP5VSOWx3t+ZfD81kd80T+", "/build/app.9acfcfc8.js": "sha384-tzbF0uEmHghIo+D5IuKarLYkwB3AyrLMTEFqR5mqaXCP5VSOWx3t+ZfD81kd80T+",
"/build/app.eb575c90.css": "sha384-CT92SMS+AjbulH+hT+pOq4jicscpVYpl/acq388EdqruZe26icGl9sNq9xaYXH8l", "/build/app.405a508c.css": "sha384-D8LqbrX2x0UHuxQasN8nli7f/tzVYlNeJleATqXW1o0Pjwycyo0t1jKRe1h1MeIV",
"/build/export-pdf.d367a32e.js": "sha384-Z5baqnzjI636nYFs4g63ViIKBZKRW4Jhv/7PQmTEQlqhfA7eK0vUMUtiyy0R5A9u", "/build/export-pdf.d367a32e.js": "sha384-Z5baqnzjI636nYFs4g63ViIKBZKRW4Jhv/7PQmTEQlqhfA7eK0vUMUtiyy0R5A9u",
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg", "/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
"/build/invoice.2604495e.js": "sha384-D6JvhGSqlx7z72b/qD3nF3QDXPy+XsCSRGtWfs1icjDKOcd2UzuXwuSa/E1Fg2TJ", "/build/invoice.2604495e.js": "sha384-D6JvhGSqlx7z72b/qD3nF3QDXPy+XsCSRGtWfs1icjDKOcd2UzuXwuSa/E1Fg2TJ",

View File

@@ -1,5 +1,5 @@
{ {
"build/app.css": "/build/app.eb575c90.css", "build/app.css": "/build/app.405a508c.css",
"build/app.js": "/build/app.9acfcfc8.js", "build/app.js": "/build/app.9acfcfc8.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css", "build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
"build/export-pdf.js": "/build/export-pdf.d367a32e.js", "build/export-pdf.js": "/build/export-pdf.d367a32e.js",

View File

@@ -197,6 +197,7 @@ final class UserController extends BaseApiController
'include_roles' => $this->isGranted('roles', $profile), 'include_roles' => $this->isGranted('roles', $profile),
'include_active_flag' => ($profile->getId() !== $this->getUser()->getId()), 'include_active_flag' => ($profile->getId() !== $this->getUser()->getId()),
'include_preferences' => $this->isGranted('preferences', $profile), 'include_preferences' => $this->isGranted('preferences', $profile),
'include_supervisor' => $this->isGranted('supervisor', $profile),
]); ]);
$form->setData($profile); $form->setData($profile);

View File

@@ -316,7 +316,8 @@ final class ProfileController extends AbstractController
'action' => $this->generateUrl('user_profile_edit', ['username' => $user->getUserIdentifier()]), 'action' => $this->generateUrl('user_profile_edit', ['username' => $user->getUserIdentifier()]),
'method' => 'POST', 'method' => 'POST',
'include_active_flag' => ($user->getId() !== $this->getUser()->getId()), 'include_active_flag' => ($user->getId() !== $this->getUser()->getId()),
'include_preferences' => false, 'include_preferences' => true,
'include_supervisor' => $this->isGranted('supervisor', $user),
] ]
); );
} }

View File

@@ -233,6 +233,7 @@ final class UserController extends AbstractController
'method' => 'POST', 'method' => 'POST',
'include_active_flag' => true, 'include_active_flag' => true,
'include_preferences' => true, 'include_preferences' => true,
'include_supervisor' => $this->isGranted('supervisor_other_profile'),
'include_teams' => $this->isGranted('teams_other_profile'), 'include_teams' => $this->isGranted('teams_other_profile'),
'include_roles' => $this->isGranted('roles_other_profile'), 'include_roles' => $this->isGranted('roles_other_profile'),
]); ]);

View File

@@ -128,15 +128,15 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
* *
* @var Collection<UserPreference>|null * @var Collection<UserPreference>|null
*/ */
#[ORM\OneToMany(targetEntity: 'App\Entity\UserPreference', mappedBy: 'user', cascade: ['persist'])] #[ORM\OneToMany(targetEntity: UserPreference::class, mappedBy: 'user', cascade: ['persist'])]
private ?Collection $preferences = null; private ?Collection $preferences = null;
/** /**
* List of all team memberships. * List of all team memberships.
* *
* @var Collection<TeamMember> * @var Collection<TeamMember>
*/ */
#[ORM\OneToMany(targetEntity: 'App\Entity\TeamMember', mappedBy: 'user', fetch: 'LAZY', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: TeamMember::class, mappedBy: 'user', fetch: 'LAZY', cascade: ['persist'], orphanRemoval: true)]
#[ORM\JoinColumn(onDelete: 'CASCADE')] #[ORM\JoinColumn(onDelete: 'CASCADE', nullable: false)]
#[Assert\NotNull] #[Assert\NotNull]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['User_Entity'])] #[Serializer\Groups(['User_Entity'])]
@@ -216,6 +216,12 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
private bool $totpEnabled = false; private bool $totpEnabled = false;
#[ORM\Column(name: 'system_account', type: 'boolean', nullable: false, options: ['default' => false])] #[ORM\Column(name: 'system_account', type: 'boolean', nullable: false, options: ['default' => false])]
private bool $systemAccount = false; private bool $systemAccount = false;
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(onDelete: 'SET NULL', nullable: true)]
#[Serializer\Expose]
#[Serializer\Groups(['User_Entity'])]
#[OA\Property(ref: '#/components/schemas/User')]
private ?User $supervisor = null;
use ColorTrait; use ColorTrait;
@@ -1281,4 +1287,24 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
default => throw new \Exception('Unknown day: ' . $dateTime->format('Y-m-d')) default => throw new \Exception('Unknown day: ' . $dateTime->format('Y-m-d'))
}; };
} }
public function isWorkDay(\DateTimeInterface $dateTime): bool
{
return $this->getWorkHoursForDay($dateTime) > 0;
}
public function hasSupervisor(): bool
{
return $this->supervisor !== null;
}
public function getSupervisor(): ?User
{
return $this->supervisor;
}
public function setSupervisor(?User $supervisor): void
{
$this->supervisor = $supervisor;
}
} }

View File

@@ -16,18 +16,18 @@ use Symfony\Component\Form\FormEvents;
trait ColorTrait trait ColorTrait
{ {
protected function addColor(FormBuilderInterface $builder): void protected function addColor(FormBuilderInterface $builder, bool $required = false): void
{ {
$builder $builder
->add('color', ColorChoiceType::class, [ ->add('color', ColorChoiceType::class, [
'required' => false, 'required' => $required,
]) ])
; ;
// this code exists only for backward compatibility // this code exists only for backward compatibility
$builder->addEventListener( $builder->addEventListener(
FormEvents::PRE_SET_DATA, FormEvents::PRE_SET_DATA,
function (FormEvent $event) { function (FormEvent $event) use ($required) {
if (!$event->getForm()->getConfig()->hasOption('choices')) { if (!$event->getForm()->getConfig()->hasOption('choices')) {
return; return;
} }
@@ -42,7 +42,7 @@ trait ColorTrait
} }
$event->getForm()->add('color', ColorChoiceType::class, [ $event->getForm()->add('color', ColorChoiceType::class, [
'required' => false, 'required' => $required,
'choices' => $choices, 'choices' => $choices,
]); ]);
} }

View File

@@ -23,47 +23,45 @@ class UserCreateType extends UserEditType
{ {
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder->add('username', null, [
->add('username', null, [ 'label' => 'username',
'label' => 'username', 'required' => true,
'required' => true, 'attr' => [
'attr' => [ 'autofocus' => 'autofocus'
'autofocus' => 'autofocus' ],
], ]);
])
->add('plainPassword', RepeatedType::class, [ $builder->add('plainPassword', RepeatedType::class, [
'required' => true, 'required' => true,
'type' => PasswordType::class, 'type' => PasswordType::class,
'first_options' => [ 'first_options' => [
'label' => 'password', 'label' => 'password',
'attr' => ['autocomplete' => 'new-password'], 'attr' => ['autocomplete' => 'new-password'],
'block_prefix' => 'secret' 'block_prefix' => 'secret'
], ],
'second_options' => [ 'second_options' => [
'label' => 'password_repeat', 'label' => 'password_repeat',
'attr' => ['autocomplete' => 'new-password'], 'attr' => ['autocomplete' => 'new-password'],
'block_prefix' => 'secret' 'block_prefix' => 'secret'
], ],
]); ]);
parent::buildForm($builder, $options); parent::buildForm($builder, $options);
if ($options['include_teams'] === true) { if ($options['include_teams'] === true) {
$builder $builder->add('teams', TeamType::class, [
->add('teams', TeamType::class, [ 'multiple' => true,
'multiple' => true, 'expanded' => false,
'expanded' => false, 'required' => false,
'required' => false, ]);
]);
} }
if ($options['include_roles'] === true) { if ($options['include_roles'] === true) {
$builder $builder->add('roles', UserRoleType::class, [
->add('roles', UserRoleType::class, [ 'multiple' => true,
'multiple' => true, 'expanded' => false,
'expanded' => false, 'required' => false,
'required' => false, ]);
]);
} }
} }

View File

@@ -12,9 +12,10 @@ namespace App\Form;
use App\Configuration\SystemConfiguration; use App\Configuration\SystemConfiguration;
use App\Entity\User; use App\Entity\User;
use App\Form\Type\AvatarType; use App\Form\Type\AvatarType;
use App\Form\Type\LanguageType;
use App\Form\Type\MailType; use App\Form\Type\MailType;
use App\Form\Type\TimezoneType; use App\Form\Type\TimezoneType;
use App\Form\Type\UserLanguageType;
use App\Form\Type\UserType;
use App\Form\Type\YesNoType; use App\Form\Type\YesNoType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
@@ -35,20 +36,26 @@ class UserEditType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder /** @var User|null $user */
->add('alias', TextType::class, [ $user = null;
'label' => 'alias', if (\array_key_exists('data', $options)) {
'required' => false, $user = $options['data'];
]) }
->add('title', TextType::class, [
'label' => 'title', $builder->add('alias', TextType::class, [
'required' => false, 'label' => 'alias',
]) 'required' => false,
->add('accountNumber', TextType::class, [ ]);
'label' => 'account_number',
'required' => false, $builder->add('title', TextType::class, [
]) 'label' => 'title',
; 'required' => false,
]);
$builder->add('accountNumber', TextType::class, [
'label' => 'account_number',
'required' => false,
]);
if ($this->configuration->isThemeAllowAvatarUrls()) { if ($this->configuration->isThemeAllowAvatarUrls()) {
$builder->add('avatar', AvatarType::class, [ $builder->add('avatar', AvatarType::class, [
@@ -61,7 +68,7 @@ class UserEditType extends AbstractType
$builder->add('email', MailType::class); $builder->add('email', MailType::class);
if ($options['include_preferences']) { if ($options['include_preferences']) {
$builder->add('language', LanguageType::class, [ $builder->add('language', UserLanguageType::class, [
'required' => true, 'required' => true,
]); ]);
@@ -75,12 +82,20 @@ class UserEditType extends AbstractType
'label' => 'active', 'label' => 'active',
'help' => 'active.help' 'help' => 'active.help'
]); ]);
$builder->add('systemAccount', YesNoType::class, [
'label' => 'system_account',
'help' => 'system_account.help',
]);
} }
$builder->add('systemAccount', YesNoType::class, [ if ($options['include_supervisor']) {
'label' => 'system_account', $builder->add('supervisor', UserType::class, [
'help' => 'system_account.help', 'required' => false,
]); 'label' => 'supervisor',
'ignore_users' => ($user instanceof User && $user->getId() !== null ? [$user] : []),
]);
}
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void
@@ -93,6 +108,7 @@ class UserEditType extends AbstractType
'csrf_token_id' => 'edit_user_profile', 'csrf_token_id' => 'edit_user_profile',
'include_active_flag' => true, 'include_active_flag' => true,
'include_preferences' => true, 'include_preferences' => true,
'include_supervisor' => true,
]); ]);
} }
} }

View File

@@ -34,6 +34,7 @@ final class UserVoter extends Voter
'hourly-rate', 'hourly-rate',
'view_team_member', 'view_team_member',
'contract', 'contract',
'supervisor',
]; ];
public function __construct(private RolePermissionManager $permissionManager) public function __construct(private RolePermissionManager $permissionManager)

View File

@@ -10,27 +10,12 @@
{% embed formEditTemplate with formOptions %} {% embed formEditTemplate with formOptions %}
{% block form_body %} {% block form_body %}
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-6">
{{ form_row(form.username) }} {{ form_row(form.username) }}
</div> </div>
<div class="col-md-4"> <div class="col-md-6">
{{ form_row(form.email) }} {{ form_row(form.email) }}
</div> </div>
<div class="col-md-4">
{{ form_row(form.color) }}
</div>
</div>
<div class="row">
<div class="col-md-4">
{{ form_row(form.alias) }}
</div>
<div class="col-md-4">
{{ form_row(form.title) }}
</div>
<div class="col-md-4">
{{ form_row(form.accountNumber) }}
</div>
</div> </div>
<div class="row"> <div class="row">
@@ -42,6 +27,26 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-6">
{{ form_row(form.alias) }}
</div>
<div class="col-md-6">
{{ form_row(form.title) }}
</div>
</div>
<div class="row">
<div class="col-md{% if form.avatar is defined %}-6{% endif %}">
{{ form_row(form.color) }}
</div>
{% if form.avatar is defined %}
<div class="col-md-6">
{{ form_row(form.avatar) }}
</div>
{% endif %}
</div>
{% if form.language is defined and form.timezone is defined %} {% if form.language is defined and form.timezone is defined %}
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
@@ -53,6 +58,15 @@
</div> </div>
{% endif %} {% endif %}
<div class="row">
<div class="col-md-6">
{{ form_row(form.accountNumber) }}
</div>
<div class="col-md-6">
{{ form_row(form.supervisor) }}
</div>
</div>
{% if form.teams is defined or form.roles is defined %} {% if form.teams is defined or form.roles is defined %}
{% set length = 12 %} {% set length = 12 %}
{% if form.teams is defined and form.roles is defined %} {% if form.teams is defined and form.roles is defined %}

View File

@@ -1,20 +1,36 @@
{% extends 'user/form.html.twig' %} {% extends 'user/form.html.twig' %}
{% block form_pre_content %} {% block form_content %}
<div class="row">
<div class="col-md-9"> {% form_theme form 'form/horizontal.html.twig' %}
{{ form_row(form.alias) }}
</div> {{ form_row(form._token) }}
<div class="col-md-3">
{{ form_row(form.color) }} <fieldset class="form-fieldset form-fieldset-light">
</div> {{ form_row(form.alias) }}
</div> {{ form_row(form.email) }}
<div class="row"> {% if form.systemAccount is defined %}
<div class="col-md-9"> {{ form_row(form.systemAccount) }}
{{ form_row(form.title) }} {% endif %}
</div> </fieldset>
<div class="col-md-3">
{{ form_row(form.accountNumber) }} <fieldset class="form-fieldset form-fieldset-light">
</div> {{ form_row(form.accountNumber) }}
</div> {% if form.supervisor is defined %}
{{ form_row(form.supervisor) }}
{% endif %}
</fieldset>
<fieldset class="form-fieldset form-fieldset-light">
{{ form_row(form.title) }}
{{ form_row(form.color) }}
{% if form.avatar is defined %}
{{ form_row(form.avatar) }}
{% endif %}
</fieldset>
<fieldset class="form-fieldset form-fieldset-light">
{{- form_rest(form) -}}
</fieldset>
{% endblock %} {% endblock %}

View File

@@ -78,7 +78,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
$this->assertEquals($pageSize, $response->headers->get('X-Per-Page')); $this->assertEquals($pageSize, $response->headers->get('X-Per-Page'));
} }
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET'): void protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, string $method = 'GET'): void
{ {
$this->request($client, $url, $method); $this->request($client, $url, $method);
$this->assertResponseIsSecured($client->getResponse(), $url); $this->assertResponseIsSecured($client->getResponse(), $url);
@@ -126,7 +126,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
]); ]);
} }
public function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler public function request(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null): Crawler
{ {
$server = ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json']; $server = ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'];
@@ -355,6 +355,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
'enabled' => 'bool', 'enabled' => 'bool',
'alias' => '@string', 'alias' => '@string',
'title' => '@string', 'title' => '@string',
'supervisor' => ['result' => 'object', 'type' => '@UserEntity'],
'avatar' => '@string', 'avatar' => '@string',
'color' => '@string', 'color' => '@string',
'teams' => ['result' => 'array', 'type' => 'Team'], 'teams' => ['result' => 'array', 'type' => 'Team'],

View File

@@ -17,12 +17,12 @@ use App\Tests\Controller\ControllerBaseTest;
*/ */
class ApiDocControllerTest extends ControllerBaseTest class ApiDocControllerTest extends ControllerBaseTest
{ {
public function testIsSecure() public function testIsSecure(): void
{ {
$this->assertUrlIsSecured('/api/doc'); $this->assertUrlIsSecured('/api/doc');
} }
public function testGetDocs() public function testGetDocs(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/doc'); $this->assertAccessIsGranted($client, '/api/doc');
@@ -47,7 +47,7 @@ class ApiDocControllerTest extends ControllerBaseTest
self::assertEquals($expectedKeys, $actual, sprintf('Expected %s sections in API docs, but found %s.', \count($actual), \count($expectedKeys))); self::assertEquals($expectedKeys, $actual, sprintf('Expected %s sections in API docs, but found %s.', \count($actual), \count($expectedKeys)));
} }
public function testGetJsonDocs() public function testGetJsonDocs(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/doc.json'); $this->assertAccessIsGranted($client, '/api/doc.json');

View File

@@ -16,12 +16,15 @@ use App\Entity\User;
*/ */
class UserControllerTest extends APIControllerBaseTest class UserControllerTest extends APIControllerBaseTest
{ {
public function testIsSecure() public function testIsSecure(): void
{ {
$this->assertUrlIsSecured('/api/users'); $this->assertUrlIsSecured('/api/users');
} }
public function getRoleTestData() /**
* @return array<array<string>>
*/
public function getRoleTestData(): array
{ {
return [ return [
[User::ROLE_USER], [User::ROLE_USER],
@@ -33,12 +36,12 @@ class UserControllerTest extends APIControllerBaseTest
/** /**
* @dataProvider getRoleTestData * @dataProvider getRoleTestData
*/ */
public function testIsSecureForRole(string $role) public function testIsSecureForRole(string $role): void
{ {
$this->assertUrlIsSecuredForRole($role, '/api/users'); $this->assertUrlIsSecuredForRole($role, '/api/users');
} }
public function testGetCollection() public function testGetCollection(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/api/users'); $this->assertAccessIsGranted($client, '/api/users');
@@ -52,7 +55,7 @@ class UserControllerTest extends APIControllerBaseTest
} }
} }
public function testGetCollectionWithQuery() public function testGetCollectionWithQuery(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/api/users', 'GET', ['visible' => 2, 'orderBy' => 'email', 'order' => 'DESC', 'term' => 'chris']); $this->assertAccessIsGranted($client, '/api/users', 'GET', ['visible' => 2, 'orderBy' => 'email', 'order' => 'DESC', 'term' => 'chris']);
@@ -66,7 +69,7 @@ class UserControllerTest extends APIControllerBaseTest
} }
} }
public function testGetCollectionWithQuery2() public function testGetCollectionWithQuery2(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/api/users', 'GET', ['visible' => 3, 'orderBy' => 'email', 'order' => 'DESC']); $this->assertAccessIsGranted($client, '/api/users', 'GET', ['visible' => 3, 'orderBy' => 'email', 'order' => 'DESC']);
@@ -80,7 +83,7 @@ class UserControllerTest extends APIControllerBaseTest
} }
} }
public function testGetEntity() public function testGetEntity(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/api/users/1'); $this->assertAccessIsGranted($client, '/api/users/1');
@@ -93,7 +96,7 @@ class UserControllerTest extends APIControllerBaseTest
self::assertEquals('Clara Haynes', $result['alias']); self::assertEquals('Clara Haynes', $result['alias']);
} }
public function testGetMyProfile() public function testGetMyProfile(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/api/users/me'); $this->assertAccessIsGranted($client, '/api/users/me');
@@ -106,18 +109,18 @@ class UserControllerTest extends APIControllerBaseTest
self::assertEquals('', $result['alias']); self::assertEquals('', $result['alias']);
} }
public function testNotFound() public function testNotFound(): void
{ {
$this->assertEntityNotFound(User::ROLE_SUPER_ADMIN, '/api/users/99'); $this->assertEntityNotFound(User::ROLE_SUPER_ADMIN, '/api/users/99');
} }
public function testGetEntityAccessDenied() public function testGetEntityAccessDenied(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertApiAccessDenied($client, '/api/users/4', 'You are not allowed to view this profile'); $this->assertApiAccessDenied($client, '/api/users/4', 'You are not allowed to view this profile');
} }
public function testGetEntityAccessAllowedForOwnProfile() public function testGetEntityAccessAllowedForOwnProfile(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/users/2'); $this->assertAccessIsGranted($client, '/api/users/2');
@@ -127,7 +130,7 @@ class UserControllerTest extends APIControllerBaseTest
self::assertApiResponseTypeStructure('UserEntity', $result); self::assertApiResponseTypeStructure('UserEntity', $result);
} }
public function testPostAction() public function testPostAction(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [ $data = [
@@ -136,6 +139,7 @@ class UserControllerTest extends APIControllerBaseTest
'title' => 'asdfghjkl', 'title' => 'asdfghjkl',
'plainPassword' => 'foo@example.com', 'plainPassword' => 'foo@example.com',
'enabled' => true, 'enabled' => true,
'supervisor' => 2,
'language' => 'ru', 'language' => 'ru',
'timezone' => 'Europe/Paris', 'timezone' => 'Europe/Paris',
'roles' => [ 'roles' => [
@@ -158,7 +162,7 @@ class UserControllerTest extends APIControllerBaseTest
self::assertEquals(['ROLE_TEAMLEAD', 'ROLE_ADMIN'], $result['roles']); self::assertEquals(['ROLE_TEAMLEAD', 'ROLE_ADMIN'], $result['roles']);
} }
public function testPostActionWithShortPassword() public function testPostActionWithShortPassword(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [ $data = [
@@ -181,7 +185,7 @@ class UserControllerTest extends APIControllerBaseTest
$this->assertApiCallValidationError($response, ['plainPassword']); $this->assertApiCallValidationError($response, ['plainPassword']);
} }
public function testPostActionWithValidationErrors() public function testPostActionWithValidationErrors(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [ $data = [
@@ -201,7 +205,7 @@ class UserControllerTest extends APIControllerBaseTest
$this->assertApiCallValidationError($response, ['username', 'email', 'plainPassword', 'language', 'timezone', 'roles']); $this->assertApiCallValidationError($response, ['username', 'email', 'plainPassword', 'language', 'timezone', 'roles']);
} }
public function testPostActionWithInvalidUser() public function testPostActionWithInvalidUser(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$data = [ $data = [
@@ -217,7 +221,7 @@ class UserControllerTest extends APIControllerBaseTest
$this->assertApiResponseAccessDenied($response, 'Access denied.'); $this->assertApiResponseAccessDenied($response, 'Access denied.');
} }
public function testPatchAction() public function testPatchAction(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [ $data = [
@@ -261,19 +265,19 @@ class UserControllerTest extends APIControllerBaseTest
self::assertEquals(['ROLE_TEAMLEAD'], $result['roles']); self::assertEquals(['ROLE_TEAMLEAD'], $result['roles']);
} }
public function testPatchActionWithUnknownUser() public function testPatchActionWithUnknownUser(): void
{ {
$this->assertEntityNotFoundForPatch(User::ROLE_SUPER_ADMIN, '/api/users/255', []); $this->assertEntityNotFoundForPatch(User::ROLE_SUPER_ADMIN, '/api/users/255', []);
} }
public function testPatchActionWithInvalidUser() public function testPatchActionWithInvalidUser(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->request($client, '/api/users/1', 'PATCH', [], json_encode(['language' => 'hu'])); $this->request($client, '/api/users/1', 'PATCH', [], json_encode(['language' => 'hu']));
$this->assertApiResponseAccessDenied($client->getResponse(), 'Not allowed to edit user'); $this->assertApiResponseAccessDenied($client->getResponse(), 'Not allowed to edit user');
} }
public function testPatchActionWithValidationErrors() public function testPatchActionWithValidationErrors(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$data = [ $data = [

View File

@@ -203,6 +203,7 @@ class ActivityControllerTest extends ControllerBaseTest
]); ]);
$location = $this->assertIsModalRedirect($client, '/details'); $location = $this->assertIsModalRedirect($client, '/details');
self::assertNotNull($location);
$this->requestPure($client, $location); $this->requestPure($client, $location);
$this->assertDetailsPage($client); $this->assertDetailsPage($client);

View File

@@ -17,6 +17,7 @@ use App\Form\Type\DateRangeType;
use App\Repository\UserRepository; use App\Repository\UserRepository;
use App\Tests\KernelTestTrait; use App\Tests\KernelTestTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -77,7 +78,7 @@ abstract class ControllerBaseTest extends WebTestCase
return self::getContainer()->get($service); return self::getContainer()->get($service);
} }
protected function loadUserFromDatabase(string $username) protected function loadUserFromDatabase(string $username): User
{ {
/** @var UserRepository $userRepository */ /** @var UserRepository $userRepository */
$userRepository = self::getContainer()->get('doctrine')->getRepository(User::class); $userRepository = self::getContainer()->get('doctrine')->getRepository(User::class);
@@ -102,7 +103,7 @@ abstract class ControllerBaseTest extends WebTestCase
$this->clearConfigCache(); $this->clearConfigCache();
} }
protected function clearConfigCache() protected function clearConfigCache(): void
{ {
/** @var ConfigurationService $service */ /** @var ConfigurationService $service */
$service = self::getContainer()->get(ConfigurationService::class); $service = self::getContainer()->get(ConfigurationService::class);
@@ -146,30 +147,17 @@ abstract class ControllerBaseTest extends WebTestCase
return $url; return $url;
} }
/** public function request(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null): Crawler
* @param HttpKernelBrowser $client
* @param string $url
* @param string $method
* @param array $parameters
* @param string $content
* @return \Symfony\Component\DomCrawler\Crawler
*/
public function request(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null)
{ {
return $client->request($method, $this->createUrl($url), $parameters, [], [], $content); return $client->request($method, $this->createUrl($url), $parameters, [], [], $content);
} }
public function requestPure(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null) public function requestPure(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = [], string $content = null): Crawler
{ {
return $client->request($method, $url, $parameters, [], [], $content); return $client->request($method, $url, $parameters, [], [], $content);
} }
/** protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, string $method = 'GET'): void
* @param HttpKernelBrowser $client
* @param string $url
* @param string $method
*/
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, ?string $method = 'GET')
{ {
$this->request($client, $url, $method); $this->request($client, $url, $method);
@@ -189,7 +177,7 @@ abstract class ControllerBaseTest extends WebTestCase
); );
} }
protected function assertSuccessResponse(HttpKernelBrowser $client, string $message = '') protected function assertSuccessResponse(HttpKernelBrowser $client, string $message = ''): void
{ {
$response = $client->getResponse(); $response = $client->getResponse();
self::assertThat($response, new ResponseConstraint\ResponseIsSuccessful(), 'Response is not successful, got code: ' . $response->getStatusCode()); self::assertThat($response, new ResponseConstraint\ResponseIsSuccessful(), 'Response is not successful, got code: ' . $response->getStatusCode());
@@ -225,19 +213,19 @@ abstract class ControllerBaseTest extends WebTestCase
); );
} }
protected function assertAccessIsGranted(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = []) protected function assertAccessIsGranted(HttpKernelBrowser $client, string $url, string $method = 'GET', array $parameters = []): void
{ {
$this->request($client, $url, $method, $parameters); $this->request($client, $url, $method, $parameters);
self::assertTrue($client->getResponse()->isSuccessful()); self::assertTrue($client->getResponse()->isSuccessful());
} }
protected function assertRouteNotFound(HttpKernelBrowser $client) protected function assertRouteNotFound(HttpKernelBrowser $client): void
{ {
self::assertFalse($client->getResponse()->isSuccessful()); self::assertFalse($client->getResponse()->isSuccessful());
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode()); self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
} }
protected function assert404(Response $response, ?string $message = null) protected function assert404(Response $response, ?string $message = null): void
{ {
$message = 'Page not found'; $message = 'Page not found';
self::assertFalse($response->isSuccessful()); self::assertFalse($response->isSuccessful());
@@ -245,7 +233,7 @@ abstract class ControllerBaseTest extends WebTestCase
self::assertStringContainsString($message, $response->getContent()); self::assertStringContainsString($message, $response->getContent());
} }
protected function assertMainContentClass(HttpKernelBrowser $client, string $classname) protected function assertMainContentClass(HttpKernelBrowser $client, string $classname): void
{ {
self::assertStringContainsString('<section id="" class="content ' . $classname . '">', $client->getResponse()->getContent()); self::assertStringContainsString('<section id="" class="content ' . $classname . '">', $client->getResponse()->getContent());
} }
@@ -253,7 +241,7 @@ abstract class ControllerBaseTest extends WebTestCase
/** /**
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
*/ */
protected function assertHasDataTable(HttpKernelBrowser $client) protected function assertHasDataTable(HttpKernelBrowser $client): void
{ {
self::assertStringContainsString('<table class="table table-hover dataTable" role="grid" data-reload-event="', $client->getResponse()->getContent()); self::assertStringContainsString('<table class="table table-hover dataTable" role="grid" data-reload-event="', $client->getResponse()->getContent());
} }
@@ -261,7 +249,7 @@ abstract class ControllerBaseTest extends WebTestCase
/** /**
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
*/ */
protected static function assertHasProgressbar(HttpKernelBrowser $client) protected static function assertHasProgressbar(HttpKernelBrowser $client): void
{ {
$content = $client->getResponse()->getContent(); $content = $client->getResponse()->getContent();
self::assertStringContainsString('<div class="progress-bar', $content); self::assertStringContainsString('<div class="progress-bar', $content);
@@ -274,7 +262,7 @@ abstract class ControllerBaseTest extends WebTestCase
* @param string $class * @param string $class
* @param int $count * @param int $count
*/ */
protected function assertDataTableRowCount(HttpKernelBrowser $client, string $class, int $count) protected function assertDataTableRowCount(HttpKernelBrowser $client, string $class, int $count): void
{ {
$node = $client->getCrawler()->filter('section.content div.' . $class . ' table.dataTable tbody tr:not(.summary)'); $node = $client->getCrawler()->filter('section.content div.' . $class . ' table.dataTable tbody tr:not(.summary)');
self::assertEquals($count, $node->count()); self::assertEquals($count, $node->count());
@@ -284,7 +272,7 @@ abstract class ControllerBaseTest extends WebTestCase
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
* @param array $buttons * @param array $buttons
*/ */
protected function assertPageActions(HttpKernelBrowser $client, array $buttons) protected function assertPageActions(HttpKernelBrowser $client, array $buttons): void
{ {
$node = $client->getCrawler()->filter('div.page-header div.page-actions .pa-desktop a'); $node = $client->getCrawler()->filter('div.page-header div.page-actions .pa-desktop a');
@@ -307,7 +295,7 @@ abstract class ControllerBaseTest extends WebTestCase
* @param array $fieldNames array of form-fields that should fail * @param array $fieldNames array of form-fields that should fail
* @param bool $disableValidation whether the form should validate before submitting or not * @param bool $disableValidation whether the form should validate before submitting or not
*/ */
protected function assertHasValidationError(HttpKernelBrowser $client, $url, $formSelector, array $formData, array $fieldNames, $disableValidation = true) protected function assertHasValidationError(HttpKernelBrowser $client, string $url, string $formSelector, array $formData, array $fieldNames, bool $disableValidation = true): void
{ {
$crawler = $client->request('GET', $this->createUrl($url)); $crawler = $client->request('GET', $this->createUrl($url));
$form = $crawler->filter($formSelector)->form(); $form = $crawler->filter($formSelector)->form();
@@ -342,24 +330,16 @@ abstract class ControllerBaseTest extends WebTestCase
} }
} }
/** protected function assertFormHasValidationError(string $role, string $url, string $formSelector, array $formData, array $fieldNames): void
* @param string $role the USER role to use for the request
* @param string $url the URL of the page displaying the initial form to submit
* @param string $formSelector a selector to find the form to test
* @param array $formData values to fill in the form
* @param array $fieldNames array of form-fields that should fail
* @param bool $disableValidation whether the form should validate before submitting or not
*/
protected function assertFormHasValidationError($role, $url, $formSelector, array $formData, array $fieldNames, $disableValidation = true)
{ {
$client = $this->getClientForAuthenticatedUser($role); $client = $this->getClientForAuthenticatedUser($role);
$this->assertHasValidationError($client, $url, $formSelector, $formData, $fieldNames, $disableValidation); $this->assertHasValidationError($client, $url, $formSelector, $formData, $fieldNames);
} }
/** /**
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
*/ */
protected function assertHasNoEntriesWithFilter(HttpKernelBrowser $client) protected function assertHasNoEntriesWithFilter(HttpKernelBrowser $client): void
{ {
$this->assertCalloutWidgetWithMessage($client, 'No entries were found based on your selected filters.'); $this->assertCalloutWidgetWithMessage($client, 'No entries were found based on your selected filters.');
} }
@@ -368,18 +348,18 @@ abstract class ControllerBaseTest extends WebTestCase
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
* @param string $message * @param string $message
*/ */
protected function assertCalloutWidgetWithMessage(HttpKernelBrowser $client, string $message) protected function assertCalloutWidgetWithMessage(HttpKernelBrowser $client, string $message): void
{ {
$node = $client->getCrawler()->filter('div.alert.alert-warning.alert-important'); $node = $client->getCrawler()->filter('div.alert.alert-warning.alert-important');
self::assertStringContainsString($message, $node->text(null, true)); self::assertStringContainsString($message, $node->text(null, true));
} }
protected function assertHasFlashDeleteSuccess(HttpKernelBrowser $client) protected function assertHasFlashDeleteSuccess(HttpKernelBrowser $client): void
{ {
$this->assertHasFlashSuccess($client, 'Entry was deleted'); $this->assertHasFlashSuccess($client, 'Entry was deleted');
} }
protected function assertHasFlashSaveSuccess(HttpKernelBrowser $client) protected function assertHasFlashSaveSuccess(HttpKernelBrowser $client): void
{ {
$this->assertHasFlashSuccess($client, 'Saved changes'); $this->assertHasFlashSuccess($client, 'Saved changes');
} }
@@ -388,7 +368,7 @@ abstract class ControllerBaseTest extends WebTestCase
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
* @param string|null $message * @param string|null $message
*/ */
protected function assertHasFlashSuccess(HttpKernelBrowser $client, string $message = null) protected function assertHasFlashSuccess(HttpKernelBrowser $client, string $message = null): void
{ {
$this->assertHasFlashMessage($client, 'success', $message); $this->assertHasFlashMessage($client, 'success', $message);
} }
@@ -397,12 +377,12 @@ abstract class ControllerBaseTest extends WebTestCase
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
* @param string|null $message * @param string|null $message
*/ */
protected function assertHasFlashError(HttpKernelBrowser $client, string $message = null) protected function assertHasFlashError(HttpKernelBrowser $client, string $message = null): void
{ {
$this->assertHasFlashMessage($client, 'error', $message); $this->assertHasFlashMessage($client, 'error', $message);
} }
private function assertHasFlashMessage(HttpKernelBrowser $client, string $type, string $message = null) private function assertHasFlashMessage(HttpKernelBrowser $client, string $type, string $message = null): void
{ {
$content = $client->getResponse()->getContent(); $content = $client->getResponse()->getContent();
self::assertStringContainsString('ALERT.' . $type . '(\'', $content, 'Could not find flash ' . $type . ' message'); self::assertStringContainsString('ALERT.' . $type . '(\'', $content, 'Could not find flash ' . $type . ' message');
@@ -418,7 +398,7 @@ abstract class ControllerBaseTest extends WebTestCase
* @param HttpKernelBrowser $client * @param HttpKernelBrowser $client
* @param string $url * @param string $url
*/ */
protected function assertIsRedirect(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true) protected function assertIsRedirect(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true): void
{ {
self::assertResponseRedirects(); self::assertResponseRedirects();
@@ -434,6 +414,7 @@ abstract class ControllerBaseTest extends WebTestCase
self::assertEquals(201, $client->getResponse()->getStatusCode()); self::assertEquals(201, $client->getResponse()->getStatusCode());
self::assertTrue($client->getResponse()->headers->has('x-modal-redirect'), 'Could not find "x-modal-redirect" header'); self::assertTrue($client->getResponse()->headers->has('x-modal-redirect'), 'Could not find "x-modal-redirect" header');
$location = $client->getResponse()->headers->get('x-modal-redirect'); $location = $client->getResponse()->headers->get('x-modal-redirect');
self::assertNotNull($location);
// check for meta refresh // check for meta refresh
$expectedMeta = sprintf('<meta http-equiv="refresh" content="0;url=\'%1$s\'" />', $location); $expectedMeta = sprintf('<meta http-equiv="refresh" content="0;url=\'%1$s\'" />', $location);
@@ -450,14 +431,11 @@ abstract class ControllerBaseTest extends WebTestCase
return $location; return $location;
} }
protected function assertRedirectUrl(HttpKernelBrowser $client, ?string $url = null, bool $endsWith = true) protected function assertRedirectUrl(HttpKernelBrowser $client, string $url, bool $endsWith = true): void
{ {
self::assertTrue($client->getResponse()->headers->has('Location'), 'Could not find "Location" header'); self::assertTrue($client->getResponse()->headers->has('Location'), 'Could not find "Location" header');
$location = $client->getResponse()->headers->get('Location'); $location = $client->getResponse()->headers->get('Location');
self::assertNotNull($location);
if ($url === null) {
return;
}
if ($endsWith) { if ($endsWith) {
self::assertStringEndsWith($url, $location, 'Redirect URL does not match'); self::assertStringEndsWith($url, $location, 'Redirect URL does not match');
@@ -466,18 +444,21 @@ abstract class ControllerBaseTest extends WebTestCase
} }
} }
protected function assertExcelExportResponse(HttpKernelBrowser $client, string $prefix) protected function assertExcelExportResponse(HttpKernelBrowser $client, string $prefix): void
{ {
/** @var BinaryFileResponse $response */ /** @var BinaryFileResponse $response */
$response = $client->getResponse(); $response = $client->getResponse();
self::assertInstanceOf(BinaryFileResponse::class, $response); self::assertInstanceOf(BinaryFileResponse::class, $response);
$disposition = $response->headers->get('Content-Disposition');
self::assertNotNull($disposition);
self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $response->headers->get('Content-Type')); self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $response->headers->get('Content-Type'));
self::assertStringContainsString('attachment; filename=' . $prefix, $response->headers->get('Content-Disposition')); self::assertStringContainsString('attachment; filename=' . $prefix, $disposition);
self::assertStringContainsString('.xlsx', $response->headers->get('Content-Disposition')); self::assertStringContainsString('.xlsx', $disposition);
} }
protected function assertInvalidCsrfToken(HttpKernelBrowser $client, string $url, string $expectedRedirect) protected function assertInvalidCsrfToken(HttpKernelBrowser $client, string $url, string $expectedRedirect): void
{ {
$this->request($client, $url); $this->request($client, $url);

View File

@@ -34,7 +34,7 @@ class PermissionControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN); $client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/admin/permissions'); $this->assertAccessIsGranted($client, '/admin/permissions');
$this->assertHasDataTable($client); $this->assertHasDataTable($client);
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 130); $this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 132);
$this->assertPageActions($client, [ $this->assertPageActions($client, [
'create modal-ajax-form' => $this->createUrl('/admin/permissions/roles/create'), 'create modal-ajax-form' => $this->createUrl('/admin/permissions/roles/create'),
]); ]);

View File

@@ -25,7 +25,7 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
*/ */
class SecurityControllerTest extends ControllerBaseTest class SecurityControllerTest extends ControllerBaseTest
{ {
public function testRootUrlIsRedirectedToLogin() public function testRootUrlIsRedirectedToLogin(): void
{ {
$client = self::createClient(); $client = self::createClient();
$client->request('GET', '/'); $client->request('GET', '/');
@@ -35,7 +35,7 @@ class SecurityControllerTest extends ControllerBaseTest
$this->assertIsRedirect($client, $this->createUrl('/login')); $this->assertIsRedirect($client, $this->createUrl('/login'));
} }
public function testLoginPageIsRendered() public function testLoginPageIsRendered(): void
{ {
$client = self::createClient(); $client = self::createClient();
$this->request($client, '/login'); $this->request($client, '/login');
@@ -54,7 +54,7 @@ class SecurityControllerTest extends ControllerBaseTest
$this->assertStringNotContainsString('Register a new account', $content); $this->assertStringNotContainsString('Register a new account', $content);
} }
public function testLoginPositive() public function testLoginPositive(): void
{ {
$client = self::createClient(); $client = self::createClient();
$this->request($client, '/login'); $this->request($client, '/login');
@@ -79,7 +79,7 @@ class SecurityControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
} }
public function testLoginAlreadyLoggedIn() public function testLoginAlreadyLoggedIn(): void
{ {
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
@@ -94,7 +94,7 @@ class SecurityControllerTest extends ControllerBaseTest
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
} }
public function testLoginNegative() public function testLoginNegative(): void
{ {
$client = self::createClient(); $client = self::createClient();
$this->request($client, '/login'); $this->request($client, '/login');
@@ -114,7 +114,7 @@ class SecurityControllerTest extends ControllerBaseTest
self::assertStringContainsString('<div class="alert alert-important 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() public function testCheckAction(): void
{ {
$this->expectException(\RuntimeException::class); $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.'); $this->expectExceptionMessage('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
@@ -127,7 +127,7 @@ class SecurityControllerTest extends ControllerBaseTest
$sut->checkAction(); $sut->checkAction();
} }
public function testLogoutAction() public function testLogoutAction(): void
{ {
$this->expectException(\RuntimeException::class); $this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('You must activate the logout in your security firewall configuration.'); $this->expectExceptionMessage('You must activate the logout in your security firewall configuration.');

View File

@@ -186,8 +186,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
[ [
'#system_configuration_form_timesheet_configuration_0_value', // mode '#system_configuration_form_timesheet_configuration_0_value', // mode
'#system_configuration_form_timesheet_configuration_6_value', // hard_limit '#system_configuration_form_timesheet_configuration_6_value', // hard_limit
], ]
true
); );
} }
@@ -297,8 +296,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
'#system_configuration_form_customer_configuration_0_value', '#system_configuration_form_customer_configuration_0_value',
'#system_configuration_form_customer_configuration_1_value', '#system_configuration_form_customer_configuration_1_value',
'#system_configuration_form_customer_configuration_2_value', '#system_configuration_form_customer_configuration_2_value',
], ]
true
); );
} }
@@ -344,8 +342,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
], ],
[ [
'#system_configuration_form_theme_configuration_1_value', '#system_configuration_form_theme_configuration_1_value',
], ]
true
); );
} }
@@ -413,8 +410,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
'#system_configuration_form_calendar_configuration_3_value', '#system_configuration_form_calendar_configuration_3_value',
'#system_configuration_form_calendar_configuration_4_value', '#system_configuration_form_calendar_configuration_4_value',
'#system_configuration_form_calendar_configuration_5_value', '#system_configuration_form_calendar_configuration_5_value',
], ]
true
); );
} }
} }

View File

@@ -27,7 +27,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
*/ */
class UserTest extends TestCase class UserTest extends TestCase
{ {
public function testDefaultValues() public function testDefaultValues(): void
{ {
$user = new User(); $user = new User();
self::assertInstanceOf(EquatableInterface::class, $user); self::assertInstanceOf(EquatableInterface::class, $user);
@@ -83,6 +83,8 @@ class UserTest extends TestCase
self::assertEquals(0, $user->getHolidaysPerYear()); self::assertEquals(0, $user->getHolidaysPerYear());
self::assertFalse($user->hasWorkHourConfiguration()); self::assertFalse($user->hasWorkHourConfiguration());
self::assertNull($user->getPublicHolidayGroup()); self::assertNull($user->getPublicHolidayGroup());
self::assertFalse($user->hasSupervisor());
self::assertNull($user->getSupervisor());
} }
public function testWorkContract(): void public function testWorkContract(): void
@@ -139,7 +141,7 @@ class UserTest extends TestCase
self::assertTrue($sut->hasColor()); self::assertTrue($sut->hasColor());
} }
public function testWizards() public function testWizards(): void
{ {
$sut = new User(); $sut = new User();
// internal name may not be changed // internal name may not be changed
@@ -155,7 +157,7 @@ class UserTest extends TestCase
self::assertTrue($sut->hasSeenWizard('profile')); self::assertTrue($sut->hasSeenWizard('profile'));
} }
public function testAuth() public function testAuth(): void
{ {
$user = new User(); $user = new User();
@@ -183,7 +185,7 @@ class UserTest extends TestCase
self::assertTrue($user->isInternalUser()); self::assertTrue($user->isInternalUser());
} }
public function testDatetime() public function testDatetime(): void
{ {
$date = new \DateTime('+1 day'); $date = new \DateTime('+1 day');
$user = new User(); $user = new User();
@@ -191,7 +193,7 @@ class UserTest extends TestCase
self::assertEquals($date, $user->getRegisteredAt()); self::assertEquals($date, $user->getRegisteredAt());
} }
public function testPasswordRequestedAt() public function testPasswordRequestedAt(): void
{ {
$date = new \DateTime('-60 minutes'); $date = new \DateTime('-60 minutes');
$sut = new User(); $sut = new User();
@@ -205,7 +207,7 @@ class UserTest extends TestCase
self::assertTrue($sut->isPasswordRequestNonExpired(3610)); self::assertTrue($sut->isPasswordRequestNonExpired(3610));
} }
public function testPreferences() public function testPreferences(): void
{ {
$user = new User(); $user = new User();
self::assertNull($user->getPreference('test')); self::assertNull($user->getPreference('test'));
@@ -228,7 +230,7 @@ class UserTest extends TestCase
self::assertTrue($user->isExportDecimal()); self::assertTrue($user->isExportDecimal());
} }
public function testDisplayName() public function testDisplayName(): void
{ {
$user = new User(); $user = new User();
@@ -244,7 +246,7 @@ class UserTest extends TestCase
self::assertEquals('foo', (string) $user); self::assertEquals('foo', (string) $user);
} }
public function testGetUsername() public function testGetUsername(): void
{ {
$user = new User(); $user = new User();
@@ -260,7 +262,7 @@ class UserTest extends TestCase
self::assertEquals('foo', (string) $user); self::assertEquals('foo', (string) $user);
} }
public function testGetLocale() public function testGetLocale(): void
{ {
$sut = new User(); $sut = new User();
self::assertEquals(User::DEFAULT_LANGUAGE, $sut->getLocale()); self::assertEquals(User::DEFAULT_LANGUAGE, $sut->getLocale());
@@ -271,7 +273,7 @@ class UserTest extends TestCase
self::assertEquals('fr', $sut->getLocale()); self::assertEquals('fr', $sut->getLocale());
} }
public function testTeams() public function testTeams(): void
{ {
$sut = new User(); $sut = new User();
$user = new User(); $user = new User();
@@ -314,7 +316,7 @@ class UserTest extends TestCase
self::assertFalse($sut->hasTeamAssignment()); self::assertFalse($sut->hasTeamAssignment());
} }
public function testRoles() public function testRoles(): void
{ {
$sut = new User(); $sut = new User();
self::assertFalse($sut->canSeeAllData()); self::assertFalse($sut->canSeeAllData());
@@ -357,7 +359,7 @@ class UserTest extends TestCase
* *
* @see #1562 * @see #1562
*/ */
public function testMaxLength() public function testMaxLength(): void
{ {
$sut = new User(); $sut = new User();
$sut->setAlias('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); $sut->setAlias('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
@@ -372,7 +374,7 @@ class UserTest extends TestCase
self::assertEquals(50, mb_strlen($sut->getTitle())); self::assertEquals(50, mb_strlen($sut->getTitle()));
} }
public function testPreferencesCollectionIsCreatedOnBrokenUser() public function testPreferencesCollectionIsCreatedOnBrokenUser(): void
{ {
// this code is only used in some rare edge cases, maybe even only in development ... // this code is only used in some rare edge cases, maybe even only in development ...
// lets keep it, as it occured during the work on SAML authentication // lets keep it, as it occured during the work on SAML authentication
@@ -390,7 +392,7 @@ class UserTest extends TestCase
self::assertEquals('foobar', $sut->getPreferenceValue('test')); self::assertEquals('foobar', $sut->getPreferenceValue('test'));
} }
public function testCanSeeAllData() public function testCanSeeAllData(): void
{ {
$sut = new User(); $sut = new User();
$sut->addRole(User::ROLE_USER); $sut->addRole(User::ROLE_USER);
@@ -400,7 +402,7 @@ class UserTest extends TestCase
self::assertFalse($sut->initCanSeeAllData(true)); self::assertFalse($sut->initCanSeeAllData(true));
} }
public function testSystemAccount() public function testSystemAccount(): void
{ {
$sut = new User(); $sut = new User();
self::assertFalse($sut->isSystemAccount()); self::assertFalse($sut->isSystemAccount());
@@ -410,7 +412,7 @@ class UserTest extends TestCase
self::assertFalse($sut->isSystemAccount()); self::assertFalse($sut->isSystemAccount());
} }
public function testExportAnnotations() public function testExportAnnotations(): void
{ {
$sut = new AnnotationExtractor(); $sut = new AnnotationExtractor();
@@ -449,7 +451,7 @@ class UserTest extends TestCase
} }
} }
public function testEqualsTo() public function testEqualsTo(): void
{ {
$sut = new User(); $sut = new User();
$sut->setUserIdentifier('foo'); $sut->setUserIdentifier('foo');
@@ -479,7 +481,7 @@ class UserTest extends TestCase
self::assertTrue($sut2->isEqualTo($sut)); self::assertTrue($sut2->isEqualTo($sut));
} }
public function testSerialize() public function testSerialize(): void
{ {
$sut = new User(); $sut = new User();
$sut->setPassword('ABC-1234567890'); $sut->setPassword('ABC-1234567890');
@@ -509,7 +511,7 @@ class UserTest extends TestCase
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
public function testTeamMemberships() public function testTeamMemberships(): void
{ {
$team = new Team('Foo'); $team = new Team('Foo');
@@ -564,7 +566,7 @@ class UserTest extends TestCase
self::assertCount(1, $sut->getMemberships()); self::assertCount(1, $sut->getMemberships());
} }
public function testTeamMembershipsException() public function testTeamMembershipsException(): void
{ {
$this->expectException(\InvalidArgumentException::class); $this->expectException(\InvalidArgumentException::class);
$sut = new User(); $sut = new User();
@@ -572,4 +574,19 @@ class UserTest extends TestCase
$member->setUser(new User()); $member->setUser(new User());
$sut->addMembership($member); $sut->addMembership($member);
} }
public function testSupervisor(): void
{
$user = new User();
self::assertFalse($user->hasSupervisor());
self::assertNull($user->getSupervisor());
$supervisor = new User();
$supervisor->setTitle('Cool boss');
$user->setSupervisor($supervisor);
self::assertTrue($user->hasSupervisor());
self::assertNotNull($user->getSupervisor());
self::assertSame($supervisor, $user->getSupervisor());
}
} }

View File

@@ -422,21 +422,6 @@ parameters:
count: 1 count: 1
path: API/ApiDocControllerTest.php path: API/ApiDocControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ApiDocControllerTest\\:\\:testGetDocs\\(\\) has no return type specified\\.$#"
count: 1
path: API/ApiDocControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ApiDocControllerTest\\:\\:testGetJsonDocs\\(\\) has no return type specified\\.$#"
count: 1
path: API/ApiDocControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ApiDocControllerTest\\:\\:testIsSecure\\(\\) has no return type specified\\.$#"
count: 1
path: API/ApiDocControllerTest.php
- -
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 2 count: 2
@@ -1252,101 +1237,6 @@ parameters:
count: 1 count: 1
path: API/UserControllerTest.php path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:getRoleTestData\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetCollection\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetCollectionWithQuery\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetCollectionWithQuery2\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetEntity\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetEntityAccessAllowedForOwnProfile\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetEntityAccessDenied\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testGetMyProfile\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testIsSecure\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testIsSecureForRole\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testNotFound\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPatchAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPatchActionWithInvalidUser\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPatchActionWithUnknownUser\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPatchActionWithValidationErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPostAction\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPostActionWithInvalidUser\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPostActionWithShortPassword\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\UserControllerTest\\:\\:testPostActionWithValidationErrors\\(\\) has no return type specified\\.$#"
count: 1
path: API/UserControllerTest.php
- -
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
count: 9 count: 9
@@ -2402,41 +2292,11 @@ parameters:
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assert404\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertAccessIsGranted\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertAccessIsGranted\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertAccessIsGranted\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertCalloutWidgetWithMessage\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertDataTableRowCount\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertExcelExportResponse\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertFormHasValidationError\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertFormHasValidationError\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertFormHasValidationError\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -2447,51 +2307,6 @@ parameters:
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasDataTable\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasFlashDeleteSuccess\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasFlashError\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasFlashMessage\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasFlashSaveSuccess\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasFlashSuccess\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasNoEntriesWithFilter\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasProgressbar\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasValidationError\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasValidationError\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertHasValidationError\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -2502,81 +2317,21 @@ parameters:
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertInvalidCsrfToken\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertIsModalRedirect\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertIsRedirect\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertMainContentClass\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertPageActions\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertPageActions\\(\\) has parameter \\$buttons with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertPageActions\\(\\) has parameter \\$buttons with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertRedirectUrl\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertRequestIsSecured\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertRouteNotFound\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:assertSuccessResponse\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:clearConfigCache\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:importFixture\\(\\) return type has no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:importFixture\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:loadUserFromDatabase\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:request\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:request\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:requestPure\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:requestPure\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:requestPure\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -2592,21 +2347,6 @@ parameters:
count: 10 count: 10
path: Controller/ControllerBaseTest.php path: Controller/ControllerBaseTest.php
-
message: "#^Parameter \\#2 \\$haystack of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, string\\|null given\\.$#"
count: 4
path: Controller/ControllerBaseTest.php
-
message: "#^Parameter \\#2 \\$string of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringEndsWith\\(\\) expects string, string\\|null given\\.$#"
count: 2
path: Controller/ControllerBaseTest.php
-
message: "#^Parameter \\#3 \\$method of method App\\\\Tests\\\\Controller\\\\ControllerBaseTest\\:\\:request\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: Controller/ControllerBaseTest.php
- -
message: "#^Cannot call method addSubscriber\\(\\) on object\\|null\\.$#" message: "#^Cannot call method addSubscriber\\(\\) on object\\|null\\.$#"
count: 1 count: 1
@@ -3537,41 +3277,6 @@ parameters:
count: 6 count: 6
path: Controller/Security/PasswordResetControllerTest.php path: Controller/Security/PasswordResetControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testCheckAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testLoginAlreadyLoggedIn\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testLoginNegative\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testLoginPageIsRendered\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testLoginPositive\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testLogoutAction\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\Controller\\\\Security\\\\SecurityControllerTest\\:\\:testRootUrlIsRedirectedToLogin\\(\\) has no return type specified\\.$#"
count: 1
path: Controller/Security/SecurityControllerTest.php
- -
message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, string\\|false given\\.$#" message: "#^Parameter \\#2 \\$haystack of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\) expects string, string\\|false given\\.$#"
count: 6 count: 6
@@ -4722,106 +4427,6 @@ parameters:
count: 1 count: 1
path: Entity/UserTest.php path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testAuth\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testCanSeeAllData\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testDatetime\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testDefaultValues\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testDisplayName\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testEqualsTo\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testExportAnnotations\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testGetLocale\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testGetUsername\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testMaxLength\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testPasswordRequestedAt\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testPreferences\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testPreferencesCollectionIsCreatedOnBrokenUser\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testRoles\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testSystemAccount\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testTeamMemberships\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testTeamMembershipsException\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testTeams\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
-
message: "#^Method App\\\\Tests\\\\Entity\\\\UserTest\\:\\:testWizards\\(\\) has no return type specified\\.$#"
count: 1
path: Entity/UserTest.php
- -
message: "#^Parameter \\#1 \\$string of function mb_strlen expects string, string\\|null given\\.$#" message: "#^Parameter \\#1 \\$string of function mb_strlen expects string, string\\|null given\\.$#"
count: 5 count: 5

View File

@@ -1621,6 +1621,10 @@
<source>day_half</source> <source>day_half</source>
<target>Halber Tag</target> <target>Halber Tag</target>
</trans-unit> </trans-unit>
<trans-unit id="CDTC1gc" resname="supervisor">
<source>supervisor</source>
<target>Vorgesetzter</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -1621,6 +1621,10 @@
<source>day_half</source> <source>day_half</source>
<target>Half day</target> <target>Half day</target>
</trans-unit> </trans-unit>
<trans-unit id="CDTC1gc" resname="supervisor">
<source>supervisor</source>
<target>Vorgesetzter</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>