release 1.17.2 (#3135)
* allow to input password interactively in console * added block to simplify overwriting export template parts * prevent installation in PHP 8.1 * composer update * phpunit version to 9 * support negative amounts in excel export * added system configuration actions for calendar, weekly timesheet, users * added method to render text with full markdown support
This commit is contained in:
@@ -20,6 +20,7 @@ use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @covers \App\Command\ChangePasswordCommand
|
||||
* @covers \App\Command\AbstractUserCommand
|
||||
* @group integration
|
||||
*/
|
||||
class ChangePasswordCommandTest extends KernelTestCase
|
||||
@@ -59,6 +60,7 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
$input = [
|
||||
'command' => $command->getName(),
|
||||
];
|
||||
$interactive = false;
|
||||
|
||||
if ($username !== null) {
|
||||
$input['username'] = $username;
|
||||
@@ -66,10 +68,19 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
|
||||
if ($password !== null) {
|
||||
$input['password'] = $password;
|
||||
} else {
|
||||
$interactive = true;
|
||||
}
|
||||
|
||||
$commandTester = new CommandTester($command);
|
||||
$commandTester->execute($input);
|
||||
|
||||
$options = [];
|
||||
if ($interactive) {
|
||||
$options = ['interactive' => true];
|
||||
$commandTester->setInputs(['12345678']);
|
||||
}
|
||||
|
||||
$commandTester->execute($input, $options);
|
||||
|
||||
return $commandTester;
|
||||
}
|
||||
@@ -100,11 +111,10 @@ class ChangePasswordCommandTest extends KernelTestCase
|
||||
$this->callCommand(null, '1234567890');
|
||||
}
|
||||
|
||||
public function testWithMissingPassword()
|
||||
public function testWithMissingPasswordAsksForPassword()
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage('Not enough arguments (missing: "password").');
|
||||
|
||||
$this->callCommand('1234567890', null);
|
||||
$commandTester = $this->callCommand('john_user', null);
|
||||
$output = $commandTester->getDisplay();
|
||||
$this->assertStringContainsString('[OK] Changed password for user "john_user".', $output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ use App\Tests\Configuration\TestConfigLoader;
|
||||
use App\Tests\Mocks\Saml\SamlAuthFactoryFactory;
|
||||
use OneLogin\Saml2\Auth;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Util\Xml;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
@@ -83,7 +82,7 @@ class SamlControllerTest extends TestCase
|
||||
|
||||
public function testMetadataAction()
|
||||
{
|
||||
$expected = <<<EOD
|
||||
$expectedXmlString = <<<EOD
|
||||
<?xml version="1.0"?>
|
||||
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2020-07-23T10:26:50Z" cacheDuration="PT604800S" entityID="https://127.0.0.1:8010/auth/saml/metadata">
|
||||
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
@@ -118,8 +117,13 @@ EOD;
|
||||
self::assertInstanceOf(Response::class, $result);
|
||||
self::assertEquals('xml', $result->headers->get('Content-Type'));
|
||||
|
||||
$expected = Xml::load($expected);
|
||||
$actual = Xml::load($result->getContent());
|
||||
$expected = new \DOMDocument();
|
||||
$tmp = $expected->loadXML($expectedXmlString);
|
||||
self::assertTrue($tmp);
|
||||
|
||||
$actual = new \DOMDocument();
|
||||
$tmp = $actual->loadXML($result->getContent());
|
||||
self::assertTrue($tmp);
|
||||
|
||||
// the "validUntil" attribute in the outer node changes per request
|
||||
self::assertEquals($expected->firstChild->firstChild, $actual->firstChild->firstChild);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
use App\Tests\Configuration\TestConfigLoader;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
@@ -33,6 +34,11 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
$this->request($client, '/calendar/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'create modal-ajax-form' => $this->createUrl('/timesheet/create'),
|
||||
'help' => 'https://www.kimai.org/documentation/calendar.html'
|
||||
]);
|
||||
|
||||
$crawler = $client->getCrawler();
|
||||
$calendar = $crawler->filter('div#timesheet_calendar');
|
||||
$this->assertEquals(1, $calendar->count());
|
||||
@@ -40,6 +46,18 @@ class CalendarControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals(1, $dragAndDropBoxes->count());
|
||||
}
|
||||
|
||||
public function testCalendarActionAsSuperAdmin()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/calendar/');
|
||||
|
||||
$this->assertPageActions($client, [
|
||||
'create modal-ajax-form' => $this->createUrl('/timesheet/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/calendar'),
|
||||
'help' => 'https://www.kimai.org/documentation/calendar.html'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCalendarActionWithGoogleSource()
|
||||
{
|
||||
$loader = new TestConfigLoader([]);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerComment;
|
||||
use App\Entity\CustomerMeta;
|
||||
@@ -161,6 +162,13 @@ class CustomerControllerTest extends ControllerBaseTest
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('A beautiful and short comment **with some** markdown formatting', $node->html());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService->offsetSet('timesheet.markdown_content', true);
|
||||
$this->assertAccessIsGranted($client, '/admin/customer/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('<p>A beautiful and short comment <strong>with some</strong> markdown formatting</p>', $node->html());
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\ActivityMeta;
|
||||
use App\Entity\ActivityRate;
|
||||
@@ -260,6 +261,13 @@ class ProjectControllerTest extends ControllerBaseTest
|
||||
$this->assertIsRedirect($client, $this->createUrl('/admin/project/1/details'));
|
||||
$client->followRedirect();
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('A beautiful and long comment **with some** markdown formatting', $node->html());
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$configService = static::$kernel->getContainer()->get(SystemConfiguration::class);
|
||||
$configService->offsetSet('timesheet.markdown_content', true);
|
||||
$this->assertAccessIsGranted($client, '/admin/project/1/details');
|
||||
$node = $client->getCrawler()->filter('div.box#comments_box .direct-chat-text');
|
||||
self::assertStringContainsString('<p>A beautiful and long comment <strong>with some</strong> markdown formatting</p>', $node->html());
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ class UserControllerTest extends ControllerBaseTest
|
||||
'visibility' => '#',
|
||||
'download toolbar-action' => $this->createUrl('/admin/user/export'),
|
||||
'create' => $this->createUrl('/admin/user/create'),
|
||||
'settings modal-ajax-form' => $this->createUrl('/admin/system-config/edit/user'),
|
||||
'help' => 'https://www.kimai.org/documentation/users.html'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Tests\DependencyInjection;
|
||||
|
||||
use App\DependencyInjection\AppExtension;
|
||||
use PHPUnit\Framework\Error\Notice;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
@@ -301,7 +300,7 @@ class AppExtensionTest extends TestCase
|
||||
*/
|
||||
public function testDurationOnlyDeprecationIsTriggered()
|
||||
{
|
||||
$this->expectException(Notice::class);
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Found ambiguous configuration: remove "kimai.timesheet.duration_only" and set "kimai.timesheet.mode" instead.');
|
||||
|
||||
$minConfig = $this->getMinConfig();
|
||||
@@ -449,7 +448,7 @@ class AppExtensionTest extends TestCase
|
||||
|
||||
public function testWithBundleConfigurationFailsOnDuplicatedKey()
|
||||
{
|
||||
$this->expectException(Notice::class);
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Invalid bundle configuration "timesheet" found, skipping');
|
||||
|
||||
$bundleConfig = [
|
||||
@@ -463,7 +462,7 @@ class AppExtensionTest extends TestCase
|
||||
|
||||
public function testWithBundleConfigurationFailsOnNonArray()
|
||||
{
|
||||
$this->expectException(Notice::class);
|
||||
$this->expectNotice();
|
||||
$this->expectExceptionMessage('Invalid bundle configuration found, skipping all bundle configuration');
|
||||
|
||||
$container = $this->getContainer();
|
||||
|
||||
@@ -163,7 +163,18 @@ abstract class AbstractRendererTest extends KernelTestCase
|
||||
->setMetaField((new TimesheetMeta())->setName('foo2')->setValue('meta-bar2')->setIsVisible(true))
|
||||
;
|
||||
|
||||
$entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5];
|
||||
$timesheet6 = new Timesheet();
|
||||
$timesheet6
|
||||
->setDuration(400)
|
||||
->setFixedRate(-100.92)
|
||||
->setUser((new User())->setUsername('nivek'))
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setBegin(new \DateTime('2019-06-16 12:00:00'))
|
||||
->setEnd(new \DateTime('2019-06-16 12:06:40'))
|
||||
;
|
||||
|
||||
$entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5, $timesheet6];
|
||||
|
||||
$query = new TimesheetQuery();
|
||||
$query->setActivities([$activity]);
|
||||
|
||||
@@ -34,7 +34,7 @@ class CsvRendererTest extends AbstractRendererTest
|
||||
public function getTestModel()
|
||||
{
|
||||
return [
|
||||
['400', '2437.12', ' EUR 1,947.99 ', 6, 5, 1, 2, 2]
|
||||
['400', '2437.12', ' EUR 1,947.99 ', 7, 6, 1, 2, 2]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -107,8 +107,40 @@ class CsvRendererTest extends AbstractRendererTest
|
||||
27 => 'ORDER-123',
|
||||
];
|
||||
|
||||
self::assertEquals(6, \count($all));
|
||||
$expected2 = [
|
||||
0 => '2019-06-16',
|
||||
1 => '12:00',
|
||||
2 => '12:06',
|
||||
3 => '400',
|
||||
4 => '0',
|
||||
5 => '',
|
||||
6 => 'nivek',
|
||||
7 => 'nivek',
|
||||
8 => 'Customer Name',
|
||||
9 => 'project name',
|
||||
10 => 'activity description',
|
||||
11 => '',
|
||||
12 => '',
|
||||
13 => '',
|
||||
14 => '',
|
||||
15 => '',
|
||||
16 => ' EUR -100.92',
|
||||
17 => '',
|
||||
18 => '',
|
||||
19 => 'customer-bar',
|
||||
20 => '',
|
||||
21 => 'project-foo2',
|
||||
22 => 'activity-bar',
|
||||
23 => 'timesheet',
|
||||
24 => 'work',
|
||||
25 => 'A-0123456789',
|
||||
26 => 'DE-9876543210',
|
||||
27 => 'ORDER-123',
|
||||
];
|
||||
|
||||
self::assertEquals(7, \count($all));
|
||||
self::assertEquals($expected, $all[5]);
|
||||
self::assertEquals($expected2, $all[6]);
|
||||
self::assertEquals(\count($expected), \count($all[0]));
|
||||
self::assertEquals('foo', $all[4][14]);
|
||||
}
|
||||
|
||||
@@ -82,11 +82,12 @@ class HtmlRendererTest extends AbstractRendererTest
|
||||
|
||||
$this->assertStringContainsString('<td>Customer Name</td>', $content);
|
||||
$this->assertStringContainsString('<td>project name</td>', $content);
|
||||
$this->assertStringContainsString('<span class="duration-format" data-duration="6600">01:50 h</span>', $content);
|
||||
$this->assertStringContainsString('<span class="duration-format" data-duration="7000">01:56 h</span>', $content);
|
||||
$this->assertStringContainsString('<td class="cost summary-rate">€2,437.12</td>', $content);
|
||||
$this->assertStringContainsString('-€100.92', $content);
|
||||
|
||||
// 5 times in the "full list" and once in the "summary with activities"
|
||||
$this->assertEquals(6, substr_count($content, 'activity description'));
|
||||
$this->assertEquals(7, substr_count($content, 'activity description'));
|
||||
$this->assertEquals(1, substr_count($content, '<td>activity description</td>'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class MarkdownExtensionTest extends TestCase
|
||||
$config = new SystemConfiguration($loader, ['timesheet' => ['markdown_content' => true]]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
$this->assertEquals('<p><em>test</em></p>', $sut->markdownToHtml('*test*'));
|
||||
$this->assertEquals('<p># foobar</p>', $sut->markdownToHtml('# foobar'));
|
||||
$this->assertEquals('<h1>foobar</h1>', $sut->markdownToHtml('# foobar'));
|
||||
$this->assertEquals(
|
||||
'<p><a href="javascript%3Aalert(`XSS`)">XSS</a></p>',
|
||||
$sut->markdownToHtml('[XSS](javascript:alert(`XSS`))')
|
||||
@@ -44,6 +44,9 @@ class MarkdownExtensionTest extends TestCase
|
||||
);
|
||||
$this->assertEquals('', $sut->timesheetContent(null));
|
||||
$this->assertEquals('', $sut->timesheetContent(''));
|
||||
$this->assertEquals('# foobar', $sut->timesheetContent('# foobar'));
|
||||
$this->assertEquals('## foobar', $sut->timesheetContent('## foobar'));
|
||||
$this->assertEquals('### foobar', $sut->timesheetContent('### foobar'));
|
||||
|
||||
$config = new SystemConfiguration($loader, ['timesheet' => ['markdown_content' => true]]);
|
||||
$sut = new MarkdownExtension(new Markdown(), $config);
|
||||
@@ -75,6 +78,9 @@ class MarkdownExtensionTest extends TestCase
|
||||
|
||||
$this->assertEquals('', $sut->commentContent(null));
|
||||
$this->assertEquals('', $sut->commentContent(''));
|
||||
$this->assertEquals('# foobar', $sut->commentContent('# foobar'));
|
||||
$this->assertEquals('## foobar', $sut->commentContent('## foobar'));
|
||||
$this->assertEquals('### foobar', $sut->commentContent('### foobar'));
|
||||
$this->assertEquals('<p>' . $loremIpsum . '</p>', $sut->commentContent($loremIpsum, true));
|
||||
$this->assertEquals('Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l …', $sut->commentContent($loremIpsum));
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@ use Symfony\Component\Dotenv\Dotenv;
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
(new Dotenv(false))->loadEnv(dirname(__DIR__) . '/.env');
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
|
||||
$env = $_SERVER['APP_ENV'] ?? 'prod';
|
||||
$debug = (bool) ($_SERVER['APP_DEBUG'] ?? (in_array($env, ['dev', 'test'])));
|
||||
|
||||
$kernel = new Kernel($env, $debug);
|
||||
$kernel->boot();
|
||||
|
||||
return $kernel->getContainer()->get('doctrine')->getManager();
|
||||
|
||||
Reference in New Issue
Block a user