0
0
Fork 0
mirror of https://github.com/kevinpapst/kimai2.git synced 2025-05-13 13:11:54 +00:00

cleanup global context usage in widgets

This commit is contained in:
Kevin Papst 2020-08-18 11:39:41 +02:00
parent 08696651fd
commit 9597015413
29 changed files with 458 additions and 202 deletions

View file

@ -13,6 +13,7 @@ use App\Event\DashboardEvent;
use App\Widget\Type\AbstractContainer;
use App\Widget\Type\AuthorizedWidget;
use App\Widget\Type\CompoundRow;
use App\Widget\Type\UserWidget;
use App\Widget\WidgetContainerInterface;
use App\Widget\WidgetException;
use App\Widget\WidgetService;
@ -31,15 +32,15 @@ class DashboardController extends AbstractController
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
private $eventDispatcher;
/**
* @var WidgetService
*/
protected $widgets;
private $widgets;
/**
* @var array
*/
protected $dashboard;
private $dashboard;
/**
* @param EventDispatcherInterface $dispatcher
@ -58,8 +59,9 @@ class DashboardController extends AbstractController
*/
public function indexAction()
{
$event = new DashboardEvent($this->getUser());
$user = $this->getUser();
$event = new DashboardEvent($user);
foreach ($this->dashboard as $widgetRow) {
if (empty($widgetRow['widgets'])) {
continue;
@ -110,6 +112,10 @@ class DashboardController extends AbstractController
$add = $tmp;
}
if ($widget instanceof UserWidget) {
$widget->setUser($user);
}
if ($add) {
$row->addWidget($widget);
}

View file

@ -9,10 +9,8 @@
namespace App\Repository;
use App\Entity\User;
use App\Security\CurrentUser;
use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\Counter;
use App\Widget\Type\SimpleStatisticChart;
use App\Widget\Type\YearChart;
use App\Widget\WidgetException;
use App\Widget\WidgetInterface;
@ -25,29 +23,19 @@ class WidgetRepository
/**
* @var TimesheetRepository
*/
protected $repository;
private $repository;
/**
* @var array
*/
protected $widgets = [];
private $widgets = [];
/**
* @var array
*/
protected $definitions = [];
/**
* @var User|null
*/
protected $user;
private $definitions = [];
/**
* @param TimesheetRepository $repository
* @param CurrentUser $user
* @param array $widgets
*/
public function __construct(TimesheetRepository $repository, CurrentUser $user, array $widgets)
public function __construct(TimesheetRepository $repository, array $widgets)
{
$this->repository = $repository;
$this->user = $user->getUser();
$this->definitions = array_merge($this->getDefaultWidgets(), $widgets);
}
@ -89,12 +77,6 @@ class WidgetRepository
*/
protected function create(string $name, array $widget): WidgetInterface
{
$user = $this->user;
$timezone = new \DateTimeZone($user->getTimezone());
$begin = !empty($widget['begin']) ? new \DateTime($widget['begin'], $timezone) : null;
$end = !empty($widget['end']) ? new \DateTime($widget['end'], $timezone) : null;
$theUser = $widget['user'] ? $user : null;
if (!isset($widget['type'])) {
@trigger_error('Using a widget definition without a "type" is deprecated', E_USER_DEPRECATED);
$widget['type'] = Counter::class;
@ -104,30 +86,25 @@ class WidgetRepository
throw new WidgetException(sprintf('Unknown widget type "%s"', $widgetClassName));
}
/** @var AbstractWidgetType $model */
$model = new $widgetClassName();
if (!($model instanceof AbstractWidgetType)) {
/** @var SimpleStatisticChart $model */
$model = new $widgetClassName($this->repository);
if (!($model instanceof SimpleStatisticChart)) {
throw new WidgetException(
sprintf(
'Widget type "%s" is not an instance of "%s"',
$widgetClassName,
AbstractWidgetType::class
SimpleStatisticChart::class
)
);
}
try {
$data = $this->repository->getStatistic($widget['query'], $begin, $end, $theUser);
} catch (\Exception $ex) {
throw new WidgetException(
'Failed loading widget data: ' . $ex->getMessage()
);
}
$model
->setQuery($widget['query'])
->setBegin($widget['begin'])
->setEnd($widget['end'])
->setId($name)
->setTitle($widget['title'])
->setData($data);
;
if ($widget['query'] == TimesheetRepository::STATS_QUERY_DURATION) {
$model->setOption('dataType', 'duration');

View file

@ -9,75 +9,19 @@
namespace App\Twig;
use App\Entity\User;
use App\Event\ThemeEvent;
use App\Security\CurrentUser;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use App\Twig\Runtime\ThemeEventExtension;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class EventExtensions extends AbstractExtension
{
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* @var User
*/
protected $user;
/**
* @param EventDispatcherInterface $dispatcher
* @param CurrentUser $user
*/
public function __construct(EventDispatcherInterface $dispatcher, CurrentUser $user)
{
$this->eventDispatcher = $dispatcher;
$this->user = $user->getUser();
}
/**
* {@inheritdoc}
*/
public function getFunctions()
{
return [
new TwigFunction('trigger', [$this, 'triggerEvent']),
new TwigFunction('trigger', [ThemeEventExtension::class, 'trigger']),
];
}
/**
* @return EventDispatcherInterface
*/
protected function getDispatcher()
{
return $this->eventDispatcher;
}
/**
* @param string $eventName
*
* @return bool
*/
protected function hasListener($eventName)
{
return $this->getDispatcher()->hasListeners($eventName);
}
/**
* @param string $eventName
* @param mixed $payload
* @return ThemeEvent
*/
public function triggerEvent(string $eventName, $payload = null)
{
$themeEvent = new ThemeEvent($this->user, $payload);
if ($this->hasListener($eventName)) {
$this->getDispatcher()->dispatch($themeEvent, $eventName);
}
return $themeEvent;
}
}

View file

@ -52,7 +52,7 @@ final class MarkdownExtension extends AbstractExtension
/**
* Transforms the entities comment (customer, project, activity ...) into HTML.
*
* @param string $content
* @param string|null $content
* @param bool $fullLength
* @return string
*/

View file

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Twig\Runtime;
use App\Event\ThemeEvent;
use App\Security\CurrentUser;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Twig\Extension\RuntimeExtensionInterface;
final class ThemeEventExtension implements RuntimeExtensionInterface
{
/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;
/**
* @var CurrentUser
*/
private $user;
public function __construct(EventDispatcherInterface $dispatcher, CurrentUser $user)
{
$this->eventDispatcher = $dispatcher;
$this->user = $user;
}
/**
* @param string $eventName
* @param mixed|null $payload
* @return ThemeEvent
*/
public function trigger(string $eventName, $payload = null): ThemeEvent
{
$themeEvent = new ThemeEvent($this->user->getUser(), $payload);
if ($this->eventDispatcher->hasListeners($eventName)) {
$this->eventDispatcher->dispatch($themeEvent, $eventName);
}
return $themeEvent;
}
}

View file

@ -30,7 +30,7 @@ abstract class AbstractWidgetType implements WidgetInterface
*/
protected $data;
public function setId(string $id): AbstractWidgetType
public function setId(string $id): self
{
$this->id = $id;
@ -42,7 +42,7 @@ abstract class AbstractWidgetType implements WidgetInterface
return $this->id;
}
public function setData($data): AbstractWidgetType
public function setData($data): self
{
$this->data = $data;
@ -58,7 +58,7 @@ abstract class AbstractWidgetType implements WidgetInterface
return $this->data;
}
public function setTitle(string $title): AbstractWidgetType
public function setTitle(string $title): self
{
$this->title = $title;
@ -70,7 +70,7 @@ abstract class AbstractWidgetType implements WidgetInterface
return $this->title;
}
public function setOptions(array $options): AbstractWidgetType
public function setOptions(array $options): self
{
foreach ($options as $key => $value) {
$this->options[$key] = $value;

View file

@ -9,10 +9,13 @@
namespace App\Widget\Type;
class Counter extends SimpleWidget
use App\Repository\TimesheetRepository;
final class Counter extends SimpleStatisticChart
{
public function __construct()
public function __construct(TimesheetRepository $repository)
{
parent::__construct($repository);
$this->setOption('dataType', 'int');
}
}

View file

@ -11,12 +11,11 @@ namespace App\Widget\Type;
use App\Entity\Activity;
use App\Entity\Project;
use App\Entity\User;
use App\Repository\TimesheetRepository;
use App\Security\CurrentUser;
use App\Timesheet\UserDateTimeFactory;
use DateTime;
class DailyWorkingTimeChart extends SimpleWidget
class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
{
public const DEFAULT_CHART = 'bar';
@ -24,27 +23,26 @@ class DailyWorkingTimeChart extends SimpleWidget
* @var TimesheetRepository
*/
protected $repository;
/**
* @var UserDateTimeFactory
*/
private $dateTimeFactory;
public function __construct(TimesheetRepository $repository, CurrentUser $user, UserDateTimeFactory $dateTime)
public function __construct(TimesheetRepository $repository)
{
$this->repository = $repository;
$this->dateTimeFactory = $dateTime;
$this->setId('DailyWorkingTimeChart');
$this->setTitle('stats.yourWorkingHours');
$this->setOptions([
'begin' => 'monday this week 00:00:00',
'end' => 'sunday this week 23:59:59',
'color' => '',
'user' => $user->getUser(),
'type' => self::DEFAULT_CHART,
'id' => '',
]);
}
public function setUser(User $user): void
{
$this->setOption('user', $user);
}
public function getOptions(array $options = []): array
{
$options = parent::getOptions($options);
@ -65,16 +63,20 @@ class DailyWorkingTimeChart extends SimpleWidget
$options = $this->getOptions($options);
$user = $options['user'];
if (null === $user || !($user instanceof User)) {
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
}
if ($options['begin'] instanceof DateTime) {
$begin = $options['begin'];
} else {
$begin = new DateTime($options['begin'], $this->dateTimeFactory->getTimezone());
$begin = new DateTime($options['begin'], new \DateTimeZone($user->getTimezone()));
}
if ($options['end'] instanceof DateTime) {
$end = $options['end'];
} else {
$end = new DateTime($options['end'], $this->dateTimeFactory->getTimezone());
$end = new DateTime($options['end'], new \DateTimeZone($user->getTimezone()));
}
$activities = [];

View file

@ -9,37 +9,40 @@
namespace App\Widget\Type;
use App\Entity\User;
use App\Repository\TimesheetRepository;
use App\Security\CurrentUser;
use App\Timesheet\UserDateTimeFactory;
use DateTime;
final class PaginatedWorkingTimeChart extends SimpleWidget
final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
{
/**
* @var TimesheetRepository
*/
private $repository;
/**
* @var UserDateTimeFactory
*/
private $dateTimeFactory;
public function __construct(TimesheetRepository $repository, CurrentUser $user, UserDateTimeFactory $dateTime)
public function __construct(TimesheetRepository $repository)
{
$this->repository = $repository;
$this->dateTimeFactory = $dateTime;
$this->setId('PaginatedWorkingTimeChart');
$this->setTitle('stats.yourWorkingHours');
$this->setOptions([
'year' => (new DateTime('now', $this->dateTimeFactory->getTimezone()))->format('Y'),
'week' => (new DateTime('now', $this->dateTimeFactory->getTimezone()))->format('W'),
'user' => $user->getUser(),
'year' => (new DateTime('now'))->format('Y'),
'week' => (new DateTime('now'))->format('W'),
'type' => 'bar',
]);
}
public function setUser(User $user): void
{
$this->setOption('user', $user);
$now = new DateTime('now', new \DateTimeZone($user->getTimezone()));
$this->setOptions([
'year' => $now->format('Y'),
'week' => $now->format('W'),
]);
}
public function getOptions(array $options = []): array
{
$options = parent::getOptions($options);
@ -51,9 +54,9 @@ final class PaginatedWorkingTimeChart extends SimpleWidget
return $options;
}
private function getDate($year, $week, $day, $hour, $minute, $second)
private function getDate(\DateTimeZone $timezone, $year, $week, $day, $hour, $minute, $second)
{
$now = new DateTime('now', $this->dateTimeFactory->getTimezone());
$now = new DateTime('now', $timezone);
$now->setISODate($year, $week, $day);
$now->setTime($hour, $minute, $second);
@ -63,10 +66,16 @@ final class PaginatedWorkingTimeChart extends SimpleWidget
public function getData(array $options = [])
{
$options = $this->getOptions($options);
$user = $options['user'];
$weekBegin = $this->getDate($options['year'], $options['week'], 1, 0, 0, 0);
$weekEnd = $this->getDate($options['year'], $options['week'], 7, 23, 59, 59);
$user = $options['user'];
if (null === $user || !($user instanceof User)) {
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
}
$timezone = new \DateTimeZone($user->getTimezone());
$weekBegin = $this->getDate($timezone, $options['year'], $options['week'], 1, 0, 0, 0);
$weekEnd = $this->getDate($timezone, $options['year'], $options['week'], 7, 23, 59, 59);
return [
'begin' => clone $weekBegin,
@ -74,8 +83,8 @@ final class PaginatedWorkingTimeChart extends SimpleWidget
'stats' => $this->repository->getDailyStats($user, $weekBegin, $weekEnd),
'day' => $this->repository->getStatistic(
'duration',
new DateTime('00:00:00', $this->dateTimeFactory->getTimezone()),
new DateTime('23:59:59', $this->dateTimeFactory->getTimezone()),
new DateTime('00:00:00', $timezone),
new DateTime('23:59:59', $timezone),
$user
),
'week' => $this->repository->getStatistic(
@ -92,8 +101,8 @@ final class PaginatedWorkingTimeChart extends SimpleWidget
),
'year' => $this->repository->getStatistic(
'duration',
new DateTime(sprintf('01 january %s 00:00:00', $options['year']), $this->dateTimeFactory->getTimezone()),
new DateTime(sprintf('31 december %s 23:59:59', $options['year']), $this->dateTimeFactory->getTimezone()),
new DateTime(sprintf('01 january %s 00:00:00', $options['year']), $timezone),
new DateTime(sprintf('31 december %s 23:59:59', $options['year']), $timezone),
$user
),
];

View file

@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Widget\Type;
use App\Entity\User;
use App\Repository\TimesheetRepository;
use App\Widget\WidgetException;
class SimpleStatisticChart extends SimpleWidget
{
/**
* @var TimesheetRepository
*/
private $repository;
/**
* @var string
*/
private $query;
/**
* @var string
*/
private $begin;
/**
* @var string
*/
private $end;
/**
* @var User
*/
private $user;
public function __construct(TimesheetRepository $repository)
{
$this->repository = $repository;
}
public function setQuery(string $query): SimpleStatisticChart
{
$this->query = $query;
return $this;
}
public function setBegin(?string $begin): SimpleStatisticChart
{
$this->begin = $begin;
return $this;
}
public function setEnd(?string $end): SimpleStatisticChart
{
$this->end = $end;
return $this;
}
public function setUser(User $user): SimpleStatisticChart
{
$this->user = $user;
return $this;
}
public function setData($data): AbstractWidgetType
{
throw new \InvalidArgumentException('Cannot set data on instances of SimpleStatisticChart');
}
/**
* @param array $options
* @return mixed|null
* @throws WidgetException
*/
public function getData(array $options = [])
{
$timezone = date_default_timezone_get();
if (null !== $this->user) {
$timezone = $this->user->getTimezone();
}
$timezone = new \DateTimeZone($timezone);
$begin = !empty($this->begin) ? new \DateTime($this->begin, $timezone) : null;
$end = !empty($this->end) ? new \DateTime($this->end, $timezone) : null;
try {
return $this->repository->getStatistic($this->query, $begin, $end, $this->user);
} catch (\Exception $ex) {
throw new WidgetException(
'Failed loading widget data: ' . $ex->getMessage()
);
}
}
}

View file

@ -13,23 +13,19 @@ use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\ProjectRepository;
use App\Security\CurrentUser;
class UserTeamProjects extends SimpleWidget implements AuthorizedWidget
class UserTeamProjects extends SimpleWidget implements AuthorizedWidget, UserWidget
{
/**
* @var ProjectRepository
*/
private $repository;
public function __construct(CurrentUser $user, ProjectRepository $repository)
public function __construct(ProjectRepository $repository)
{
$this->setId('UserTeamProjects');
$this->setTitle('label.my_team_projects');
$this->setOptions([
'user' => $user->getUser(),
'id' => '',
]);
$this->setOption('id', '');
$this->repository = $repository;
}
@ -80,4 +76,9 @@ class UserTeamProjects extends SimpleWidget implements AuthorizedWidget
{
return ['budget_team_project', 'budget_teamlead_project', 'budget_project'];
}
public function setUser(User $user): void
{
$this->setOption('user', $user);
}
}

View file

@ -10,18 +10,14 @@
namespace App\Widget\Type;
use App\Entity\User;
use App\Security\CurrentUser;
class UserTeams extends SimpleWidget implements AuthorizedWidget
class UserTeams extends SimpleWidget implements AuthorizedWidget, UserWidget
{
public function __construct(CurrentUser $user)
public function __construct()
{
$this->setId('UserTeams');
$this->setTitle('label.my_teams');
$this->setOptions([
'user' => $user->getUser(),
'id' => '',
]);
$this->setOption('id', '');
}
public function getOptions(array $options = []): array
@ -51,4 +47,9 @@ class UserTeams extends SimpleWidget implements AuthorizedWidget
{
return ['view_team_member', 'view_team'];
}
public function setUser(User $user): void
{
$this->setOption('user', $user);
}
}

View file

@ -0,0 +1,22 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Widget\Type;
use App\Entity\User;
interface UserWidget
{
/**
* Sets the current user.
*
* @param User $user
*/
public function setUser(User $user): void;
}

View file

@ -9,6 +9,6 @@
namespace App\Widget\Type;
class YearChart extends SimpleWidget
final class YearChart extends SimpleStatisticChart
{
}

View file

@ -16,11 +16,11 @@ class WidgetService
/**
* @var WidgetRendererInterface[]
*/
protected $renderer = [];
private $renderer = [];
/**
* @var WidgetRepository
*/
protected $repository;
private $repository;
/**
* @param WidgetRepository $repository
@ -34,10 +34,6 @@ class WidgetService
$this->repository = $repository;
}
/**
* @param string $widget
* @return bool
*/
public function hasWidget(string $widget): bool
{
return $this->repository->has($widget);

View file

@ -1 +1 @@
{{ render_widget('PaginatedWorkingTimeChart', {'year': year, 'week': week}) }}
{{ render_widget('PaginatedWorkingTimeChart', {'user': user, 'year': year, 'week': week}) }}

View file

@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\User;
/**
* @group integration
*/
class WidgetControllerTest extends ControllerBaseTest
{
public function testIsSecure()
{
$this->assertUrlIsSecured('/widgets/working-time/2020/1');
}
public function testWorkingtimechartAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/widgets/working-time/2020/1');
$content = $client->getResponse()->getContent();
self::assertStringContainsString('id="PaginatedWorkingTimeChart"', $content);
self::assertStringContainsString('myChart = new Chart', $content);
self::assertStringContainsString("KimaiPaginatedBoxWidget.create('#PaginatedWorkingTimeChart');", $content);
}
}

View file

@ -9,10 +9,8 @@
namespace App\Tests\Repository;
use App\Entity\User;
use App\Repository\TimesheetRepository;
use App\Repository\WidgetRepository;
use App\Tests\Mocks\Security\CurrentUserFactory;
use App\Widget\Type\CompoundChart;
use App\Widget\Type\Counter;
use App\Widget\WidgetException;
@ -26,9 +24,8 @@ class WidgetRepositoryTest extends TestCase
public function testHasWidget()
{
$repoMock = $this->createMock(TimesheetRepository::class);
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => []]);
$sut = new WidgetRepository($repoMock, ['test' => []]);
$this->assertFalse($sut->has('foo'));
$this->assertTrue($sut->has('test'));
@ -40,9 +37,8 @@ class WidgetRepositoryTest extends TestCase
$this->expectExceptionMessage('Cannot find widget "foo".');
$repoMock = $this->createMock(TimesheetRepository::class);
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => []]);
$sut = new WidgetRepository($repoMock, ['test' => []]);
$sut->get('foo');
}
@ -52,21 +48,19 @@ class WidgetRepositoryTest extends TestCase
$this->expectExceptionMessage('Unknown widget type "FooBar"');
$repoMock = $this->createMock(TimesheetRepository::class);
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => ['type' => 'FooBar', 'user' => false]]);
$sut = new WidgetRepository($repoMock, ['test' => ['type' => 'FooBar', 'user' => false]]);
$sut->get('test');
}
public function testGetWidgetTriggersExceptionOnWrongClass()
{
$this->expectException(WidgetException::class);
$this->expectExceptionMessage('Widget type "App\Widget\Type\CompoundChart" is not an instance of "App\Widget\Type\AbstractWidgetType"');
$this->expectExceptionMessage('Widget type "App\Widget\Type\CompoundChart" is not an instance of "App\Widget\Type\SimpleStatisticChart"');
$repoMock = $this->createMock(TimesheetRepository::class);
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => ['type' => CompoundChart::class, 'user' => false]]);
$sut = new WidgetRepository($repoMock, ['test' => ['type' => CompoundChart::class, 'user' => false]]);
$sut->get('test');
}
@ -78,8 +72,6 @@ class WidgetRepositoryTest extends TestCase
$repoMock = $this->createMock(TimesheetRepository::class);
$repoMock->method('getStatistic')->willReturn($data);
$userMock = (new CurrentUserFactory($this))->create(new User());
$widget = [
'color' => 'sunny',
'icon' => 'far fa-test',
@ -91,7 +83,7 @@ class WidgetRepositoryTest extends TestCase
'type' => Counter::class,
];
$sut = new WidgetRepository($repoMock, $userMock, ['test' => $widget]);
$sut = new WidgetRepository($repoMock, ['test' => $widget]);
$widget = $sut->get('test');
$options = $widget->getOptions();

View file

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Twig;
use App\Twig\EventExtensions;
use PHPUnit\Framework\TestCase;
use Twig\TwigFunction;
/**
* @covers \App\Twig\EventExtensions
*/
class EventExtensionsTest extends TestCase
{
protected function getSut(): EventExtensions
{
return new EventExtensions();
}
public function testGetFunctions()
{
$functions = ['trigger'];
$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());
}
}
}

View file

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Twig\Runtime;
use App\Entity\User;
use App\Event\ThemeEvent;
use App\Tests\Mocks\Security\CurrentUserFactory;
use App\Twig\Runtime\ThemeEventExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* @covers \App\Twig\Runtime\ThemeEventExtension
*/
class ThemeEventExtensionTest extends TestCase
{
protected function getSut(bool $hasListener = true): ThemeEventExtension
{
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$dispatcher->expects($this->once())->method('hasListeners')->willReturn($hasListener);
$dispatcher->expects($hasListener ? $this->once() : $this->never())->method('dispatch');
$user = (new CurrentUserFactory($this))->create(new User());
return new ThemeEventExtension($dispatcher, $user);
}
public function testTrigger()
{
$sut = $this->getSut();
$event = $sut->trigger('foo', []);
self::assertInstanceOf(ThemeEvent::class, $event);
}
public function testTriggerWithoutListener()
{
$sut = $this->getSut(false);
$event = $sut->trigger('foo', []);
self::assertInstanceOf(ThemeEvent::class, $event);
}
}

View file

@ -10,7 +10,7 @@
namespace App\Tests\Twig;
use App\Twig\WidgetExtension;
use App\Widget\Type\Counter;
use App\Widget\Type\More;
use App\Widget\WidgetInterface;
use App\Widget\WidgetRendererInterface;
use App\Widget\WidgetService;
@ -72,7 +72,7 @@ class WidgetExtensionTest extends TestCase
public function testRenderWidgetByString()
{
$widget = new Counter();
$widget = new More();
$sut = $this->getSut(true, $widget, new TestRenderer());
$options = ['foo' => 'bar', 'dataType' => 'blub'];
$result = $sut->renderWidget('test', $options);
@ -82,7 +82,7 @@ class WidgetExtensionTest extends TestCase
public function testRenderWidgetObject()
{
$widget = new Counter();
$widget = new More();
$sut = $this->getSut(null, null, new TestRenderer());
$options = ['foo' => 'bar', 'dataType' => 'blub'];
$result = $sut->renderWidget($widget, $options);

View file

@ -12,7 +12,7 @@ namespace App\Tests\Widget\Renderer;
use App\Widget\Renderer\CompoundChartRenderer;
use App\Widget\Type\CompoundChart;
use App\Widget\Type\CompoundRow;
use App\Widget\Type\Counter;
use App\Widget\Type\More;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
@ -40,7 +40,7 @@ class CompoundChartRendererTest extends TestCase
$sut = new CompoundChartRenderer($twig);
$row = new CompoundChart();
$row->setTitle('foo-bar');
$row->addWidget(new Counter());
$row->addWidget(new More());
$result = $sut->render($row);
$result = json_decode($result, true);

View file

@ -12,7 +12,7 @@ namespace App\Tests\Widget\Renderer;
use App\Widget\Renderer\CompoundRowRenderer;
use App\Widget\Type\CompoundChart;
use App\Widget\Type\CompoundRow;
use App\Widget\Type\Counter;
use App\Widget\Type\More;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
@ -40,7 +40,7 @@ class CompoundRowRendererTest extends TestCase
$sut = new CompoundRowRenderer($twig);
$row = new CompoundRow();
$row->setTitle('foo-bar');
$row->addWidget(new Counter());
$row->addWidget(new More());
$result = $sut->render($row);
$result = json_decode($result, true);

View file

@ -10,7 +10,6 @@
namespace App\Tests\Widget\Renderer;
use App\Widget\Renderer\SimpleWidgetRenderer;
use App\Widget\Type\Counter;
use App\Widget\Type\More;
use App\Widget\Type\SimpleWidget;
use PHPUnit\Framework\TestCase;
@ -60,7 +59,6 @@ class SimpleWidgetRendererTest extends TestCase
{
return [
[new SimpleWidget(), 'widget/widget-simplewidget.html.twig', 'yellow'],
[new Counter(), 'widget/widget-counter.html.twig', 'asdfgh'],
[new More(), 'widget/widget-more.html.twig', '#123456'],
];
}

View file

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Widget\Type;
/**
* @covers \App\Widget\Type\SimpleStatisticChart
*/
abstract class AbstractSimpleStatisticsWidgetTypeTest extends AbstractWidgetTypeTest
{
public function testData()
{
$sut = $this->createSut();
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Cannot set data on instances of SimpleStatisticChart');
$sut->setData(10);
}
}

View file

@ -38,7 +38,6 @@ abstract class AbstractWidgetTypeTest extends TestCase
self::assertInstanceOf(AbstractWidgetType::class, $sut->setOptions([]));
self::assertInstanceOf(AbstractWidgetType::class, $sut->setId(''));
self::assertInstanceOf(AbstractWidgetType::class, $sut->setTitle(''));
self::assertInstanceOf(AbstractWidgetType::class, $sut->setData(''));
}
public function testSetter()
@ -57,8 +56,14 @@ abstract class AbstractWidgetTypeTest extends TestCase
// id
$sut->setId('cvbnmyx');
self::assertEquals('cvbnmyx', $sut->getId());
}
public function testData()
{
$sut = $this->createSut();
self::assertInstanceOf(AbstractWidgetType::class, $sut->setData(''));
// data
$sut->setData('slkudfhalksjdhfkljsahdf');
self::assertEquals('slkudfhalksjdhfkljsahdf', $sut->getData());

View file

@ -9,6 +9,7 @@
namespace App\Tests\Widget\Type;
use App\Repository\TimesheetRepository;
use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\Counter;
use App\Widget\Type\SimpleWidget;
@ -17,11 +18,14 @@ use App\Widget\Type\SimpleWidget;
* @covers \App\Widget\Type\Counter
* @covers \App\Widget\Type\SimpleWidget
*/
class CounterTest extends AbstractWidgetTypeTest
class CounterTest extends AbstractSimpleStatisticsWidgetTypeTest
{
public function createSut(): AbstractWidgetType
{
return new Counter();
$sut = new Counter($this->createMock(TimesheetRepository::class));
$sut->setQuery(TimesheetRepository::STATS_QUERY_ACTIVE);
return $sut;
}
public function getDefaultOptions(): array
@ -37,7 +41,8 @@ class CounterTest extends AbstractWidgetTypeTest
public function testTemplateName()
{
$sut = new Counter();
/** @var Counter $sut */
$sut = $this->createSut();
self::assertEquals('widget/widget-counter.html.twig', $sut->getTemplateName());
}
}

View file

@ -12,8 +12,6 @@ namespace App\Tests\Widget\Type;
use App\Entity\User;
use App\Model\Statistic\Day;
use App\Repository\TimesheetRepository;
use App\Tests\Mocks\Security\CurrentUserFactory;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\DailyWorkingTimeChart;
use App\Widget\Type\SimpleWidget;
@ -30,11 +28,11 @@ class DailyWorkingTimeChartTest extends TestCase
public function createSut(): AbstractWidgetType
{
$repository = $this->createMock(TimesheetRepository::class);
$mockFactory = new UserDateTimeFactoryFactory($this);
$userFactory = new CurrentUserFactory($this);
$user = $userFactory->create(new User(), 'Europe/Berlin');
return new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));
$sut = new DailyWorkingTimeChart($repository);
$sut->setUser(new User());
return $sut;
}
public function testExtendsSimpleWidget()
@ -107,12 +105,8 @@ class DailyWorkingTimeChartTest extends TestCase
];
});
$userFactory = new CurrentUserFactory($this);
$user = $userFactory->create(new User(), 'Europe/Berlin');
$mockFactory = new UserDateTimeFactoryFactory($this);
$sut = new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));
$sut = new DailyWorkingTimeChart($repository);
$sut->setUser(new User());
$data = $sut->getData([]);
self::assertCount(2, $data);
self::assertArrayHasKey('activities', $data);

View file

@ -9,17 +9,21 @@
namespace App\Tests\Widget\Type;
use App\Repository\TimesheetRepository;
use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\YearChart;
/**
* @covers \App\Widget\Type\YearChart
*/
class YearChartTest extends AbstractWidgetTypeTest
class YearChartTest extends AbstractSimpleStatisticsWidgetTypeTest
{
public function createSut(): AbstractWidgetType
{
return new YearChart();
$sut = new YearChart($this->createMock(TimesheetRepository::class));
$sut->setQuery(TimesheetRepository::STATS_QUERY_ACTIVE);
return $sut;
}
public function getDefaultOptions(): array