Release 2.2.0 (#4359)

* deactivate deprecation logging in prod for now
* fix several deprecations
* enable CSRF for logout
* allow more twig methods and functions in InvoiceSecurity policy
This commit is contained in:
Kevin Papst
2023-10-31 16:41:09 +01:00
committed by GitHub
parent 114617a052
commit 95f15e6c88
22 changed files with 396 additions and 324 deletions

368
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
when@prod: when@prod:
monolog: monolog:
channels: ["deprecation"] # channels: ["deprecation"]
handlers: handlers:
main: main:
type: fingers_crossed type: fingers_crossed
@@ -15,10 +15,11 @@ when@prod:
type: console type: console
process_psr_3_messages: false process_psr_3_messages: false
channels: ["!event", "!doctrine"] channels: ["!event", "!doctrine"]
deprecation: # deactivated, because currently there are too many deprecations cause by gedmo and doctrine
type: stream # deprecation:
channels: ["deprecation"] # type: stream
path: "%kernel.logs_dir%/deprecations.log" # channels: ["deprecation"]
# path: "%kernel.logs_dir%/deprecations.log"
when@dev: when@dev:
monolog: monolog:

View File

@@ -60,6 +60,7 @@ security:
logout: logout:
path: logout path: logout
target: homepage target: homepage
enable_csrf: true
login_throttling: login_throttling:
max_attempts: 5 max_attempts: 5

View File

@@ -77,7 +77,7 @@ final class UserLoginLinkCommand extends Command
$loginLink = $loginLinkDetails->getUrl(); $loginLink = $loginLinkDetails->getUrl();
if ($input->getOption('password-reset') === true) { if ($input->getOption('password-reset') === true) {
$user->setPasswordRequestedAt(new \DateTime()); $user->markPasswordRequested();
$user->setRequiresPasswordReset(true); $user->setRequiresPasswordReset(true);
$this->userRepository->saveUser($user); $this->userRepository->saveUser($user);
} }

View File

@@ -17,11 +17,11 @@ class Constants
/** /**
* The current release version * The current release version
*/ */
public const VERSION = '2.1.0'; public const VERSION = '2.2.0';
/** /**
* The current release: major * 10000 + minor * 100 + patch * The current release: major * 10000 + minor * 100 + patch
*/ */
public const VERSION_ID = 20100; public const VERSION_ID = 20200;
/** /**
* The software name * The software name
*/ */

View File

@@ -17,7 +17,6 @@ use App\Event\EmailPasswordResetEvent;
use App\Form\PasswordResetForm; use App\Form\PasswordResetForm;
use App\User\LoginManager; use App\User\LoginManager;
use App\User\UserService; use App\User\UserService;
use DateTime;
use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
@@ -83,7 +82,7 @@ final class PasswordResetController extends AbstractController
// this will finally send the email // this will finally send the email
$this->eventDispatcher->dispatch(new EmailEvent($event->getEmail())); $this->eventDispatcher->dispatch(new EmailEvent($event->getEmail()));
$user->setPasswordRequestedAt(new DateTime()); $user->markPasswordRequested();
$this->userService->updateUser($user); $this->userService->updateUser($user);
} }
@@ -138,8 +137,7 @@ final class PasswordResetController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$user->setConfirmationToken(null); $user->markPasswordResetted();
$user->setPasswordRequestedAt(null);
$user->setEnabled(true); $user->setEnabled(true);
$this->userService->updateUser($user); $this->userService->updateUser($user);

View File

@@ -188,8 +188,8 @@ class Timesheet implements EntityWithMetaFields, ExportableItem
#[ORM\Column(name: 'category', type: 'string', length: 10, nullable: false, options: ['default' => 'work'])] #[ORM\Column(name: 'category', type: 'string', length: 10, nullable: false, options: ['default' => 'work'])]
#[Assert\NotNull] #[Assert\NotNull]
private ?string $category = self::WORK; private ?string $category = self::WORK;
#[ORM\Column(name: 'modified_at', type: 'datetime', nullable: true)] #[ORM\Column(name: 'modified_at', type: 'datetime_immutable', nullable: true)]
private \DateTimeInterface $modifiedAt; private \DateTimeImmutable $modifiedAt; // @phpstan-ignore-line - create migration and update all null values and then make it not null
/** /**
* Tags * Tags
* *
@@ -586,12 +586,12 @@ class Timesheet implements EntityWithMetaFields, ExportableItem
return $this; return $this;
} }
public function getModifiedAt(): \DateTimeInterface public function getModifiedAt(): \DateTimeImmutable
{ {
return $this->modifiedAt; return $this->modifiedAt;
} }
public function setModifiedAt(\DateTimeInterface $dateTime): void public function setModifiedAt(\DateTimeImmutable $dateTime): void
{ {
$this->modifiedAt = $dateTime; $this->modifiedAt = $dateTime;
} }

View File

@@ -196,8 +196,8 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
#[ORM\Column(name: 'confirmation_token', type: 'string', length: 180, unique: true, nullable: true)] #[ORM\Column(name: 'confirmation_token', type: 'string', length: 180, unique: true, nullable: true)]
#[Assert\Length(max: 180)] #[Assert\Length(max: 180)]
private ?string $confirmationToken = null; private ?string $confirmationToken = null;
#[ORM\Column(name: 'password_requested_at', type: 'datetime', nullable: true)] #[ORM\Column(name: 'password_requested_at', type: 'datetime_immutable', nullable: true)]
private ?\DateTime $passwordRequestedAt = null; private ?\DateTimeImmutable $passwordRequestedAt = null;
/** /**
* List of all role names * List of all role names
*/ */
@@ -958,26 +958,31 @@ class User implements UserInterface, EquatableInterface, ThemeUserInterface, Pas
return $this; return $this;
} }
public function setConfirmationToken($confirmationToken): User public function setConfirmationToken($confirmationToken): void
{ {
$this->confirmationToken = $confirmationToken; $this->confirmationToken = $confirmationToken;
return $this;
} }
public function setPasswordRequestedAt(?\DateTime $date = null): User public function markPasswordRequested(): void
{
$this->setPasswordRequestedAt(new \DateTimeImmutable('now', new \DateTimeZone($this->getTimezone())));
}
public function markPasswordResetted(): void
{
$this->setConfirmationToken(null);
$this->setPasswordRequestedAt(null);
}
public function setPasswordRequestedAt(?\DateTimeImmutable $date): void
{ {
$this->passwordRequestedAt = $date; $this->passwordRequestedAt = $date;
return $this;
} }
/** /**
* Gets the timestamp that the user requested a password reset. * Gets the timestamp that the user requested a password reset.
*
* @return DateTime|null
*/ */
public function getPasswordRequestedAt(): ?DateTime public function getPasswordRequestedAt(): ?\DateTimeImmutable
{ {
return $this->passwordRequestedAt; return $this->passwordRequestedAt;
} }

View File

@@ -41,9 +41,9 @@ class WorkingTime
#[ORM\ManyToOne(targetEntity: User::class)] #[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(name: 'approved_by', nullable: true, onDelete: 'SET NULL')] #[ORM\JoinColumn(name: 'approved_by', nullable: true, onDelete: 'SET NULL')]
private ?User $approvedBy = null; private ?User $approvedBy = null;
#[ORM\Column(name: 'approved_at', type: 'datetime', nullable: true)] #[ORM\Column(name: 'approved_at', type: 'datetime_immutable', nullable: true)]
#[Assert\NotNull] #[Assert\NotNull]
private ?\DateTimeInterface $approvedAt = null; private ?\DateTimeImmutable $approvedAt = null;
public function __construct(User $user, \DateTimeInterface $date) public function __construct(User $user, \DateTimeInterface $date)
{ {
@@ -96,12 +96,12 @@ class WorkingTime
$this->approvedBy = $approvedBy; $this->approvedBy = $approvedBy;
} }
public function getApprovedAt(): ?\DateTimeInterface public function getApprovedAt(): ?\DateTimeImmutable
{ {
return $this->approvedAt; return $this->approvedAt;
} }
public function setApprovedAt(?\DateTimeInterface $approvedAt): void public function setApprovedAt(?\DateTimeImmutable $approvedAt): void
{ {
$this->approvedAt = $approvedAt; $this->approvedAt = $approvedAt;
} }

View File

@@ -9,15 +9,15 @@
namespace App\Model; namespace App\Model;
use DateTimeInterface; use DateTimeImmutable;
class Day class Day
{ {
public function __construct(private DateTimeInterface $day) public function __construct(private DateTimeImmutable $day)
{ {
} }
public function getDay(): DateTimeInterface public function getDay(): DateTimeImmutable
{ {
return $this->day; return $this->day;
} }

View File

@@ -20,7 +20,7 @@ class Month
public function __construct(private \DateTimeInterface $month) public function __construct(private \DateTimeInterface $month)
{ {
$date = new \DateTimeImmutable($this->month->format('Y-m-01 00:00:00')); $date = new \DateTimeImmutable($this->month->format('Y-m-01 00:00:00'), $month->getTimezone());
$start = $date->format('m'); $start = $date->format('m');
while ($start === $date->format('m')) { while ($start === $date->format('m')) {
$day = $this->createDay($date); $day = $this->createDay($date);
@@ -29,7 +29,7 @@ class Month
} }
} }
protected function createDay(\DateTimeInterface $day): Day protected function createDay(\DateTimeImmutable $day): Day
{ {
return new Day($day); return new Day($day);
} }

View File

@@ -20,17 +20,15 @@ class Year
public function __construct(private DateTimeInterface $month) public function __construct(private DateTimeInterface $month)
{ {
$monthDate = new \DateTimeImmutable(); $monthDate = new \DateTimeImmutable($this->month->format('Y-01-01 01:00:00'), $this->month->getTimezone());
$monthDate = $monthDate->setDate((int) $this->month->format('Y'), 1, 1);
$monthDate = $monthDate->setTime(1, 0);
for ($i = 1; $i < 13; $i++) { for ($i = 1; $i < 13; $i++) {
$month = $this->createMonth($monthDate); $tmp = $this->createMonth($monthDate);
$this->setMonth($month); $this->setMonth($tmp);
$monthDate = $monthDate->add(new \DateInterval('P1M')); $monthDate = $monthDate->add(new \DateInterval('P1M'));
} }
} }
protected function createMonth(\DateTimeInterface $month): Month protected function createMonth(\DateTimeImmutable $month): Month
{ {
return new Month($month); return new Month($month);
} }

View File

@@ -31,14 +31,113 @@ final class InvoicePolicy implements SecurityPolicyInterface
$this->policy->addPolicy(new SecurityPolicy( $this->policy->addPolicy(new SecurityPolicy(
['block', 'if', 'for', 'set', 'extends'], ['block', 'if', 'for', 'set', 'extends'],
[ [
// Twig core filters // =================================================================
'map', 'escape', 'trans', 'default', 'nl2br', 'trim', 'raw', // vendor/twig/twig/src/Extension/CoreExtension.php
'join', 'u', 'slice', 'date', 'month_name', 'first', 'country_name',
'replace', 'length', 'number_format', 'split',
// Kimai filters // formatting filters
'md2html', 'desc2html', 'comment2html', 'comment1line', 'multiline_indent', 'nl2str', 'date',
'date_short', 'duration', 'amount', 'money', 'duration_decimal', 'date_modify',
'format',
'replace',
'number_format',
'abs',
'round',
// encoding
'url_encode',
'json_encode',
'convert_encoding',
// string filters
'title',
'capitalize',
'upper',
'lower',
'striptags',
'trim',
'nl2br',
'spaceless',
// array helpers
'join',
'split',
'sort',
'merge',
'batch',
'column',
'filter',
'map',
'reduce',
// string/array filters
'reverse',
'length',
'slice',
'first',
'last',
// iteration and runtime
'default',
'keys',
// =================================================================
// vendor/twig/twig/src/Extension/EscaperExtension.php
'escape',
'e',
'raw',
// =================================================================
// vendor/symfony/twig-bridge/Extension/TranslationExtension.php
'trans',
// =================================================================
// vendor/twig/string-extra/StringExtension.php
'u',
'slug',
// =================================================================
// vendor/twig/intl-extra/IntlExtension.php
'country_name',
'currency_name',
'currency_symbol',
'language_name',
'locale_name',
'format_currency',
'format_number',
'format_*_number',
'format_datetime',
'format_date',
'format_time',
// =================================================================
// src/Twig/LocaleFormatExtensions.php
'month_name',
'day_name',
'date_short',
'date_time',
'date_full',
'date_format',
'date_weekday',
'time',
'duration',
'duration_decimal',
'money',
'amount',
// =================================================================
// src/Twig/RuntimeExtensions.php
'md2html',
'desc2html',
'comment2html',
'comment1line',
// =================================================================
// src/Twig/Extensions.php
'multiline_indent',
'color',
'font_contrast',
'default_color',
'nl2str',
], ],
[ [
PdfContext::class => ['setoption'], PdfContext::class => ['setoption'],
@@ -46,11 +145,37 @@ final class InvoicePolicy implements SecurityPolicyInterface
], ],
[], // properties [], // properties
[ [
// Twig core functions // =================================================================
'cycle', 'asset', 'range', // vendor/twig/twig/src/Extension/CoreExtension.php
'max',
'min',
'range',
'constant',
'cycle',
'random',
'date',
'asset',
'range',
// Kimai functions // =================================================================
'encore_entry_css_source', 'qr_code_data_uri', 'config', // vendor/symfony/twig-bridge/Extension/TranslationExtension.php
't',
// =================================================================
// vendor/symfony/webpack-encore-bundle/src/Twig/EntryFilesTwigExtension.php
'encore_entry_css_source',
// =================================================================
// vendor/symfony/twig-bridge/Extension/AssetExtension.php
'asset',
// =================================================================
// Twig/RuntimeExtensions.php
'qr_code_data_uri',
// =================================================================
// Twig/Configuration.php
'config',
] ]
)); ));
} }

View File

@@ -18,7 +18,7 @@ use App\Model\Month as BaseMonth;
*/ */
final class Month extends BaseMonth final class Month extends BaseMonth
{ {
public function __construct(\DateTimeInterface $month, private User $user) public function __construct(\DateTimeImmutable $month, private User $user)
{ {
parent::__construct($month); parent::__construct($month);
} }
@@ -67,7 +67,7 @@ final class Month extends BaseMonth
return null; return null;
} }
protected function createDay(\DateTimeInterface $day): Day protected function createDay(\DateTimeImmutable $day): Day
{ {
return new Day($day); return new Day($day);
} }

View File

@@ -28,7 +28,7 @@ final class Year extends BaseYear
return $this->user; return $this->user;
} }
protected function createMonth(\DateTimeInterface $month): Month protected function createMonth(\DateTimeImmutable $month): Month
{ {
return new Month($month, $this->user); return new Month($month, $this->user);
} }

View File

@@ -117,7 +117,8 @@ final class WorkingTimeService
} }
$workingTime->setApprovedBy($approver); $workingTime->setApprovedBy($approver);
$workingTime->setApprovedAt($approvalDate); // FIXME see calling method
$workingTime->setApprovedAt(\DateTimeImmutable::createFromInterface($approvalDate));
$this->workingTimeRepository->scheduleWorkingTimeUpdate($workingTime); $this->workingTimeRepository->scheduleWorkingTimeUpdate($workingTime);
} }

View File

@@ -24,14 +24,17 @@ class SystemConfigurationTest extends TestCase
* @param array $loaderSettings * @param array $loaderSettings
* @return SystemConfiguration * @return SystemConfiguration
*/ */
protected function getSut(array $settings, array $loaderSettings = []) protected function getSut(array $settings, array $loaderSettings = []): SystemConfiguration
{ {
$loader = new TestConfigLoader($loaderSettings); $loader = new TestConfigLoader($loaderSettings);
return SystemConfigurationFactory::create($loader, $settings); return SystemConfigurationFactory::create($loader, $settings);
} }
protected function getDefaultSettings() /**
* @return array<string, array<mixed>>
*/
protected function getDefaultSettings(): array
{ {
return [ return [
'timesheet' => [ 'timesheet' => [
@@ -106,7 +109,10 @@ class SystemConfigurationTest extends TestCase
]; ];
} }
protected function getDefaultLoaderSettings() /**
* @return array<Configuration>
*/
protected function getDefaultLoaderSettings(): array
{ {
return [ return [
(new Configuration())->setName('defaults.customer.timezone')->setValue('Russia/Moscov'), (new Configuration())->setName('defaults.customer.timezone')->setValue('Russia/Moscov'),
@@ -123,7 +129,7 @@ class SystemConfigurationTest extends TestCase
]; ];
} }
public function testDefaultWithoutLoader() public function testDefaultWithoutLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), []); $sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertEquals('Europe/London', $sut->find('defaults.customer.timezone')); $this->assertEquals('Europe/London', $sut->find('defaults.customer.timezone'));
@@ -133,7 +139,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals('Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,#ffffff,,|#000000', $sut->getThemeColorChoices()); $this->assertEquals('Maroon|#800000,Brown|#a52a2a,Red|#ff0000,Orange|#ffa500,#ffffff,,|#000000', $sut->getThemeColorChoices());
} }
public function testDefaultWithLoader() public function testDefaultWithLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings()); $sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
$this->assertEquals('Russia/Moscov', $sut->find('defaults.customer.timezone')); $this->assertEquals('Russia/Moscov', $sut->find('defaults.customer.timezone'));
@@ -143,7 +149,7 @@ class SystemConfigurationTest extends TestCase
$this->assertFalse($sut->isSamlActive()); $this->assertFalse($sut->isSamlActive());
} }
public function testDefaultWithMixedConfigs() public function testDefaultWithMixedConfigs(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), [ $sut = $this->getSut($this->getDefaultSettings(), [
(new Configuration())->setName('timesheet.rules.allow_future_times')->setValue(''), (new Configuration())->setName('timesheet.rules.allow_future_times')->setValue(''),
@@ -157,7 +163,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals('2020-03-27', $sut->getFinancialYearStart()); $this->assertEquals('2020-03-27', $sut->getFinancialYearStart());
} }
public function testOffsetUnsetThrowsException() public function testOffsetUnsetThrowsException(): void
{ {
$this->expectException(\BadMethodCallException::class); $this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('SystemBundleConfiguration does not support offsetUnset()'); $this->expectExceptionMessage('SystemBundleConfiguration does not support offsetUnset()');
@@ -166,7 +172,7 @@ class SystemConfigurationTest extends TestCase
$sut->offsetUnset('dfsdf'); $sut->offsetUnset('dfsdf');
} }
public function testUnknownConfigs() public function testUnknownConfigs(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), [ $sut = $this->getSut($this->getDefaultSettings(), [
(new Configuration())->setName('timesheet.foo')->setValue('hello'), (new Configuration())->setName('timesheet.foo')->setValue('hello'),
@@ -182,7 +188,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals('foooo-bar!', $sut->find('xxxxxxxx.yyyyyyyyy')); $this->assertEquals('foooo-bar!', $sut->find('xxxxxxxx.yyyyyyyyy'));
} }
public function testCalendarWithoutLoader() public function testCalendarWithoutLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), []); $sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertEquals('07:49', $sut->getCalendarBusinessTimeBegin()); $this->assertEquals('07:49', $sut->getCalendarBusinessTimeBegin());
@@ -199,7 +205,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals(2, \count($sources)); $this->assertEquals(2, \count($sources));
} }
public function testCalendarWithLoader() public function testCalendarWithLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings()); $sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
$this->assertEquals('00:30:00', $sut->getCalendarSlotDuration()); $this->assertEquals('00:30:00', $sut->getCalendarSlotDuration());
@@ -207,7 +213,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals(2, \count($sources)); $this->assertEquals(2, \count($sources));
} }
public function testFormDefaultWithoutLoader() public function testFormDefaultWithoutLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), []); $sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone()); $this->assertEquals('Europe/London', $sut->getCustomerDefaultTimezone());
@@ -220,7 +226,7 @@ class SystemConfigurationTest extends TestCase
$this->assertNull($sut->getFinancialYearStart()); $this->assertNull($sut->getFinancialYearStart());
} }
public function testFormDefaultWithLoader() public function testFormDefaultWithLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings()); $sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
$this->assertEquals('Russia/Moscov', $sut->getCustomerDefaultTimezone()); $this->assertEquals('Russia/Moscov', $sut->getCustomerDefaultTimezone());
@@ -232,7 +238,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals('USD', $sut->getUserDefaultCurrency()); $this->assertEquals('USD', $sut->getUserDefaultCurrency());
} }
public function testTimesheetWithoutLoader() public function testTimesheetWithoutLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), []); $sut = $this->getSut($this->getDefaultSettings(), []);
$this->assertEquals(99, $sut->getTimesheetActiveEntriesHardLimit()); $this->assertEquals(99, $sut->getTimesheetActiveEntriesHardLimit());
@@ -250,7 +256,7 @@ class SystemConfigurationTest extends TestCase
$this->assertEquals(5, $sut->getTimesheetIncrementMinutes()); $this->assertEquals(5, $sut->getTimesheetIncrementMinutes());
} }
public function testTimesheetWithLoader() public function testTimesheetWithLoader(): void
{ {
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings()); $sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
$this->assertEquals(7, $sut->getTimesheetActiveEntriesHardLimit()); $this->assertEquals(7, $sut->getTimesheetActiveEntriesHardLimit());

View File

@@ -38,7 +38,7 @@ class LayoutControllerTest extends ControllerBaseTest
$this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '"', $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() . '/edit"', $content);
$this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '/prefs"', $content); $this->assertStringContainsString('href="/en/profile/' . $user->getUserIdentifier() . '/prefs"', $content);
$this->assertStringContainsString('href="/en/logout"', $content); $this->assertStringContainsString('href="/en/logout?_csrf_token=', $content);
} }
protected function assertHasNavigation(HttpKernelBrowser $client) protected function assertHasNavigation(HttpKernelBrowser $client)

View File

@@ -183,7 +183,7 @@ class TimesheetTest extends TestCase
$sut->setExported(true); $sut->setExported(true);
$sut->setDescription('Invalid timesheet category "foo" given, expected one of: work, holiday, sickness, parental, overtime'); $sut->setDescription('Invalid timesheet category "foo" given, expected one of: work, holiday, sickness, parental, overtime');
$modifiedDate = new \DateTime(); $modifiedDate = new \DateTimeImmutable();
$reflection = new \ReflectionClass($sut); $reflection = new \ReflectionClass($sut);
$property = $reflection->getProperty('modifiedAt'); $property = $reflection->getProperty('modifiedAt');

View File

@@ -219,7 +219,7 @@ class UserTest extends TestCase
public function testPasswordRequestedAt(): void public function testPasswordRequestedAt(): void
{ {
$date = new \DateTime('-60 minutes'); $date = new \DateTimeImmutable('-60 minutes');
$sut = new User(); $sut = new User();
self::assertFalse($sut->isPasswordRequestNonExpired(3599)); self::assertFalse($sut->isPasswordRequestNonExpired(3599));

View File

@@ -22,7 +22,7 @@ class WorkingTimeTest extends TestCase
{ {
$user = new User(); $user = new User();
$user->setUsername('bar'); $user->setUsername('bar');
$date = new \DateTime(); $date = new \DateTimeImmutable();
$sut = new WorkingTime($user, $date); $sut = new WorkingTime($user, $date);
self::assertSame($user, $sut->getUser()); self::assertSame($user, $sut->getUser());
@@ -36,7 +36,7 @@ class WorkingTimeTest extends TestCase
self::assertNull($sut->getApprovedBy()); self::assertNull($sut->getApprovedBy());
self::assertFalse($sut->isApproved()); self::assertFalse($sut->isApproved());
$approvedAt = new \DateTime('2023-01-01 12:00:00', new \DateTimeZone('Europe/Vienna')); $approvedAt = new \DateTimeImmutable('2023-01-01 12:00:00', new \DateTimeZone('Europe/Vienna'));
$approvedBy = new User(); $approvedBy = new User();
$approvedBy->setUsername('foo'); $approvedBy->setUsername('foo');

View File

@@ -1922,16 +1922,6 @@ parameters:
count: 1 count: 1
path: Configuration/SamlConfigurationTest.php path: Configuration/SamlConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:getDefaultLoaderSettings\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:getDefaultSettings\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
- -
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:getSut\\(\\) has parameter \\$loaderSettings with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:getSut\\(\\) has parameter \\$loaderSettings with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -1942,61 +1932,6 @@ parameters:
count: 1 count: 1
path: Configuration/SystemConfigurationTest.php path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testCalendarWithLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testCalendarWithoutLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testDefaultWithLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testDefaultWithMixedConfigs\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testDefaultWithoutLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testFormDefaultWithLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testFormDefaultWithoutLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testOffsetUnsetThrowsException\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testTimesheetWithLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testTimesheetWithoutLoader\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
-
message: "#^Method App\\\\Tests\\\\Configuration\\\\SystemConfigurationTest\\:\\:testUnknownConfigs\\(\\) has no return type specified\\.$#"
count: 1
path: Configuration/SystemConfigurationTest.php
- -
message: "#^Method App\\\\Tests\\\\ConsoleApplicationTest\\:\\:testVersion\\(\\) has no return type specified\\.$#" message: "#^Method App\\\\Tests\\\\ConsoleApplicationTest\\:\\:testVersion\\(\\) has no return type specified\\.$#"
count: 1 count: 1