diff --git a/composer.json b/composer.json index f08bf134..e0225dff 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "hslavich/oneloginsaml-bundle": "^1.4", "jms/metadata": "^2.0", "jms/serializer-bundle": "^3.2", - "kevinpapst/adminlte-bundle": "^3.0", + "kevinpapst/adminlte-bundle": "^3.3", "kimai/user-bundle": "^1.1", "laravolt/avatar": "^3.0", "league/csv": "^9.4", diff --git a/composer.lock b/composer.lock index a2608e3e..47a285d8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7931f435fc1cfba7a09fe9d65369984e", + "content-hash": "25136f17833c529f24e50535c155dda5", "packages": [ { "name": "beberlei/doctrineextensions", @@ -2795,40 +2795,42 @@ }, { "name": "kevinpapst/adminlte-bundle", - "version": "3.2.4", + "version": "3.3", "source": { "type": "git", "url": "https://github.com/kevinpapst/AdminLTEBundle.git", - "reference": "c7cd7c5f7f3f49fae9df24c70e175be77c2cab61" + "reference": "67183f1684c71620cd42901539b7196d97f90e2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kevinpapst/AdminLTEBundle/zipball/c7cd7c5f7f3f49fae9df24c70e175be77c2cab61", - "reference": "c7cd7c5f7f3f49fae9df24c70e175be77c2cab61", + "url": "https://api.github.com/repos/kevinpapst/AdminLTEBundle/zipball/67183f1684c71620cd42901539b7196d97f90e2c", + "reference": "67183f1684c71620cd42901539b7196d97f90e2c", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/config": ">4.1", - "symfony/dependency-injection": ">4.0", + "php": ">=7.1.3", + "symfony/asset": ">4.3", + "symfony/config": ">4.3", + "symfony/dependency-injection": ">4.3", "symfony/event-dispatcher": ">4.3", - "symfony/http-foundation": ">4.0", - "symfony/http-kernel": ">4.0", - "symfony/options-resolver": ">4.0", - "symfony/security-core": ">4.0", + "symfony/http-foundation": ">4.3", + "symfony/http-kernel": ">4.3", + "symfony/options-resolver": ">4.3", + "symfony/security-core": ">4.3", + "symfony/translation": ">4.3", + "symfony/twig-bridge": ">4.3", "twig/twig": ">2.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.10", "knplabs/knp-menu-bundle": "^2.2", "phpspec/prophecy": "^1.6", - "phpstan/phpstan": "^0.11.8", - "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", "phpunit/phpunit": "^7.3", - "symfony/framework-bundle": ">4.0" + "symfony/framework-bundle": ">4.3" }, "suggest": { - "friendsofsymfony/user-bundle": "Allows easy user management and security support", "knplabs/knp-menu-bundle": "Allows easy menu integration" }, "type": "symfony-bundle", @@ -2858,7 +2860,7 @@ "description": "Admin theme bundle for Symfony 4+ based on AdminLTE 2.4 with FOSUserBundle support", "support": { "issues": "https://github.com/kevinpapst/AdminLTEBundle/issues", - "source": "https://github.com/kevinpapst/AdminLTEBundle/tree/3.2.4" + "source": "https://github.com/kevinpapst/AdminLTEBundle/tree/3.3" }, "funding": [ { @@ -2870,7 +2872,7 @@ "type": "github" } ], - "time": "2020-07-07T11:40:52+00:00" + "time": "2021-02-20T16:05:21+00:00" }, { "name": "kimai/user-bundle", diff --git a/src/Controller/LayoutController.php b/src/Controller/LayoutController.php deleted file mode 100644 index ad8a4171..00000000 --- a/src/Controller/LayoutController.php +++ /dev/null @@ -1,39 +0,0 @@ -getUser(); - $activeEntries = $repository->getActiveEntries($user); - - $recentActivity = new RecentActivityEvent($this->getUser(), $activeEntries); - $dispatcher->dispatch($recentActivity); - - return $this->render( - 'navbar/active-entries.html.twig', - [ - 'entries' => $recentActivity->getRecentActivities(), - 'soft_limit' => $configuration->getTimesheetActiveEntriesSoftLimit(), - ] - ); - } -} diff --git a/src/EventSubscriber/ThemeOptionsSubscriber.php b/src/EventSubscriber/ThemeOptionsSubscriber.php index 34f9ea72..e9e717b0 100644 --- a/src/EventSubscriber/ThemeOptionsSubscriber.php +++ b/src/EventSubscriber/ThemeOptionsSubscriber.php @@ -20,22 +20,17 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt /** * Allows dynamic injection of theme related options. */ -class ThemeOptionsSubscriber implements EventSubscriberInterface +final class ThemeOptionsSubscriber implements EventSubscriberInterface { /** * @var TokenStorageInterface */ - protected $storage; - + private $storage; /** * @var ContextHelper */ - protected $helper; + private $helper; - /** - * @param TokenStorageInterface $storage - * @param ContextHelper $helper - */ public function __construct(TokenStorageInterface $storage, ContextHelper $helper) { $this->storage = $storage; @@ -52,18 +47,24 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface ]; } - /** - * @param KernelEvent $event - */ - public function setThemeOptions(KernelEvent $event) + public function setThemeOptions(KernelEvent $event): void { - if (!$this->canHandleEvent($event)) { + // Ignore sub-requests + if (!$event->isMasterRequest()) { + return; + } + + // ignore events like the toolbar where we do not have a token + if (null === $this->storage->getToken()) { return; } - /** @var User $user */ $user = $this->storage->getToken()->getUser(); + if (!($user instanceof User)) { + return; + } + /** @var UserPreference $ref */ foreach ($user->getPreferences() as $ref) { $name = $ref->getName(); @@ -90,26 +91,4 @@ class ThemeOptionsSubscriber implements EventSubscriberInterface } } } - - /** - * @param KernelEvent $event - * @return bool - */ - protected function canHandleEvent(KernelEvent $event): bool - { - // Ignore sub-requests - if (!$event->isMasterRequest()) { - return false; - } - - // ignore events like the toolbar where we do not have a token - if (null === $this->storage->getToken()) { - return false; - } - - /** @var User $user */ - $user = $this->storage->getToken()->getUser(); - - return ($user instanceof User); - } } diff --git a/src/EventSubscriber/UserEnvironmentSubscriber.php b/src/EventSubscriber/UserEnvironmentSubscriber.php index 6e8f2df9..02f52a53 100644 --- a/src/EventSubscriber/UserEnvironmentSubscriber.php +++ b/src/EventSubscriber/UserEnvironmentSubscriber.php @@ -16,7 +16,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -class UserEnvironmentSubscriber implements EventSubscriberInterface +final class UserEnvironmentSubscriber implements EventSubscriberInterface { /** * @var TokenStorageInterface @@ -40,20 +40,22 @@ class UserEnvironmentSubscriber implements EventSubscriberInterface ]; } - public function prepareEnvironment(RequestEvent $event) + public function prepareEnvironment(RequestEvent $event): void { + // ignore sub-requests if (!$event->isMasterRequest()) { return; } - if (null === $this->storage->getToken()) { + // the locale depends on the request, not on the user configuration + \Locale::setDefault($event->getRequest()->getLocale()); + + // ignore events like the toolbar where we do not have a token + if (null === ($token = $this->storage->getToken())) { return; } - $user = $this->storage->getToken()->getUser(); - - // the locale depends on the request, not on the user configuration - \Locale::setDefault($event->getRequest()->getLocale()); + $user = $token->getUser(); if ($user instanceof User) { date_default_timezone_set($user->getTimezone()); diff --git a/src/EventSubscriber/UserPreferenceSubscriber.php b/src/EventSubscriber/UserPreferenceSubscriber.php index 7098df02..3f955d21 100644 --- a/src/EventSubscriber/UserPreferenceSubscriber.php +++ b/src/EventSubscriber/UserPreferenceSubscriber.php @@ -28,20 +28,20 @@ use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Validator\Constraints\Range; -class UserPreferenceSubscriber implements EventSubscriberInterface +final class UserPreferenceSubscriber implements EventSubscriberInterface { /** * @var EventDispatcherInterface */ - protected $eventDispatcher; + private $eventDispatcher; /** * @var AuthorizationCheckerInterface */ - protected $voter; + private $voter; /** * @var SystemConfiguration */ - protected $configuration; + private $configuration; public function __construct(EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter, SystemConfiguration $formConfig) { @@ -57,43 +57,23 @@ class UserPreferenceSubscriber implements EventSubscriberInterface ]; } - private function getDefaultTheme(): ?string - { - return $this->configuration->getUserDefaultTheme(); - } - - private function getDefaultCurrency(): string - { - return $this->configuration->getUserDefaultCurrency(); - } - - private function getDefaultLanguage(): string - { - return $this->configuration->getUserDefaultLanguage(); - } - - private function getDefaultTimezone(): string - { - $timezone = $this->configuration->getUserDefaultTimezone(); - if (null === $timezone) { - $timezone = date_default_timezone_get(); - } - - return $timezone; - } - /** * @param User $user * @return UserPreference[] */ public function getDefaultPreferences(User $user) { + $timezone = $this->configuration->getUserDefaultTimezone(); + if (null === $timezone) { + $timezone = date_default_timezone_get(); + } + $enableHourlyRate = false; $hourlyRateOptions = []; if ($this->voter->isGranted('hourly-rate', $user)) { $enableHourlyRate = true; - $hourlyRateOptions = ['currency' => $this->getDefaultCurrency()]; + $hourlyRateOptions = ['currency' => $this->configuration->getUserDefaultCurrency()]; } return [ @@ -119,14 +99,14 @@ class UserPreferenceSubscriber implements EventSubscriberInterface (new UserPreference()) ->setName(UserPreference::TIMEZONE) - ->setValue($this->getDefaultTimezone()) + ->setValue($timezone) ->setOrder(200) ->setSection('locale') ->setType(TimezoneType::class), (new UserPreference()) ->setName(UserPreference::LOCALE) - ->setValue($this->getDefaultLanguage()) + ->setValue($this->configuration->getUserDefaultLanguage()) ->setOrder(250) ->setSection('locale') ->setType(LanguageType::class), @@ -140,7 +120,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface (new UserPreference()) ->setName(UserPreference::SKIN) - ->setValue($this->getDefaultTheme()) + ->setValue($this->configuration->getUserDefaultTheme()) ->setOrder(400) ->setSection('theme') ->setType(SkinType::class), diff --git a/src/EventSubscriber/UserProfileSubscriber.php b/src/EventSubscriber/UserProfileSubscriber.php index 30eff558..ec31c4bb 100644 --- a/src/EventSubscriber/UserProfileSubscriber.php +++ b/src/EventSubscriber/UserProfileSubscriber.php @@ -43,32 +43,21 @@ final class UserProfileSubscriber implements EventSubscriberInterface public function prepareUserProfile(KernelEvent $event): void { - if (!$this->canHandleEvent($event)) { + // ignore sub-requests + if (!$event->isMasterRequest()) { return; } - /** @var User $user */ - $user = $this->storage->getToken()->getUser(); - - $event = new PrepareUserEvent($user); - $this->eventDispatcher->dispatch($event); - } - - private function canHandleEvent(KernelEvent $event): bool - { - // Ignore sub-requests - if (!$event->isMasterRequest()) { - return false; - } - // ignore events like the toolbar where we do not have a token - if (null === $this->storage->getToken()) { - return false; + if (null === ($token = $this->storage->getToken())) { + return; } - /** @var User $user */ - $user = $this->storage->getToken()->getUser(); + $user = $token->getUser(); - return ($user instanceof User); + if ($user instanceof User) { + $event = new PrepareUserEvent($user); + $this->eventDispatcher->dispatch($event); + } } } diff --git a/src/Export/Base/AbstractSpreadsheetRenderer.php b/src/Export/Base/AbstractSpreadsheetRenderer.php index bfc699e6..daa0ff1e 100644 --- a/src/Export/Base/AbstractSpreadsheetRenderer.php +++ b/src/Export/Base/AbstractSpreadsheetRenderer.php @@ -19,7 +19,7 @@ use App\Event\UserPreferenceDisplayEvent; use App\Export\ExportItemInterface; use App\Repository\Query\CustomerQuery; use App\Repository\Query\TimesheetQuery; -use App\Twig\DateExtensions; +use App\Twig\LocaleFormatExtensions; use DateTime; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Shared\Date; @@ -47,7 +47,7 @@ abstract class AbstractSpreadsheetRenderer public const RATE_FORMAT = self::RATE_FORMAT_LEFT; /** - * @var DateExtensions + * @var LocaleFormatExtensions */ protected $dateExtension; /** @@ -92,7 +92,7 @@ abstract class AbstractSpreadsheetRenderer 'user-meta' => [], ]; - public function __construct(TranslatorInterface $translator, DateExtensions $dateExtension, EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter) + public function __construct(TranslatorInterface $translator, LocaleFormatExtensions $dateExtension, EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter) { $this->translator = $translator; $this->dateExtension = $dateExtension; diff --git a/src/Invoice/Renderer/AbstractTwigRenderer.php b/src/Invoice/Renderer/AbstractTwigRenderer.php index 89bd2d4a..4f65c365 100644 --- a/src/Invoice/Renderer/AbstractTwigRenderer.php +++ b/src/Invoice/Renderer/AbstractTwigRenderer.php @@ -12,8 +12,7 @@ namespace App\Invoice\Renderer; use App\Entity\InvoiceDocument; use App\Invoice\InvoiceModel; use App\Invoice\RendererInterface; -use App\Twig\DateExtensions; -use App\Twig\LocaleExtensions; +use App\Twig\LocaleFormatExtensions; use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Contracts\Translation\LocaleAwareInterface; use Twig\Environment; @@ -61,12 +60,8 @@ abstract class AbstractTwigRenderer implements RendererInterface $translator->setLocale($locale); - /** @var LocaleExtensions $extension */ - $extension = $twig->getExtension(LocaleExtensions::class); - $extension->setLocale($locale); - - /** @var DateExtensions $extension */ - $extension = $twig->getExtension(DateExtensions::class); + /** @var LocaleFormatExtensions $extension */ + $extension = $twig->getExtension(LocaleFormatExtensions::class); $extension->setLocale($locale); return $previousLocale; diff --git a/src/Repository/WidgetRepository.php b/src/Repository/WidgetRepository.php index 3e693fd9..f48003fb 100644 --- a/src/Repository/WidgetRepository.php +++ b/src/Repository/WidgetRepository.php @@ -31,17 +31,30 @@ class WidgetRepository /** * @var array */ - private $definitions = []; + private $definitions; + /** + * @var array + */ + private $customDefinition; public function __construct(TimesheetRepository $repository, array $widgets) { $this->repository = $repository; - $this->definitions = array_merge($this->getDefaultWidgets(), $widgets); + $this->customDefinition = $widgets; + } + + private function getDefinedWidgets(): array + { + if (null === $this->definitions) { + $this->definitions = array_merge($this->getDefaultWidgets(), $this->customDefinition); + } + + return $this->definitions; } public function has(string $id): bool { - return isset($this->definitions[$id]) || isset($this->widgets[$id]); + return isset($this->getDefinedWidgets()[$id]) || isset($this->widgets[$id]); } public function registerWidget(WidgetInterface $widget): WidgetRepository @@ -64,7 +77,7 @@ class WidgetRepository } // this code should ONLY be reached for internal (pre-registered) widgets - $this->registerWidget($this->create($id, $this->definitions[$id])); + $this->registerWidget($this->create($id, $this->getDefinedWidgets()[$id])); return $this->widgets[$id]; } diff --git a/src/Twig/ConfigExtension.php b/src/Twig/ConfigExtension.php index ebbeae04..9017cbdb 100644 --- a/src/Twig/ConfigExtension.php +++ b/src/Twig/ConfigExtension.php @@ -9,18 +9,18 @@ namespace App\Twig; -use App\Configuration\ThemeConfiguration; +use App\Configuration\SystemConfiguration; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -class ConfigExtension extends AbstractExtension +final class ConfigExtension extends AbstractExtension { /** - * @var ThemeConfiguration + * @var SystemConfiguration */ - protected $configuration; + private $configuration; - public function __construct(ThemeConfiguration $configuration) + public function __construct(SystemConfiguration $configuration) { $this->configuration = $configuration; } @@ -41,6 +41,15 @@ class ConfigExtension extends AbstractExtension */ public function getThemeConfig(string $name) { + switch ($name) { + case 'soft_limit': + return $this->configuration->getTimesheetActiveEntriesSoftLimit(); + + default: + $name = 'theme.' . $name; + break; + } + return $this->configuration->find($name); } } diff --git a/src/Twig/DateExtensions.php b/src/Twig/DateExtensions.php deleted file mode 100644 index d99640a9..00000000 --- a/src/Twig/DateExtensions.php +++ /dev/null @@ -1,189 +0,0 @@ -getMasterRequest()) { - $locale = $requestStack->getMasterRequest()->getLocale(); - } - - $this->formats = $formats; - $this->setLocale($locale); - } - - /** - * {@inheritdoc} - */ - public function getFilters() - { - return [ - new TwigFilter('month_name', [$this, 'monthName']), - new TwigFilter('day_name', [$this, 'dayName']), - new TwigFilter('date_short', [$this, 'dateShort']), - new TwigFilter('date_time', [$this, 'dateTime']), - new TwigFilter('date_full', [$this, 'dateTimeFull']), - new TwigFilter('date_format', [$this, 'dateFormat']), - new TwigFilter('time', [$this, 'time']), - new TwigFilter('hour24', [$this, 'hour24']), - ]; - } - - public function getTests() - { - return [ - new TwigTest('weekend', function ($dateTime) { - if (!$dateTime instanceof \DateTime) { - return false; - } - $day = (int) $dateTime->format('w'); - - return ($day === 0 || $day === 6); - }), - ]; - } - - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('get_format_duration', [$this, 'getDurationFormat']), - new TwigFunction('create_date', [$this, 'createDate']), - ]; - } - - /** - * Allows to switch the locale used for all twig filter and functions. - * - * @param string $locale - */ - public function setLocale(string $locale) - { - $this->formatter = new LocaleFormatter($this->formats, $locale); - $this->localeFormats = new LocaleFormats($this->formats, $locale); - } - - /** - * @param DateTime|string $date - * @return string - */ - public function dateShort($date) - { - return $this->formatter->dateShort($date); - } - - /** - * @param DateTime|string $date - * @return string - */ - public function dateTime($date) - { - return $this->formatter->dateTime($date); - } - - /** - * @param DateTime|string $date - * @return bool|false|string - */ - public function dateTimeFull($date) - { - return $this->formatter->dateTimeFull($date); - } - - public function createDate(string $date, ?User $user = null): \DateTime - { - $timezone = $user !== null ? $user->getTimezone() : date_default_timezone_get(); - - return new DateTime($date, new \DateTimeZone($timezone)); - } - - /** - * @param DateTime|string $date - * @param string $format - * @return false|string - * @throws \Exception - */ - public function dateFormat($date, string $format) - { - return $this->formatter->dateFormat($date, $format); - } - - /** - * @param DateTime|string $date - * @return string - */ - public function time($date) - { - return $this->formatter->time($date); - } - - public function monthName(\DateTime $dateTime, bool $withYear = false): string - { - return $this->formatter->monthName($dateTime, $withYear); - } - - public function dayName(\DateTime $dateTime, bool $short = false): string - { - return $this->formatter->dayName($dateTime, $short); - } - - /** - * @param mixed $twentyFour - * @param mixed $twelveHour - * @return mixed - */ - public function hour24($twentyFour, $twelveHour) - { - return $this->formatter->hour24($twentyFour, $twelveHour); - } - - /** - * @return string - */ - public function getDurationFormat() - { - return $this->localeFormats->getDurationFormat(); - } -} diff --git a/src/Twig/EventExtensions.php b/src/Twig/EventExtensions.php deleted file mode 100644 index 7c2e52e0..00000000 --- a/src/Twig/EventExtensions.php +++ /dev/null @@ -1,28 +0,0 @@ -getMasterRequest()) { - $locale = $requestStack->getMasterRequest()->getLocale(); - } - - $this->formats = $formats; - $this->setLocale($locale); - } - - /** - * {@inheritdoc} - */ - public function getFilters() - { - return [ - new TwigFilter('duration', [$this, 'duration']), - new TwigFilter('duration_decimal', [$this, 'durationDecimal']), - new TwigFilter('money', [$this, 'money']), - new TwigFilter('currency', [$this, 'currency']), - new TwigFilter('country', [$this, 'country']), - new TwigFilter('language', [$this, 'language']), - new TwigFilter('amount', [$this, 'amount']), - ]; - } - - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('locales', [$this, 'getLocales']), - ]; - } - - /** - * Allows to switch the locale used for all twig filter and functions. - * - * @param string $locale - */ - public function setLocale(string $locale) - { - $this->formatter = new LocaleFormatter($this->formats, $locale); - } - - /** - * Transforms seconds into a duration string. - * - * @param int|Timesheet|null $duration - * @param bool $decimal - * @return string - */ - public function duration($duration, $decimal = false) - { - return $this->formatter->duration($duration, $decimal); - } - - /** - * Transforms seconds into a decimal formatted duration string. - * - * @param int|Timesheet|null $duration - * @return string - */ - public function durationDecimal($duration) - { - return $this->formatter->durationDecimal($duration); - } - - /** - * @param string|float $amount - * @return bool|false|string - */ - public function amount($amount) - { - return $this->formatter->amount($amount); - } - - /** - * Returns the currency symbol. - * - * @param string $currency - * @return string - */ - public function currency($currency) - { - return $this->formatter->currency($currency); - } - - /** - * @param string $language - * @return string - */ - public function language($language) - { - return $this->formatter->language($language); - } - - /** - * @param string $country - * @return string - */ - public function country($country) - { - return $this->formatter->country($country); - } - - /** - * @param float $amount - * @param string|null $currency - * @param bool $withCurrency - * @return string - */ - public function money($amount, ?string $currency = null, bool $withCurrency = true) - { - return $this->formatter->money($amount, $currency, $withCurrency); - } - - /** - * Takes the list of codes of the locales (languages) enabled in the - * application and returns an array with the name of each locale written - * in its own language (e.g. English, Français, Español, etc.) - * - * @return array - */ - public function getLocales() - { - return $this->formatter->getLocales(); - } -} diff --git a/src/Twig/LocaleFormatExtensions.php b/src/Twig/LocaleFormatExtensions.php new file mode 100644 index 00000000..17edd729 --- /dev/null +++ b/src/Twig/LocaleFormatExtensions.php @@ -0,0 +1,298 @@ +formats = $formats; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return [ + new TwigFilter('month_name', [$this, 'monthName']), + new TwigFilter('day_name', [$this, 'dayName']), + new TwigFilter('date_short', [$this, 'dateShort']), + new TwigFilter('date_time', [$this, 'dateTime']), + new TwigFilter('date_full', [$this, 'dateTimeFull']), + new TwigFilter('date_format', [$this, 'dateFormat']), + new TwigFilter('time', [$this, 'time']), + new TwigFilter('hour24', [$this, 'hour24']), + new TwigFilter('duration', [$this, 'duration']), + new TwigFilter('duration_decimal', [$this, 'durationDecimal']), + new TwigFilter('money', [$this, 'money']), + new TwigFilter('currency', [$this, 'currency']), + new TwigFilter('country', [$this, 'country']), + new TwigFilter('language', [$this, 'language']), + new TwigFilter('amount', [$this, 'amount']), + ]; + } + + public function getTests() + { + return [ + new TwigTest('weekend', function ($dateTime) { + if (!$dateTime instanceof \DateTime) { + return false; + } + $day = (int) $dateTime->format('w'); + + return ($day === 0 || $day === 6); + }), + ]; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() + { + return [ + new TwigFunction('get_format_duration', [$this, 'getDurationFormat']), + new TwigFunction('create_date', [$this, 'createDate']), + new TwigFunction('locales', [$this, 'getLocales']), + ]; + } + + /** + * Allows to switch the locale used for all twig filter and functions. + * + * @param string $locale + */ + public function setLocale(string $locale) + { + $this->locale = $locale; + $this->formatter = null; + $this->localeFormats = null; + } + + private function getLocaleFormats(): LocaleFormats + { + if (null === $this->localeFormats) { + $this->localeFormats = new LocaleFormats($this->formats, $this->getLocale()); + } + + return $this->localeFormats; + } + + private function getFormatter(): LocaleFormatter + { + if (null === $this->formatter) { + $this->formatter = new LocaleFormatter($this->formats, $this->getLocale()); + } + + return $this->formatter; + } + + private function getLocale() + { + if (null === $this->locale) { + $this->locale = \Locale::getDefault(); + } + + return $this->locale; + } + + /** + * @param DateTime|string $date + * @return string + */ + public function dateShort($date) + { + return $this->getFormatter()->dateShort($date); + } + + /** + * @param DateTime|string $date + * @return string + */ + public function dateTime($date) + { + return $this->getFormatter()->dateTime($date); + } + + /** + * @param DateTime|string $date + * @return bool|false|string + */ + public function dateTimeFull($date) + { + return $this->getFormatter()->dateTimeFull($date); + } + + public function createDate(string $date, ?User $user = null): \DateTime + { + $timezone = $user !== null ? $user->getTimezone() : date_default_timezone_get(); + + return new DateTime($date, new \DateTimeZone($timezone)); + } + + /** + * @param DateTime|string $date + * @param string $format + * @return false|string + * @throws \Exception + */ + public function dateFormat($date, string $format) + { + return $this->getFormatter()->dateFormat($date, $format); + } + + /** + * @param DateTime|string $date + * @return string + */ + public function time($date) + { + return $this->getFormatter()->time($date); + } + + public function monthName(\DateTime $dateTime, bool $withYear = false): string + { + return $this->getFormatter()->monthName($dateTime, $withYear); + } + + public function dayName(\DateTime $dateTime, bool $short = false): string + { + return $this->getFormatter()->dayName($dateTime, $short); + } + + /** + * @param mixed $twentyFour + * @param mixed $twelveHour + * @return mixed + */ + public function hour24($twentyFour, $twelveHour) + { + return $this->getFormatter()->hour24($twentyFour, $twelveHour); + } + + public function getDurationFormat(): string + { + return $this->getLocaleFormats()->getDurationFormat(); + } + + /** + * Transforms seconds into a duration string. + * + * @param int|Timesheet|null $duration + * @param bool $decimal + * @return string + */ + public function duration($duration, $decimal = false) + { + return $this->getFormatter()->duration($duration, $decimal); + } + + /** + * Transforms seconds into a decimal formatted duration string. + * + * @param int|Timesheet|null $duration + * @return string + */ + public function durationDecimal($duration) + { + return $this->getFormatter()->durationDecimal($duration); + } + + /** + * @param string|float $amount + * @return bool|false|string + */ + public function amount($amount) + { + return $this->getFormatter()->amount($amount); + } + + /** + * Returns the currency symbol. + * + * @param string $currency + * @return string + */ + public function currency($currency) + { + return $this->getFormatter()->currency($currency); + } + + /** + * @param string $language + * @return string + */ + public function language($language) + { + return $this->getFormatter()->language($language); + } + + /** + * @param string $country + * @return string + */ + public function country($country) + { + return $this->getFormatter()->country($country); + } + + /** + * @param float $amount + * @param string|null $currency + * @param bool $withCurrency + * @return string + */ + public function money($amount, ?string $currency = null, bool $withCurrency = true) + { + return $this->getFormatter()->money($amount, $currency, $withCurrency); + } + + /** + * Takes the list of codes of the locales (languages) enabled in the + * application and returns an array with the name of each locale written + * in its own language (e.g. English, Français, Español, etc.) + * + * @return array + */ + public function getLocales() + { + return $this->getFormatter()->getLocales(); + } +} diff --git a/src/Twig/PaginationExtension.php b/src/Twig/PaginationExtension.php index 879cd0dd..f6a5c6b8 100644 --- a/src/Twig/PaginationExtension.php +++ b/src/Twig/PaginationExtension.php @@ -31,7 +31,6 @@ class PaginationExtension extends AbstractExtension public function __construct(UrlGeneratorInterface $router) { - $this->view = new TwitterBootstrap3View(); $this->router = $router; } @@ -46,6 +45,15 @@ class PaginationExtension extends AbstractExtension ]; } + private function getView(): ViewInterface + { + if (null === $this->view) { + $this->view = new TwitterBootstrap3View(); + } + + return $this->view; + } + /** * @deprecated since 1.8 */ @@ -70,7 +78,7 @@ class PaginationExtension extends AbstractExtension $options['prev_message'] = ''; $options['next_message'] = ''; - return $this->view->render($pagerfanta, $routeGenerator, $options); + return $this->getView()->render($pagerfanta, $routeGenerator, $options); } private function createRouteGenerator(array $options = []) diff --git a/src/Twig/EncoreExtension.php b/src/Twig/Runtime/EncoreExtension.php similarity index 76% rename from src/Twig/EncoreExtension.php rename to src/Twig/Runtime/EncoreExtension.php index 69c34063..e922655c 100644 --- a/src/Twig/EncoreExtension.php +++ b/src/Twig/Runtime/EncoreExtension.php @@ -7,15 +7,14 @@ * file that was distributed with this source code. */ -namespace App\Twig; +namespace App\Twig\Runtime; use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; +use Twig\Extension\RuntimeExtensionInterface; -class EncoreExtension extends AbstractExtension implements ServiceSubscriberInterface +final class EncoreExtension implements RuntimeExtensionInterface, ServiceSubscriberInterface { /** * @var string @@ -39,16 +38,6 @@ class EncoreExtension extends AbstractExtension implements ServiceSubscriberInte ]; } - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('encore_entry_css_source', [$this, 'getEncoreEntryCssSource']), - ]; - } - public function getEncoreEntryCssSource(string $packageName): string { $lookup = $this->container->get(EntrypointLookupInterface::class); diff --git a/src/Twig/TimesheetExtension.php b/src/Twig/Runtime/ExporterExtension.php similarity index 65% rename from src/Twig/TimesheetExtension.php rename to src/Twig/Runtime/ExporterExtension.php index 1646746e..1fdb18d5 100644 --- a/src/Twig/TimesheetExtension.php +++ b/src/Twig/Runtime/ExporterExtension.php @@ -7,13 +7,12 @@ * file that was distributed with this source code. */ -namespace App\Twig; +namespace App\Twig\Runtime; use App\Export\ServiceExport; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; +use Twig\Extension\RuntimeExtensionInterface; -class TimesheetExtension extends AbstractExtension +final class ExporterExtension implements RuntimeExtensionInterface { /** * @var ServiceExport @@ -25,16 +24,6 @@ class TimesheetExtension extends AbstractExtension $this->service = $service; } - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('timesheet_exporter', [$this, 'getTimesheetExporter'], []), - ]; - } - public function getTimesheetExporter(): array { $ids = []; diff --git a/src/Twig/MarkdownExtension.php b/src/Twig/Runtime/MarkdownExtension.php similarity index 70% rename from src/Twig/MarkdownExtension.php rename to src/Twig/Runtime/MarkdownExtension.php index 1f28b925..1437c186 100644 --- a/src/Twig/MarkdownExtension.php +++ b/src/Twig/Runtime/MarkdownExtension.php @@ -7,17 +7,13 @@ * file that was distributed with this source code. */ -namespace App\Twig; +namespace App\Twig\Runtime; use App\Configuration\SystemConfiguration; use App\Utils\Markdown; -use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; +use Twig\Extension\RuntimeExtensionInterface; -/** - * A twig extension to handle markdown content. - */ -final class MarkdownExtension extends AbstractExtension +final class MarkdownExtension implements RuntimeExtensionInterface { /** * @var Markdown @@ -27,26 +23,24 @@ final class MarkdownExtension extends AbstractExtension * @var SystemConfiguration */ private $configuration; - /** - * @param Markdown $parser + * @var bool|null */ + private $markdownEnabled; + public function __construct(Markdown $parser, SystemConfiguration $configuration) { $this->markdown = $parser; $this->configuration = $configuration; } - /** - * @return TwigFilter[] - */ - public function getFilters() + private function isMarkdownEnabled(): bool { - return [ - new TwigFilter('md2html', [$this, 'markdownToHtml'], ['pre_escape' => 'html', 'is_safe' => ['html']]), - new TwigFilter('desc2html', [$this, 'timesheetContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]), - new TwigFilter('comment2html', [$this, 'commentContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]), - ]; + if (null === $this->markdownEnabled) { + $this->markdownEnabled = $this->configuration->isTimesheetMarkdownEnabled(); + } + + return $this->markdownEnabled; } /** @@ -66,7 +60,7 @@ final class MarkdownExtension extends AbstractExtension $content = trim(substr($content, 0, 100)) . ' …'; } - if ($this->configuration->isTimesheetMarkdownEnabled()) { + if ($this->isMarkdownEnabled()) { $content = $this->markdown->toHtml($content, false); } elseif ($fullLength) { $content = '
' . nl2br($content) . '
'; @@ -87,7 +81,7 @@ final class MarkdownExtension extends AbstractExtension return ''; } - if ($this->configuration->isTimesheetMarkdownEnabled()) { + if ($this->isMarkdownEnabled()) { return $this->markdown->toHtml($content, false); } diff --git a/src/Twig/Runtime/ThemeEventExtension.php b/src/Twig/Runtime/ThemeExtension.php similarity index 69% rename from src/Twig/Runtime/ThemeEventExtension.php rename to src/Twig/Runtime/ThemeExtension.php index e3a80b18..abde1fae 100644 --- a/src/Twig/Runtime/ThemeEventExtension.php +++ b/src/Twig/Runtime/ThemeExtension.php @@ -9,37 +9,40 @@ namespace App\Twig\Runtime; +use App\Entity\User; use App\Event\ThemeEvent; use App\Event\ThemeJavascriptTranslationsEvent; -use App\Security\CurrentUser; +use Symfony\Bridge\Twig\AppVariable; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Twig\Environment; use Twig\Extension\RuntimeExtensionInterface; -final class ThemeEventExtension implements RuntimeExtensionInterface +final class ThemeExtension implements RuntimeExtensionInterface { /** * @var EventDispatcherInterface */ private $eventDispatcher; - /** - * @var CurrentUser - */ - private $user; - public function __construct(EventDispatcherInterface $dispatcher, CurrentUser $user) + public function __construct(EventDispatcherInterface $dispatcher) { $this->eventDispatcher = $dispatcher; - $this->user = $user; } /** + * @param Environment $environment * @param string $eventName * @param mixed|null $payload * @return ThemeEvent */ - public function trigger(string $eventName, $payload = null): ThemeEvent + public function trigger(Environment $environment, string $eventName, $payload = null): ThemeEvent { - $themeEvent = new ThemeEvent($this->user->getUser(), $payload); + /** @var AppVariable $app */ + $app = $environment->getGlobals()['app']; + /** @var User $user */ + $user = $app->getUser(); + + $themeEvent = new ThemeEvent($user, $payload); if ($this->eventDispatcher->hasListeners($eventName)) { $this->eventDispatcher->dispatch($themeEvent, $eventName); diff --git a/src/Twig/Runtime/TimesheetExtension.php b/src/Twig/Runtime/TimesheetExtension.php new file mode 100644 index 00000000..bb9c9e8e --- /dev/null +++ b/src/Twig/Runtime/TimesheetExtension.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + public function activeEntries(User $user): array + { + return $this->repository->getActiveEntries($user); + } +} diff --git a/src/Twig/WidgetExtension.php b/src/Twig/Runtime/WidgetExtension.php similarity index 63% rename from src/Twig/WidgetExtension.php rename to src/Twig/Runtime/WidgetExtension.php index c6d3b3dd..4bee33ec 100644 --- a/src/Twig/WidgetExtension.php +++ b/src/Twig/Runtime/WidgetExtension.php @@ -7,37 +7,25 @@ * file that was distributed with this source code. */ -namespace App\Twig; +namespace App\Twig\Runtime; use App\Widget\WidgetException; use App\Widget\WidgetInterface; use App\Widget\WidgetService; -use InvalidArgumentException; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; +use Twig\Extension\RuntimeExtensionInterface; -class WidgetExtension extends AbstractExtension +final class WidgetExtension implements RuntimeExtensionInterface { /** * @var WidgetService */ - protected $service; + private $service; public function __construct(WidgetService $service) { $this->service = $service; } - /** - * {@inheritdoc} - */ - public function getFunctions() - { - return [ - new TwigFunction('render_widget', [$this, 'renderWidget'], ['is_safe' => ['html']]), - ]; - } - /** * @param WidgetInterface|string $widget * @param array $options @@ -47,12 +35,12 @@ class WidgetExtension extends AbstractExtension public function renderWidget($widget, array $options = []) { if (!($widget instanceof WidgetInterface) && !\is_string($widget)) { - throw new InvalidArgumentException('Widget must either implement WidgetInterface or be a string'); + throw new \InvalidArgumentException('Widget must either implement WidgetInterface or be a string'); } if (\is_string($widget)) { if (!$this->service->hasWidget($widget)) { - throw new InvalidArgumentException(sprintf('Unknown widget "%s" requested', $widget)); + throw new \InvalidArgumentException(sprintf('Unknown widget "%s" requested', $widget)); } $widget = $this->service->getWidget($widget); diff --git a/src/Twig/RuntimeExtensions.php b/src/Twig/RuntimeExtensions.php new file mode 100644 index 00000000..3e95e42f --- /dev/null +++ b/src/Twig/RuntimeExtensions.php @@ -0,0 +1,50 @@ + true]), + new TwigFunction('javascript_translations', [ThemeExtension::class, 'getJavascriptTranslations']), + new TwigFunction('timesheet_exporter', [ExporterExtension::class, 'getTimesheetExporter']), + new TwigFunction('active_timesheets', [TimesheetExtension::class, 'activeEntries']), + new TwigFunction('encore_entry_css_source', [EncoreExtension::class, 'getEncoreEntryCssSource']), + new TwigFunction('render_widget', [WidgetExtension::class, 'renderWidget'], ['is_safe' => ['html']]), + ]; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return [ + new TwigFilter('md2html', [MarkdownExtension::class, 'markdownToHtml'], ['pre_escape' => 'html', 'is_safe' => ['html']]), + new TwigFilter('desc2html', [MarkdownExtension::class, 'timesheetContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]), + new TwigFilter('comment2html', [MarkdownExtension::class, 'commentContent'], ['pre_escape' => 'html', 'is_safe' => ['html']]), + ]; + } +} diff --git a/src/Widget/WidgetService.php b/src/Widget/WidgetService.php index e904fd47..214d0410 100644 --- a/src/Widget/WidgetService.php +++ b/src/Widget/WidgetService.php @@ -11,12 +11,15 @@ namespace App\Widget; use App\Repository\WidgetRepository; +/** + * @final + */ class WidgetService { /** * @var WidgetRendererInterface[] */ - private $renderer = []; + private $renderer; /** * @var WidgetRepository */ @@ -28,9 +31,7 @@ class WidgetService */ public function __construct(WidgetRepository $repository, iterable $renderer) { - foreach ($renderer as $render) { - $this->addRenderer($render); - } + $this->renderer = $renderer; $this->repository = $repository; } @@ -70,7 +71,7 @@ class WidgetService /** * @return WidgetRendererInterface[] */ - public function getRenderer(): array + public function getRenderer(): iterable { return $this->renderer; } diff --git a/templates/base.html.twig b/templates/base.html.twig index 8d54dbda..6a24bb23 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -122,7 +122,53 @@ {% endif %} {% block navbar_extensions %}{% endblock %} - {{ render(controller('App\\Controller\\LayoutController::activeEntries')) }} + {% set active_timesheets = active_timesheets(app.user) %} + {% set soft_limit = theme_config('soft_limit') %} + + {% if is_granted('create_own_timesheet') %} + + {% endif %} {% endif %} {% endblock %} diff --git a/tests/Controller/LayoutControllerTest.php b/tests/Controller/LayoutControllerTest.php index 9f7b9e23..222591b5 100644 --- a/tests/Controller/LayoutControllerTest.php +++ b/tests/Controller/LayoutControllerTest.php @@ -77,10 +77,7 @@ class LayoutControllerTest extends ControllerBaseTest { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); - $em = $this->getEntityManager(); - $user = $this->getUserByRole(User::ROLE_USER); - - $this->request($client, '/layou/active_entries'); + $this->request($client, '/dashboard/'); $this->assertTrue($client->getResponse()->isSuccessful()); $content = $client->getResponse()->getContent(); diff --git a/tests/Export/Renderer/AbstractRendererTest.php b/tests/Export/Renderer/AbstractRendererTest.php index dba7d61a..1e96b764 100644 --- a/tests/Export/Renderer/AbstractRendererTest.php +++ b/tests/Export/Renderer/AbstractRendererTest.php @@ -28,13 +28,11 @@ use App\Event\TimesheetMetaDisplayEvent; use App\Export\ExportRendererInterface; use App\Export\RendererInterface; use App\Repository\Query\TimesheetQuery; -use App\Twig\DateExtensions; +use App\Twig\LocaleFormatExtensions; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -46,7 +44,6 @@ abstract class AbstractRendererTest extends KernelTestCase */ protected function getAbstractRenderer(string $classname) { - $requestStack = new RequestStack(); $languages = [ 'en' => [ 'date' => 'Y.m.d', @@ -55,12 +52,8 @@ abstract class AbstractRendererTest extends KernelTestCase ] ]; - $request = new Request(); - $request->setLocale('en'); - $requestStack->push($request); - $translator = $this->createMock(TranslatorInterface::class); - $dateExtension = new DateExtensions($requestStack, new LanguageFormattings($languages)); + $dateExtension = new LocaleFormatExtensions(new LanguageFormattings($languages)); $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new MetaFieldColumnSubscriber()); diff --git a/tests/Export/Timesheet/AbstractRendererTest.php b/tests/Export/Timesheet/AbstractRendererTest.php index 53f788ca..7dff9348 100644 --- a/tests/Export/Timesheet/AbstractRendererTest.php +++ b/tests/Export/Timesheet/AbstractRendererTest.php @@ -27,13 +27,11 @@ use App\Event\ProjectMetaDisplayEvent; use App\Event\TimesheetMetaDisplayEvent; use App\Export\TimesheetExportInterface; use App\Repository\Query\TimesheetQuery; -use App\Twig\DateExtensions; +use App\Twig\LocaleFormatExtensions; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -45,7 +43,6 @@ abstract class AbstractRendererTest extends KernelTestCase */ protected function getAbstractRenderer(string $classname) { - $requestStack = new RequestStack(); $languages = [ 'en' => [ 'date' => 'Y.m.d', @@ -54,12 +51,8 @@ abstract class AbstractRendererTest extends KernelTestCase ] ]; - $request = new Request(); - $request->setLocale('en'); - $requestStack->push($request); - $translator = $this->getMockBuilder(TranslatorInterface::class)->getMock(); - $dateExtension = new DateExtensions($requestStack, new LanguageFormattings($languages)); + $dateExtension = new LocaleFormatExtensions(new LanguageFormattings($languages)); $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new MetaFieldColumnSubscriber()); diff --git a/tests/Twig/ConfigExtensionTest.php b/tests/Twig/ConfigExtensionTest.php index 8aabd518..45c905a0 100644 --- a/tests/Twig/ConfigExtensionTest.php +++ b/tests/Twig/ConfigExtensionTest.php @@ -9,7 +9,7 @@ namespace App\Tests\Twig; -use App\Configuration\ThemeConfiguration; +use App\Configuration\SystemConfiguration; use App\Tests\Configuration\TestConfigLoader; use App\Twig\ConfigExtension; use PHPUnit\Framework\TestCase; @@ -23,7 +23,7 @@ class ConfigExtensionTest extends TestCase protected function getSut(array $settings, array $loaderSettings = []): ConfigExtension { $loader = new TestConfigLoader($loaderSettings); - $config = new ThemeConfiguration($loader, $settings); + $config = new SystemConfiguration($loader, ['theme' => $settings]); return new ConfigExtension($config); } diff --git a/tests/Twig/DateExtensionsTest.php b/tests/Twig/DateExtensionsTest.php deleted file mode 100644 index 43fcbb23..00000000 --- a/tests/Twig/DateExtensionsTest.php +++ /dev/null @@ -1,238 +0,0 @@ -setLocale($locale); - $requestStack = new RequestStack(); - $requestStack->push($request); - - return new DateExtensions($requestStack, new LanguageFormattings($dateSettings)); - } - - public function testGetFilters() - { - $filters = ['month_name', 'day_name', 'date_short', 'date_time', 'date_full', 'date_format', 'time', 'hour24']; - $sut = $this->getSut('de', []); - $twigFilters = $sut->getFilters(); - $this->assertCount(\count($filters), $twigFilters); - $i = 0; - foreach ($twigFilters as $filter) { - $this->assertInstanceOf(TwigFilter::class, $filter); - $this->assertEquals($filters[$i++], $filter->getName()); - } - } - - public function testGetFunctions() - { - $functions = ['get_format_duration', 'create_date']; - $sut = $this->getSut('de', []); - $twigFunctions = $sut->getFunctions(); - $this->assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $filter */ - foreach ($twigFunctions as $filter) { - $this->assertInstanceOf(TwigFunction::class, $filter); - $this->assertEquals($functions[$i++], $filter->getName()); - } - } - - /** - * @param string $locale - * @param \DateTime|string $date - * @param string $result - * @dataProvider getDateShortData - */ - public function testDateShort($locale, $date, $result) - { - $sut = $this->getSut($locale, [ - 'de' => ['date' => 'd.m.Y'], - 'en' => ['date' => 'Y-m-d'], - 'ru' => ['date' => 'd.m.Y'], - ]); - $this->assertEquals($result, $sut->dateShort($date)); - } - - public function getDateShortData() - { - return [ - ['en', new \DateTime('7 January 2010'), '2010-01-07'], - ['en', new \DateTime('2016-06-23'), '2016-06-23'], - ['de', new \DateTime('1980-12-14'), '14.12.1980'], - ['ru', new \DateTime('1980-12-14'), '14.12.1980'], - ['ru', '1980-12-14', '14.12.1980'], - ['ru', 1.2345, 1.2345], - ]; - } - - /** - * @param string $locale - * @param \DateTime|string $date - * @param string $result - * @dataProvider getDateTimeData - */ - public function testDateTime($locale, $date, $result) - { - $sut = $this->getSut($locale, [ - 'de' => ['date_time' => 'd.m.Y H:i:s'], - 'en' => ['date_time' => 'Y-m-d h:m A'], - ]); - $this->assertEquals($result, $sut->dateTime($date)); - } - - public function getDateTimeData() - { - return [ - ['en', new \DateTime('7 January 2010'), '2010-01-07 12:01 AM'], - ['de', (new \DateTime('1980-12-14'))->setTime(13, 27, 55), '14.12.1980 13:27:55'], - ['de', '1980-12-14 13:27:55', '14.12.1980 13:27:55'], - ['de', 1.2345, 1.2345], - ]; - } - - /** - * @dataProvider getDayNameTestData - */ - public function testDayName(string $locale, string $date, string $expectedName, bool $short) - { - $sut = $this->getSut($locale, []); - self::assertEquals($expectedName, $sut->dayName(new \DateTime($date), $short)); - } - - public function getDayNameTestData() - { - return [ - ['de', '2020-07-09 12:00:00', 'Donnerstag', false], - ['en', '2020-07-09 12:00:00', 'Thursday', false], - ['de', '2020-07-09 12:00:00', 'Do.', true], - ['en', '2020-07-09 12:00:00', 'Thu', true], - ]; - } - - /** - * @dataProvider getMonthNameTestData - */ - public function testMonthName(string $locale, string $date, string $expectedName, bool $withYear = false) - { - $sut = $this->getSut($locale, []); - self::assertEquals($expectedName, $sut->monthName(new \DateTime($date), $withYear)); - } - - public function getMonthNameTestData() - { - return [ - ['de', '2020-07-09 23:59:59', 'Juli', false], - ['en', '2020-07-09 23:59:59', 'July', false], - ['de', 'January 2016', 'Januar', false], - ['en', 'January 2016', 'January', false], - ['en', '2016-12-23', 'December', false], - ['ru', '2016-12-23', 'декабрь', false], - ['de', '2020-07-09 23:59:59', 'Juli 2020', true], - ['en', '2020-07-09 23:59:59', 'July 2020', true], - ['de', 'January 2016', 'Januar 2016', true], - ['en', 'January 2016', 'January 2016', true], - ['en', '2015-12-23', 'December 2015', true], - ['ru', '2015-12-23', 'декабрь 2015', true], - ]; - } - - public function testDateFormat() - { - $date = new \DateTime('7 January 2010 17:43:21', new \DateTimeZone('Europe/Berlin')); - $sut = $this->getSut('en', []); - $this->assertEquals('2010-01-07T17:43:21+01:00', $sut->dateFormat($date, 'c')); - $this->assertStringStartsWith('2010-01-07T17:43:21', $sut->dateFormat('7 January 2010 17:43:21', 'c')); - - // next test checks the fallback for errors while converting the date - /* @phpstan-ignore-next-line */ - $this->assertEquals(2010.0107, $sut->dateFormat(2010.0107, 'c')); - } - - public function testTime() - { - $time = new \DateTime('2016-06-23'); - $time->setTime(17, 53, 23); - - $sut = $this->getSut('en', ['en' => ['time' => 'H:i']]); - $this->assertEquals('17:53', $sut->time($time)); - $this->assertEquals('17:53', $sut->time('2016-06-23 17:53')); - } - - public function testHour24() - { - $sut = $this->getSut('en', [ - 'en' => ['24_hours' => false], - ]); - $this->assertEquals('bar', $sut->hour24('foo', 'bar')); - - $sut = $this->getSut('de', [ - 'de' => ['24_hours' => true], - ]); - $this->assertEquals('foo', $sut->hour24('foo', 'bar')); - } - - public function testDateTimeFull() - { - $sut = $this->getSut('en', [ - 'en' => ['date_time_type' => 'yyyy-MM-dd HH:mm:ss'], - ]); - - $dateTime = new \DateTime('2019-08-17 12:29:47', new \DateTimeZone(date_default_timezone_get())); - $dateTime->setDate(2019, 8, 17); - $dateTime->setTime(12, 29, 47); - - $this->assertEquals('2019-08-17 12:29:47', $sut->dateTimeFull($dateTime)); - $this->assertEquals('2019-08-17 12:29:47', $sut->dateTimeFull('2019-08-17 12:29:47')); - - // next test checks the fallback for errors while converting the date - /* @phpstan-ignore-next-line */ - $this->assertEquals(189.45, $sut->dateTimeFull(189.45)); - } - - public function testCreateDate() - { - $user = new User(); - $user->setTimezone('Europe/Berlin'); - $sut = $this->getSut('en', []); - $date = $sut->createDate('now', $user); - $this->assertEquals('Europe/Berlin', $date->getTimezone()->getName()); - - $user->setTimezone('Asia/Dubai'); - $date = $sut->createDate('2019-08-27 16:30:45', $user); - $this->assertEquals('2019-08-27T16:30:45+0400', $date->format(DATE_ISO8601)); - $this->assertEquals('Asia/Dubai', $date->getTimezone()->getName()); - - $date = $sut->createDate('2019-08-27 16:30:45', null); - $this->assertEquals(date_default_timezone_get(), $date->getTimezone()->getName()); - } -} diff --git a/tests/Twig/EventExtensionsTest.php b/tests/Twig/EventExtensionsTest.php deleted file mode 100644 index eefcb36f..00000000 --- a/tests/Twig/EventExtensionsTest.php +++ /dev/null @@ -1,39 +0,0 @@ -getSut(); - $twigFunctions = $sut->getFunctions(); - self::assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $filter */ - foreach ($twigFunctions as $filter) { - self::assertInstanceOf(TwigFunction::class, $filter); - self::assertEquals($functions[$i++], $filter->getName()); - } - } -} diff --git a/tests/Twig/LocaleExtensionsTest.php b/tests/Twig/LocaleFormatExtensionsTest.php similarity index 55% rename from tests/Twig/LocaleExtensionsTest.php rename to tests/Twig/LocaleFormatExtensionsTest.php index 6b0d94b5..77e0d6e0 100644 --- a/tests/Twig/LocaleExtensionsTest.php +++ b/tests/Twig/LocaleFormatExtensionsTest.php @@ -11,19 +11,19 @@ namespace App\Tests\Twig; use App\Configuration\LanguageFormattings; use App\Entity\Timesheet; -use App\Twig\LocaleExtensions; +use App\Entity\User; +use App\Twig\LocaleFormatExtensions; use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Intl\Util\IntlTestHelper; use Twig\TwigFilter; use Twig\TwigFunction; /** - * @covers \App\Twig\LocaleExtensions + * @covers \App\Twig\LocaleFormatExtensions * @covers \App\Utils\LocaleFormatter + * @covers \App\Utils\LocaleFormats */ -class LocaleExtensionsTest extends TestCase +class LocaleFormatExtensionsTest extends TestCase { private $localeEn = ['en' => ['date' => 'Y-m-d', 'duration' => '%h:%m h']]; private $localeDe = ['de' => ['date' => 'd.m.Y', 'duration' => '%h:%m h']]; @@ -31,28 +31,35 @@ class LocaleExtensionsTest extends TestCase private $localeFake = ['XX' => ['date' => 'd.m.Y', 'duration' => '%h - %m - %s Zeit']]; /** - * @param array $locales - * @param string $locale - * @return LocaleExtensions + * @param string|array $locale + * @param array|string $dateSettings + * @return LocaleFormatExtensions */ - protected function getSut($locales, $locale = 'en') + protected function getSut($locale, $dateSettings) { - $request = new Request(); - $request->setLocale($locale); - $requestStack = new RequestStack(); - $requestStack->push($request); + $language = $locale; + if (\is_array($locale)) { + $language = $dateSettings; + $dateSettings = $locale; + } + $sut = new LocaleFormatExtensions(new LanguageFormattings($dateSettings)); + $sut->setLocale($language); - return new LocaleExtensions($requestStack, new LanguageFormattings($locales)); + return $sut; } public function testGetFilters() { - $filters = ['duration', 'duration_decimal', 'money', 'currency', 'country', 'language', 'amount']; - $sut = $this->getSut($this->localeDe); + $filters = [ + 'month_name', 'day_name', 'date_short', 'date_time', 'date_full', 'date_format', 'time', 'hour24', + 'duration', 'duration_decimal', 'money', 'currency', 'country', 'language', 'amount' + ]; + $i = 0; + + $sut = $this->getSut('de', []); $twigFilters = $sut->getFilters(); $this->assertCount(\count($filters), $twigFilters); - $i = 0; - /** @var TwigFilter $filter */ + foreach ($twigFilters as $filter) { $this->assertInstanceOf(TwigFilter::class, $filter); $this->assertEquals($filters[$i++], $filter->getName()); @@ -61,11 +68,13 @@ class LocaleExtensionsTest extends TestCase public function testGetFunctions() { - $functions = ['locales']; - $sut = $this->getSut($this->localeDe); + $functions = ['get_format_duration', 'create_date', 'locales']; + $i = 0; + + $sut = $this->getSut('de', []); $twigFunctions = $sut->getFunctions(); $this->assertCount(\count($functions), $twigFunctions); - $i = 0; + /** @var TwigFunction $filter */ foreach ($twigFunctions as $filter) { $this->assertInstanceOf(TwigFunction::class, $filter); @@ -73,6 +82,175 @@ class LocaleExtensionsTest extends TestCase } } + /** + * @param string $locale + * @param \DateTime|string $date + * @param string $result + * @dataProvider getDateShortData + */ + public function testDateShort($locale, $date, $result) + { + $sut = $this->getSut($locale, [ + 'de' => ['date' => 'd.m.Y'], + 'en' => ['date' => 'Y-m-d'], + 'ru' => ['date' => 'd.m.Y'], + ]); + $this->assertEquals($result, $sut->dateShort($date)); + } + + public function getDateShortData() + { + return [ + ['en', new \DateTime('7 January 2010'), '2010-01-07'], + ['en', new \DateTime('2016-06-23'), '2016-06-23'], + ['de', new \DateTime('1980-12-14'), '14.12.1980'], + ['ru', new \DateTime('1980-12-14'), '14.12.1980'], + ['ru', '1980-12-14', '14.12.1980'], + ['ru', 1.2345, 1.2345], + ]; + } + + /** + * @param string $locale + * @param \DateTime|string $date + * @param string $result + * @dataProvider getDateTimeData + */ + public function testDateTime($locale, $date, $result) + { + $sut = $this->getSut($locale, [ + 'de' => ['date_time' => 'd.m.Y H:i:s'], + 'en' => ['date_time' => 'Y-m-d h:m A'], + ]); + $this->assertEquals($result, $sut->dateTime($date)); + } + + public function getDateTimeData() + { + return [ + ['en', new \DateTime('7 January 2010'), '2010-01-07 12:01 AM'], + ['de', (new \DateTime('1980-12-14'))->setTime(13, 27, 55), '14.12.1980 13:27:55'], + ['de', '1980-12-14 13:27:55', '14.12.1980 13:27:55'], + ['de', 1.2345, 1.2345], + ]; + } + + /** + * @dataProvider getDayNameTestData + */ + public function testDayName(string $locale, string $date, string $expectedName, bool $short) + { + $sut = $this->getSut($locale, []); + self::assertEquals($expectedName, $sut->dayName(new \DateTime($date), $short)); + } + + public function getDayNameTestData() + { + return [ + ['de', '2020-07-09 12:00:00', 'Donnerstag', false], + ['en', '2020-07-09 12:00:00', 'Thursday', false], + ['de', '2020-07-09 12:00:00', 'Do.', true], + ['en', '2020-07-09 12:00:00', 'Thu', true], + ]; + } + + /** + * @dataProvider getMonthNameTestData + */ + public function testMonthName(string $locale, string $date, string $expectedName, bool $withYear = false) + { + $sut = $this->getSut($locale, []); + self::assertEquals($expectedName, $sut->monthName(new \DateTime($date), $withYear)); + } + + public function getMonthNameTestData() + { + return [ + ['de', '2020-07-09 23:59:59', 'Juli', false], + ['en', '2020-07-09 23:59:59', 'July', false], + ['de', 'January 2016', 'Januar', false], + ['en', 'January 2016', 'January', false], + ['en', '2016-12-23', 'December', false], + ['ru', '2016-12-23', 'декабрь', false], + ['de', '2020-07-09 23:59:59', 'Juli 2020', true], + ['en', '2020-07-09 23:59:59', 'July 2020', true], + ['de', 'January 2016', 'Januar 2016', true], + ['en', 'January 2016', 'January 2016', true], + ['en', '2015-12-23', 'December 2015', true], + ['ru', '2015-12-23', 'декабрь 2015', true], + ]; + } + + public function testDateFormat() + { + $date = new \DateTime('7 January 2010 17:43:21', new \DateTimeZone('Europe/Berlin')); + $sut = $this->getSut('en', []); + $this->assertEquals('2010-01-07T17:43:21+01:00', $sut->dateFormat($date, 'c')); + $this->assertStringStartsWith('2010-01-07T17:43:21', $sut->dateFormat('7 January 2010 17:43:21', 'c')); + + // next test checks the fallback for errors while converting the date + /* @phpstan-ignore-next-line */ + $this->assertEquals(2010.0107, $sut->dateFormat(2010.0107, 'c')); + } + + public function testTime() + { + $time = new \DateTime('2016-06-23'); + $time->setTime(17, 53, 23); + + $sut = $this->getSut('en', ['en' => ['time' => 'H:i']]); + $this->assertEquals('17:53', $sut->time($time)); + $this->assertEquals('17:53', $sut->time('2016-06-23 17:53')); + } + + public function testHour24() + { + $sut = $this->getSut('en', [ + 'en' => ['24_hours' => false], + ]); + $this->assertEquals('bar', $sut->hour24('foo', 'bar')); + + $sut = $this->getSut('de', [ + 'de' => ['24_hours' => true], + ]); + $this->assertEquals('foo', $sut->hour24('foo', 'bar')); + } + + public function testDateTimeFull() + { + $sut = $this->getSut('en', [ + 'en' => ['date_time_type' => 'yyyy-MM-dd HH:mm:ss'], + ]); + + $dateTime = new \DateTime('2019-08-17 12:29:47', new \DateTimeZone(date_default_timezone_get())); + $dateTime->setDate(2019, 8, 17); + $dateTime->setTime(12, 29, 47); + + $this->assertEquals('2019-08-17 12:29:47', $sut->dateTimeFull($dateTime)); + $this->assertEquals('2019-08-17 12:29:47', $sut->dateTimeFull('2019-08-17 12:29:47')); + + // next test checks the fallback for errors while converting the date + /* @phpstan-ignore-next-line */ + $this->assertEquals(189.45, $sut->dateTimeFull(189.45)); + } + + public function testCreateDate() + { + $user = new User(); + $user->setTimezone('Europe/Berlin'); + $sut = $this->getSut('en', []); + $date = $sut->createDate('now', $user); + $this->assertEquals('Europe/Berlin', $date->getTimezone()->getName()); + + $user->setTimezone('Asia/Dubai'); + $date = $sut->createDate('2019-08-27 16:30:45', $user); + $this->assertEquals('2019-08-27T16:30:45+0400', $date->format(DATE_ISO8601)); + $this->assertEquals('Asia/Dubai', $date->getTimezone()->getName()); + + $date = $sut->createDate('2019-08-27 16:30:45', null); + $this->assertEquals(date_default_timezone_get(), $date->getTimezone()->getName()); + } + public function testLocales() { $locales = [ @@ -82,7 +260,7 @@ class LocaleExtensionsTest extends TestCase ]; $appLocales = array_merge($this->localeEn, $this->localeDe, $this->localeRu); - $sut = $this->getSut($appLocales); + $sut = $this->getSut('en', $appLocales); $this->assertEquals($locales, $sut->getLocales()); } @@ -96,7 +274,7 @@ class LocaleExtensionsTest extends TestCase 123 => 123, ]; - $sut = $this->getSut($this->localeEn); + $sut = $this->getSut('en', $this->localeEn); foreach ($symbols as $name => $symbol) { $this->assertEquals($symbol, $sut->currency($name)); } @@ -112,7 +290,7 @@ class LocaleExtensionsTest extends TestCase '12' => '12', ]; - $sut = $this->getSut($this->localeEn); + $sut = $this->getSut('en', $this->localeEn); foreach ($countries as $locale => $name) { $this->assertEquals($name, $sut->country($locale)); } @@ -128,7 +306,7 @@ class LocaleExtensionsTest extends TestCase '12' => '12', ]; - $sut = $this->getSut($this->localeEn); + $sut = $this->getSut('en', $this->localeEn); foreach ($languages as $locale => $name) { $this->assertEquals($name, $sut->language($locale)); } @@ -254,7 +432,7 @@ class LocaleExtensionsTest extends TestCase { $record = $this->getTimesheet(9437); - $sut = $this->getSut($this->localeEn); + $sut = $this->getSut('en', $this->localeEn); $this->assertEquals('02:37 h', $sut->duration($record->getDuration())); $this->assertEquals('2.62', $sut->duration($record->getDuration(), true)); @@ -284,7 +462,7 @@ class LocaleExtensionsTest extends TestCase { $record = $this->getTimesheet(9437); - $sut = $this->getSut($this->localeEn); + $sut = $this->getSut('en', $this->localeEn); $this->assertEquals('2.62', $sut->durationDecimal($record->getDuration())); // test Timesheet object diff --git a/tests/Twig/EncoreExtensionTest.php b/tests/Twig/Runtime/EncoreExtensionTest.php similarity index 66% rename from tests/Twig/EncoreExtensionTest.php rename to tests/Twig/Runtime/EncoreExtensionTest.php index 524910ed..a9fb30a2 100644 --- a/tests/Twig/EncoreExtensionTest.php +++ b/tests/Twig/Runtime/EncoreExtensionTest.php @@ -7,17 +7,16 @@ * file that was distributed with this source code. */ -namespace App\Tests\Twig; +namespace App\Tests\Twig\Runtime; -use App\Twig\EncoreExtension; +use App\Twig\Runtime\EncoreExtension; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; -use Twig\TwigFunction; /** - * @covers \App\Twig\EncoreExtension + * @covers \App\Twig\Runtime\EncoreExtension */ class EncoreExtensionTest extends TestCase { @@ -29,7 +28,7 @@ class EncoreExtensionTest extends TestCase $container = new Container(new ParameterBag([])); $container->set(EntrypointLookupInterface::class, $entryLookup); - return new EncoreExtension($container, __DIR__); + return new EncoreExtension($container, __DIR__ . '/../'); } public function testGetSubscribedServices() @@ -37,20 +36,6 @@ class EncoreExtensionTest extends TestCase self::assertEquals([EntrypointLookupInterface::class], EncoreExtension::getSubscribedServices()); } - public function testGetFunctions() - { - $functions = ['encore_entry_css_source']; - $sut = $this->getSut(); - $twigFunctions = $sut->getFunctions(); - self::assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $filter */ - foreach ($twigFunctions as $filter) { - self::assertInstanceOf(TwigFunction::class, $filter); - self::assertEquals($functions[$i++], $filter->getName()); - } - } - public function testGetEncoreEntryCssSource() { $sut = $this->getSut(['test.css', 'test1.css']); diff --git a/tests/Twig/Runtime/ExporterExtensionTest.php b/tests/Twig/Runtime/ExporterExtensionTest.php new file mode 100644 index 00000000..99ec8fa5 --- /dev/null +++ b/tests/Twig/Runtime/ExporterExtensionTest.php @@ -0,0 +1,33 @@ +getSut(); + self::assertEquals([], $sut->getTimesheetExporter()); + } +} diff --git a/tests/Twig/MarkdownExtensionTest.php b/tests/Twig/Runtime/MarkdownExtensionTest.php similarity index 71% rename from tests/Twig/MarkdownExtensionTest.php rename to tests/Twig/Runtime/MarkdownExtensionTest.php index 2890f228..66c8e104 100644 --- a/tests/Twig/MarkdownExtensionTest.php +++ b/tests/Twig/Runtime/MarkdownExtensionTest.php @@ -7,44 +7,19 @@ * file that was distributed with this source code. */ -namespace App\Tests\Twig; +namespace App\Tests\Twig\Runtime; use App\Configuration\ConfigLoaderInterface; use App\Configuration\SystemConfiguration; -use App\Twig\MarkdownExtension; +use App\Twig\Runtime\MarkdownExtension; use App\Utils\Markdown; use PHPUnit\Framework\TestCase; -use Twig\Node\Node; /** - * @covers \App\Twig\MarkdownExtension + * @covers \App\Twig\Runtime\MarkdownExtension */ class MarkdownExtensionTest extends TestCase { - public function testGetFilters() - { - $loader = $this->createMock(ConfigLoaderInterface::class); - $config = new SystemConfiguration($loader, ['timesheet' => ['markdown_content' => true]]); - $sut = new MarkdownExtension(new Markdown(), $config); - $filters = $sut->getFilters(); - $this->assertCount(3, $filters); - - // make sure that the md2html filter does proper escaping - $this->assertEquals('md2html', $filters[0]->getName()); - self::assertEquals('html', $filters[0]->getPreEscape()); - self::assertEquals(['html'], $filters[0]->getSafe(new Node())); - - // make sure that the desc2html filter does proper escaping - $this->assertEquals('desc2html', $filters[1]->getName()); - self::assertEquals('html', $filters[1]->getPreEscape()); - self::assertEquals(['html'], $filters[1]->getSafe(new Node())); - - // make sure that the comment2html filter does proper escaping - $this->assertEquals('comment2html', $filters[2]->getName()); - self::assertEquals('html', $filters[2]->getPreEscape()); - self::assertEquals(['html'], $filters[2]->getSafe(new Node())); - } - public function testMarkdownToHtml() { $loader = $this->createMock(ConfigLoaderInterface::class); diff --git a/tests/Twig/Runtime/ThemeEventExtensionTest.php b/tests/Twig/Runtime/ThemeEventExtensionTest.php index f877885a..62771ac9 100644 --- a/tests/Twig/Runtime/ThemeEventExtensionTest.php +++ b/tests/Twig/Runtime/ThemeEventExtensionTest.php @@ -12,16 +12,21 @@ namespace App\Tests\Twig\Runtime; use App\Entity\User; use App\Event\ThemeEvent; use App\Tests\Mocks\Security\CurrentUserFactory; -use App\Twig\Runtime\ThemeEventExtension; +use App\Twig\Runtime\ThemeExtension; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Twig\AppVariable; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Twig\Environment; +use Twig\Loader\FilesystemLoader; /** - * @covers \App\Twig\Runtime\ThemeEventExtension + * @covers \App\Twig\Runtime\ThemeExtension */ class ThemeEventExtensionTest extends TestCase { - protected function getSut(bool $hasListener = true): ThemeEventExtension + protected function getSut(bool $hasListener = true): ThemeExtension { $dispatcher = $this->createMock(EventDispatcherInterface::class); $dispatcher->method('hasListeners')->willReturn($hasListener); @@ -29,20 +34,39 @@ class ThemeEventExtensionTest extends TestCase $user = (new CurrentUserFactory($this))->create(new User()); - return new ThemeEventExtension($dispatcher, $user); + return new ThemeExtension($dispatcher); + } + + protected function getEnvironment(): Environment + { + $mock = $this->getMockBuilder(UsernamePasswordToken::class)->onlyMethods(['getUser'])->disableOriginalConstructor()->getMock(); + $mock->method('getUser')->willReturn(new User()); + /** @var UsernamePasswordToken $token */ + $token = $mock; + + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $app = new AppVariable(); + $app->setTokenStorage($tokenStorage); + + $environment = new Environment(new FilesystemLoader()); + $environment->addGlobal('app', $app); + + return $environment; } public function testTrigger() { $sut = $this->getSut(); - $event = $sut->trigger('foo', []); + $event = $sut->trigger($this->getEnvironment(), 'foo', []); self::assertInstanceOf(ThemeEvent::class, $event); } public function testTriggerWithoutListener() { $sut = $this->getSut(false); - $event = $sut->trigger('foo', []); + $event = $sut->trigger($this->getEnvironment(), 'foo', []); self::assertInstanceOf(ThemeEvent::class, $event); } diff --git a/tests/Twig/Runtime/TimesheetExtensionTest.php b/tests/Twig/Runtime/TimesheetExtensionTest.php new file mode 100644 index 00000000..9443c98a --- /dev/null +++ b/tests/Twig/Runtime/TimesheetExtensionTest.php @@ -0,0 +1,33 @@ +createMock(TimesheetRepository::class); + $repository->method('getActiveEntries')->willReturn($entries); + + $sut = new TimesheetExtension($repository); + self::assertEquals($entries, $sut->activeEntries(new User())); + } +} diff --git a/tests/Twig/WidgetExtensionTest.php b/tests/Twig/Runtime/WidgetExtensionTest.php similarity index 77% rename from tests/Twig/WidgetExtensionTest.php rename to tests/Twig/Runtime/WidgetExtensionTest.php index 6051c7f7..0eceaf49 100644 --- a/tests/Twig/WidgetExtensionTest.php +++ b/tests/Twig/Runtime/WidgetExtensionTest.php @@ -7,24 +7,23 @@ * file that was distributed with this source code. */ -namespace App\Tests\Twig; +namespace App\Tests\Twig\Runtime; -use App\Twig\WidgetExtension; +use App\Twig\Runtime\WidgetExtension; use App\Widget\Type\More; use App\Widget\WidgetInterface; use App\Widget\WidgetRendererInterface; use App\Widget\WidgetService; use PHPUnit\Framework\TestCase; -use Twig\TwigFunction; /** - * @covers \App\Twig\WidgetExtension + * @covers \App\Twig\Runtime\WidgetExtension */ class WidgetExtensionTest extends TestCase { protected function getSut($hasWidget = null, $getWidget = null, $renderer = null): WidgetExtension { - $service = $this->getMockBuilder(WidgetService::class)->disableOriginalConstructor()->onlyMethods(['hasWidget', 'getWidget', 'findRenderer'])->getMock(); + $service = $this->createMock(WidgetService::class); if (null !== $hasWidget) { $service->expects($this->once())->method('hasWidget')->willReturn($hasWidget); } @@ -38,20 +37,6 @@ class WidgetExtensionTest extends TestCase return new WidgetExtension($service); } - public function testGetFunctions() - { - $functions = ['render_widget']; - $sut = $this->getSut(); - $twigFunctions = $sut->getFunctions(); - $this->assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $function */ - foreach ($twigFunctions as $function) { - $this->assertInstanceOf(TwigFunction::class, $function); - $this->assertEquals($functions[$i++], $function->getName()); - } - } - public function testRenderWidgetForInvalidValue() { $this->expectException(\InvalidArgumentException::class); diff --git a/tests/Twig/RuntimeExtensionsTest.php b/tests/Twig/RuntimeExtensionsTest.php new file mode 100644 index 00000000..c6bcc686 --- /dev/null +++ b/tests/Twig/RuntimeExtensionsTest.php @@ -0,0 +1,87 @@ +getFilters(); + $this->assertCount(\count($expected), $twigFilters); + + foreach ($twigFilters as $filter) { + $this->assertInstanceOf(TwigFilter::class, $filter); + $this->assertEquals($expected[$i++], $filter->getName()); + } + } + + public function testGetFunctions() + { + $expected = ['trigger', 'javascript_translations', 'timesheet_exporter', 'active_timesheets', 'encore_entry_css_source', 'render_widget']; + $i = 0; + + $sut = new RuntimeExtensions(); + $twigFunctions = $sut->getFunctions(); + $this->assertCount(\count($expected), $twigFunctions); + + /** @var TwigFunction $filter */ + foreach ($twigFunctions as $filter) { + $this->assertInstanceOf(TwigFunction::class, $filter); + $this->assertEquals($expected[$i++], $filter->getName()); + } + } + + public function testGetFilterDefinition() + { + $sut = new RuntimeExtensions(); + $filters = $sut->getFilters(); + + $found_md2html = false; + $found_desc2html = false; + $found_comment2html = false; + + foreach ($filters as $filter) { + switch ($filter->getName()) { + case 'md2html': + self::assertEquals('html', $filters[0]->getPreEscape()); + self::assertEquals(['html'], $filters[0]->getSafe(new Node())); + $found_md2html = true; + break; + case 'desc2html': + self::assertEquals('html', $filters[1]->getPreEscape()); + self::assertEquals(['html'], $filters[1]->getSafe(new Node())); + $found_desc2html = true; + break; + case 'comment2html': + self::assertEquals('html', $filters[2]->getPreEscape()); + self::assertEquals(['html'], $filters[2]->getSafe(new Node())); + $found_comment2html = true; + break; + } + } + + self::assertTrue($found_md2html, 'Missing filter: md2html'); + self::assertTrue($found_desc2html, 'Missing filter: desc2html'); + self::assertTrue($found_comment2html, 'Missing filter: comment2html'); + } +} diff --git a/tests/Twig/TimesheetExtensionTest.php b/tests/Twig/TimesheetExtensionTest.php deleted file mode 100644 index 3921644e..00000000 --- a/tests/Twig/TimesheetExtensionTest.php +++ /dev/null @@ -1,48 +0,0 @@ -getSut(); - $functions = ['timesheet_exporter']; - $twigFunctions = $sut->getFunctions(); - $this->assertCount(\count($functions), $twigFunctions); - $i = 0; - /** @var TwigFunction $function */ - foreach ($twigFunctions as $function) { - $this->assertInstanceOf(TwigFunction::class, $function); - $this->assertEquals($functions[$i++], $function->getName()); - } - } - - public function testGetExporter() - { - $sut = $this->getSut(); - self::assertEquals([], $sut->getTimesheetExporter()); - } -}