dashboard widgets are configurable via config (#269)
This commit is contained in:
@@ -66,12 +66,11 @@ class TimesheetControllerTest extends ControllerBaseTest
|
||||
$docuUrl = $this->createUrl('/help/timesheet');
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
$this->assertContains(
|
||||
'<a href="'.$docuUrl.'"><i class="far fa-question-circle"></i></a>',
|
||||
'<a href="' . $docuUrl . '"><i class="far fa-question-circle"></i></a>',
|
||||
$response->getContent(),
|
||||
'Could not find link to documentation'
|
||||
);
|
||||
|
||||
// TODO more tests
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
91
tests/EventSubscriber/DashboardSubscriberTest.php
Normal file
91
tests/EventSubscriber/DashboardSubscriberTest.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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\Voter;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Event\DashboardEvent;
|
||||
use App\EventSubscriber\DashboardSubscriber;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\UserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
/**
|
||||
* @covers \App\EventSubscriber\DashboardSubscriber
|
||||
*/
|
||||
class DashboardSubscriberTest extends TestCase
|
||||
{
|
||||
public function testGetSubscribedEvents()
|
||||
{
|
||||
$events = DashboardSubscriber::getSubscribedEvents();
|
||||
$this->assertArrayHasKey(DashboardEvent::DASHBOARD, $events);
|
||||
$methodName = $events[DashboardEvent::DASHBOARD][0];
|
||||
$this->assertTrue(method_exists(DashboardSubscriber::class, $methodName));
|
||||
}
|
||||
|
||||
public function testWithNonAdminUser()
|
||||
{
|
||||
$sut = $this->getSubscriber(false, 13, 28, 37, 5);
|
||||
$event = new DashboardEvent(new User());
|
||||
|
||||
$this->assertEquals(0, count($event->getSections()));
|
||||
$sut->onDashboardEvent($event);
|
||||
$this->assertEquals(0, count($event->getSections()));
|
||||
}
|
||||
|
||||
public function testWithAdminUser()
|
||||
{
|
||||
$sut = $this->getSubscriber(true, 13, 28, 37, 5);
|
||||
$event = new DashboardEvent(new User());
|
||||
|
||||
$this->assertEquals(0, count($event->getSections()));
|
||||
$sut->onDashboardEvent($event);
|
||||
|
||||
$sections = $event->getSections();
|
||||
$widgets = $sections[0]->getWidgets();
|
||||
|
||||
$this->assertEquals(1, count($sections));
|
||||
$this->assertEquals(4, count($widgets));
|
||||
|
||||
$this->assertEquals('stats.userTotal', $widgets[0]->getTitle());
|
||||
$this->assertEquals(13, $widgets[0]->getData());
|
||||
|
||||
$this->assertEquals('stats.customerTotal', $widgets[1]->getTitle());
|
||||
$this->assertEquals(5, $widgets[1]->getData());
|
||||
|
||||
$this->assertEquals('stats.projectTotal', $widgets[2]->getTitle());
|
||||
$this->assertEquals(37, $widgets[2]->getData());
|
||||
|
||||
$this->assertEquals('stats.activityTotal', $widgets[3]->getTitle());
|
||||
$this->assertEquals(28, $widgets[3]->getData());
|
||||
}
|
||||
|
||||
protected function getSubscriber(bool $isAdmin, int $userCount, int $activityCount, int $projectCount, int $customerCount)
|
||||
{
|
||||
$authMock = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
|
||||
$authMock->method('isGranted')->willReturn($isAdmin);
|
||||
|
||||
$userMock = $this->getMockBuilder(UserRepository::class)->disableOriginalConstructor()->getMock();
|
||||
$userMock->method('countUser')->willReturn($userCount);
|
||||
|
||||
$projectMock = $this->getMockBuilder(ProjectRepository::class)->disableOriginalConstructor()->getMock();
|
||||
$projectMock->method('countProject')->willReturn($projectCount);
|
||||
|
||||
$activityMock = $this->getMockBuilder(ActivityRepository::class)->disableOriginalConstructor()->getMock();
|
||||
$activityMock->method('countActivity')->willReturn($activityCount);
|
||||
|
||||
$customerMock = $this->getMockBuilder(CustomerRepository::class)->disableOriginalConstructor()->getMock();
|
||||
$customerMock->method('countCustomer')->willReturn($customerCount);
|
||||
|
||||
return new DashboardSubscriber($authMock, $userMock, $activityMock, $projectMock, $customerMock);
|
||||
}
|
||||
}
|
||||
82
tests/Repository/WidgetRepositoryTest.php
Normal file
82
tests/Repository/WidgetRepositoryTest.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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\Repository;
|
||||
|
||||
use App\Model\Widget;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Repository\WidgetRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\WidgetRepository
|
||||
*/
|
||||
class WidgetRepositoryTest extends TestCase
|
||||
{
|
||||
public function testHasWidget()
|
||||
{
|
||||
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
|
||||
|
||||
$sut = new WidgetRepository($repoMock, ['test' => []]);
|
||||
|
||||
$this->assertFalse($sut->has('foo'));
|
||||
$this->assertTrue($sut->has('test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Cannot find widget: foo
|
||||
*/
|
||||
public function testGetWidgetThrowsExceptionOnNonExistingWidget()
|
||||
{
|
||||
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
|
||||
|
||||
$sut = new WidgetRepository($repoMock, ['test' => []]);
|
||||
$sut->get('foo', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getWidgetData
|
||||
*/
|
||||
public function testGetWidget($data, $query, $dataType)
|
||||
{
|
||||
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
|
||||
$repoMock->method('getStatistic')->willReturn($data);
|
||||
|
||||
$widget = [
|
||||
'color' => 'sunny',
|
||||
'icon' => 'far fa-test',
|
||||
'user' => false,
|
||||
'begin' => null,
|
||||
'end' => null,
|
||||
'query' => $query,
|
||||
'title' => 'Test widget',
|
||||
];
|
||||
|
||||
$sut = new WidgetRepository($repoMock, ['test' => $widget]);
|
||||
$widget = $sut->get('test', null);
|
||||
|
||||
$this->assertEquals('Test widget', $widget->getTitle());
|
||||
$this->assertEquals($data, $widget->getData());
|
||||
$this->assertEquals('sunny', $widget->getColor());
|
||||
$this->assertEquals('far fa-test', $widget->getIcon());
|
||||
$this->assertEquals($dataType, $widget->getDataType());
|
||||
}
|
||||
|
||||
public function getWidgetData()
|
||||
{
|
||||
return [
|
||||
[12, TimesheetRepository::STATS_QUERY_DURATION, Widget::DATA_TYPE_DURATION],
|
||||
[112233, TimesheetRepository::STATS_QUERY_AMOUNT, Widget::DATA_TYPE_MONEY],
|
||||
[37, TimesheetRepository::STATS_QUERY_ACTIVE, Widget::DATA_TYPE_INT],
|
||||
[375, TimesheetRepository::STATS_QUERY_RATE, Widget::DATA_TYPE_INT],
|
||||
[['test' => 'foo'], TimesheetRepository::STATS_QUERY_USER, Widget::DATA_TYPE_INT],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -161,40 +161,20 @@ class ExtensionsTest extends TestCase
|
||||
public function testIcon()
|
||||
{
|
||||
$icons = [
|
||||
'user' => 'fas fa-user',
|
||||
'customer' => 'fas fa-users',
|
||||
'project' => 'fas fa-project-diagram',
|
||||
'activity' => 'fas fa-tasks',
|
||||
'admin' => 'fas fa-wrench',
|
||||
'invoice' => 'fas fa-file-invoice',
|
||||
'timesheet' => 'far fa-clock',
|
||||
'dashboard' => 'fas fa-tachometer-alt',
|
||||
'logout' => 'fas fa-sign-out-alt',
|
||||
'trash' => 'far fa-trash-alt',
|
||||
'delete' => 'far fa-trash-alt',
|
||||
'repeat' => 'fas fa-redo-alt',
|
||||
'edit' => 'far fa-edit',
|
||||
'manual' => 'fas fa-book',
|
||||
'help' => 'far fa-question-circle',
|
||||
'start' => 'fas fa-play-circle',
|
||||
'start-small' => 'fas fa-play-circle',
|
||||
'stop' => 'fas fa-stop',
|
||||
'stop-small' => 'far fa-stop-circle',
|
||||
'filter' => 'fas fa-filter',
|
||||
'create' => 'far fa-plus-square',
|
||||
'list' => 'fas fa-list',
|
||||
'print' => 'fas fa-print',
|
||||
'visibility' => 'far fa-eye',
|
||||
'calendar' => 'far fa-calendar-alt',
|
||||
'user', 'customer', 'project', 'activity', 'admin', 'invoice', 'timesheet', 'dashboard', 'logout', 'trash',
|
||||
'delete', 'repeat', 'edit', 'manual', 'help', 'start', 'start-small', 'stop', 'stop-small', 'filter',
|
||||
'create', 'list', 'print', 'visibility', 'calendar', 'money', 'duration',
|
||||
];
|
||||
|
||||
// test pre-defined icons
|
||||
$sut = $this->getSut('en');
|
||||
foreach ($icons as $icon => $class) {
|
||||
foreach ($icons as $icon) {
|
||||
$result = $sut->icon($icon);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon);
|
||||
$this->assertInternalType('string', $result);
|
||||
}
|
||||
|
||||
// test fallback will be returned
|
||||
$this->assertEquals('', $sut->icon('foo'));
|
||||
$this->assertEquals('bar', $sut->icon('foo', 'bar'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user