prepare release 1.14 (#2495)

* removed un-maintained docker file
* update all packages
* fix deprecations
* only show one line descriptions for customer, projects and activities
* allow to show export column in timesheet listing
This commit is contained in:
Kevin Papst
2021-04-08 18:07:57 +02:00
committed by GitHub
parent 2ce6e815ff
commit af9dea9226
30 changed files with 920 additions and 739 deletions

View File

@@ -1,5 +1,5 @@
unreleased=true
future-release=1.13
future-release=1.14
exclude-labels=duplicate,support,question,invalid,wontfix,release,waiting for feedback
enhancement_labels=>enhancement,Enhancement,feature request,translation i18n,technical debt,documentation
issues-wo-labels=false

View File

@@ -1,5 +1,8 @@
# Changelog
## [1.14](https://github.com/kevinpapst/kimai2/tree/1.14)
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/1.13...1.14)
## [1.13](https://github.com/kevinpapst/kimai2/tree/1.13)
[Full Changelog](https://github.com/kevinpapst/kimai2/compare/1.12...1.13)

View File

@@ -1,55 +0,0 @@
# 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.
FROM php:7.2.9-apache-stretch AS tmp_kimai2_base
RUN apt update && \
apt install -y --allow-unauthenticated \
git \
haveged \
libicu-dev \
libjpeg-dev \
libldap2-dev \
libldb-dev \
libpng-dev \
mysql-client \
unzip \
wget \
zip \
&& \
EXPECTED_SIGNATURE="$(wget -q -O - https://composer.github.io/installer.sig)" && \
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" && \
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid installer signature'; rm composer-setup.php; exit 1; fi && \
php composer-setup.php --quiet && \
rm composer-setup.php && \
mv /var/www/html/composer.phar /usr/bin/composer && \
docker-php-ext-install \
gd \
intl \
ldap \
pdo_mysql \
zip && \
apt remove -y wget && \
apt -y autoremove && \
apt clean && \
cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
FROM tmp_kimai2_base
RUN git clone https://github.com/kevinpapst/kimai2.git /opt/kimai && \
sed "s/prod/dev/g" /opt/kimai/.env.dist > /opt/kimai/.env && \
composer install --working-dir=/opt/kimai --dev --optimize-autoloader && \
/opt/kimai/bin/console doctrine:database:create && \
/opt/kimai/bin/console doctrine:schema:create && \
/opt/kimai/bin/console doctrine:migrations:version --add --all && \
/opt/kimai/bin/console cache:warmup && \
chown -R www-data:www-data /opt/kimai/var && \
chown www-data:www-data /opt/kimai/vendor/mpdf/mpdf/tmp
WORKDIR /opt/kimai
EXPOSE 8001
USER www-data
CMD /opt/kimai/bin/console server:run 0.0.0.0:8001

View File

@@ -1,5 +1,3 @@
<h1 align="center">Kimai 2 - online time-tracker</h1>
<p align="center">
<img src="https://raw.githubusercontent.com/kimai/images/master/repository-header.png" alt="Kimai logo">
</p>
@@ -13,29 +11,30 @@
<a href="https://www.bountysource.com/teams/kimai2"><img alt="Bountysource" src="https://img.shields.io/bountysource/team/kimai2/activity"></a>
</p>
Kimai is a free, open source and online time-tracking software designed for small businesses and freelancers.
<h1 align="center">Kimai 2 - Time Tracker</h1>
Kimai is a free, open source and online time tracking software designed for small businesses and freelancers.
It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Doctrine, AdminLTE, Webpack, ES6 etc.
## Introduction
- [Home](https://www.kimai.org) - The house of Kimai
- [Home](https://www.kimai.org) - Kimai project homepage
- [Blog](https://www.kimai.org/blog/) - Get the latest news
- [Documentation](https://www.kimai.org/documentation/) - Learn how to use
- [Translations](https://www.kimai.org/documentation/translations.html) - Kimai in your language
- [Documentation](https://www.kimai.org/documentation/) - Learn how to use Kimai
- [Translations](https://hosted.weblate.org/projects/kimai/#languages) - Kimai in your language
- [Migration](https://www.kimai.org/documentation/migration-v1.html) - Import data from v1
### Requirements
- PHP 7.2.9 or higher
- PHP 7.2.9 or higher ([PHP 8 not yet](https://github.com/kevinpapst/kimai2/pull/2158))
- MariaDB or MySQL
- Webserver (nginx, Apache)
- A modern browser
- Some PHP extensions, see [composer.json](composer.json) or [here](https://www.kimai.org/download/)
### About
This is the new version of the open source timetracker Kimai. It is stable and production ready, ships
with most advanced features from Kimai 1 and many new ones, including but not limited to:
This is the new version of the open source time tracker Kimai. It is stable and production ready, ships
with a lot of advanced features, including but not limited to:
JSON API, invoicing, data exports, multi-timer and punch-in punch-out mode, tagging, multi-user and multi-timezones,
authentication via SAML/LDAP/Database, customizable role permissions, responsive and ready for your mobile device,
@@ -50,7 +49,7 @@ user specific rates, advanced search & filtering, money and time budgets with re
### Updating Kimai
- [Update Kimai](https://www.kimai.org/documentation/updates.html) - the documentation
- [Update Kimai](https://www.kimai.org/documentation/updates.html) - get the latest version
- [UPGRADING guide](UPGRADING.md) - version specific steps
### Plugins

View File

@@ -7,8 +7,8 @@ As announced in the [README](README.md) I only support the latest available rele
| Version | Supported |
| ------- | ------------------ |
| master | :white_check_mark: |
| 1.13 | :white_check_mark: |
| < 1.13 | :x: |
| 1.14 | :white_check_mark: |
| < 1.14 | :x: |
## Reporting a Vulnerability
@@ -17,7 +17,7 @@ Please report any security related vulnerability in the [advisories section at G
I will work as fast as I can to fix the problem and publish a bugfix release / security update.
Depending on the size of the required fixes, this might take a couple of hours or a couple of days.
You can expect that your message will be answered ASAP, but please take into account that I am living in the timezone Europe/Berlin.
You can expect that your message will be answered ASAP, but please take into account that I am living in the timezone CET.
If your issue is valid and after I verified and fixed it, you will be mentioned in the release notes.
I am grateful for any (discrete) disclosure of vulnerabilities!

View File

@@ -2,7 +2,7 @@
"name": "kevinpapst/kimai2",
"license": "MIT",
"type": "project",
"description": "Kimai Time-Tracking 2",
"description": "Kimai - Time Tracking",
"authors": [
{
"name": "Kevin Papst",
@@ -10,7 +10,7 @@
}
],
"require": {
"php": "^7.2",
"php": "^7.2.9",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",

1224
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@ doctrine:
default_connection: default
connections:
default:
override_url: true
# existing migrations will fail if the schema filter is activated
#schema_filter: ~^(?!(bundle_migration_|kimai2_sessions))~
url: '%env(DATABASE_URL)%'

View File

@@ -26,7 +26,6 @@ fos_rest:
'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404
'Symfony\Component\HttpKernel\Exception\NotFoundHttpException': 404
'App\API\NotFoundException': 404
'Pagerfanta\Exception\OutOfRangeCurrentPageException': 404
body_listener:
enabled: true
decoders:
@@ -45,7 +44,7 @@ handcraftedinthealps_rest_routing:
routing_loader:
default_format: 'json'
prefix_methods: true
include_format: true
include_format: false
formats:
json: true
xml: false

View File

@@ -42,9 +42,9 @@ nelmio_api_doc:
- ^/api(?!/doc)
documentation:
info:
title: Kimai 2 - API Docs
title: Kimai - API Docs
description: |
JSON API for the Kimai 2 time-tracking software: [API documentation](https://www.kimai.org/documentation/rest-api.html), [Swagger definition file](doc.json)
JSON API for the Kimai time-tracking software: [API documentation](https://www.kimai.org/documentation/rest-api.html), [Swagger definition file](doc.json)
version: '0.6'
securityDefinitions:
apiUser:

View File

@@ -15,7 +15,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class NotFoundException extends NotFoundHttpException
{
public function __construct(string $message = 'Not found', \Exception $previous = null, int $code = 0, array $headers = [])
public function __construct(string $message = 'Not found', \Exception $previous = null, int $code = 404, array $headers = [])
{
parent::__construct($message, $previous, $code, $headers);
}

View File

@@ -0,0 +1,45 @@
<?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\EventSubscriber;
use Pagerfanta\Exception\NotValidMaxPerPageException;
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Catches Pagerfanta Exceptions and converts them to "normal" Http Exceptions with status code 404.
* This was mainly done to convert the 500 to 404 HTTP response code.
* This prevents also the need to register them in fos_rest.yaml for the API.
*/
final class PagerfantaExceptionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => ['onCoreException', 1]
];
}
/**
* @param ExceptionEvent $event
*/
public function onCoreException(ExceptionEvent $event)
{
$throwable = $event->getThrowable();
if ($throwable instanceof NotValidMaxPerPageException || $throwable instanceof OutOfRangeCurrentPageException) {
$notFoundHttpException = new NotFoundHttpException($throwable->getMessage(), $throwable, $throwable->getCode());
$event->setThrowable($notFoundHttpException);
}
}
}

View File

@@ -28,27 +28,25 @@ use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
/**
* Defines the form used to manipulate Timesheet entries.
* @internal
* Helper functions to manage dependent customer-project-activity fields.
*/
trait FormTrait
{
protected function addCustomer(FormBuilderInterface $builder, ?Customer $customer = null)
{
$builder
->add('customer', CustomerType::class, [
'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) {
$query = new CustomerFormTypeQuery($customer);
$query->setUser($builder->getOption('user'));
$builder->add('customer', CustomerType::class, [
'query_builder' => function (CustomerRepository $repo) use ($builder, $customer) {
$query = new CustomerFormTypeQuery($customer);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
'data' => $customer ? $customer : '',
'required' => false,
'placeholder' => '',
'mapped' => false,
'project_enabled' => true,
]);
return $repo->getQueryBuilderForFormType($query);
},
'data' => $customer ? $customer : '',
'required' => false,
'placeholder' => '',
'mapped' => false,
'project_enabled' => true,
]);
}
protected function addProject(FormBuilderInterface $builder, bool $isNew, ?Project $project = null, ?Customer $customer = null)
@@ -102,17 +100,15 @@ trait FormTrait
protected function addActivity(FormBuilderInterface $builder, ?Activity $activity = null, ?Project $project = null)
{
$builder
->add('activity', ActivityType::class, [
'placeholder' => '',
'query_builder' => function (ActivityRepository $repo) use ($builder, $activity, $project) {
$query = new ActivityFormTypeQuery($activity, $project);
$query->setUser($builder->getOption('user'));
$builder->add('activity', ActivityType::class, [
'placeholder' => '',
'query_builder' => function (ActivityRepository $repo) use ($builder, $activity, $project) {
$query = new ActivityFormTypeQuery($activity, $project);
$query->setUser($builder->getOption('user'));
return $repo->getQueryBuilderForFormType($query);
},
])
;
return $repo->getQueryBuilderForFormType($query);
},
]);
// replaces the activity select after submission, to make sure only activities for the selected project are displayed
$builder->addEventListener(
@@ -143,20 +139,23 @@ trait FormTrait
{
@trigger_error('FormTrait::addDescription() is deprecated and will be removed with 2.0, use DescriptionType instead', E_USER_DEPRECATED);
$builder
->add('description', DescriptionType::class, [
'required' => false,
'attr' => [
'autofocus' => 'autofocus'
]
]);
$builder->add('description', DescriptionType::class, [
'required' => false,
'attr' => [
'autofocus' => 'autofocus'
]
]);
}
/**
* @deprecated since 1.14
*/
protected function addTags(FormBuilderInterface $builder)
{
$builder
->add('tags', TagsType::class, [
'required' => false,
]);
@trigger_error('FormTrait::addTags() is deprecated and will be removed with 2.0, use TagsType instead', E_USER_DEPRECATED);
$builder->add('tags', TagsType::class, [
'required' => false,
]);
}
}

View File

@@ -16,6 +16,7 @@ use App\Form\Type\DurationType;
use App\Form\Type\FixedRateType;
use App\Form\Type\HourlyRateType;
use App\Form\Type\MetaFieldsCollectionType;
use App\Form\Type\TagsType;
use App\Form\Type\UserType;
use App\Form\Type\YesNoType;
use App\Repository\CustomerRepository;
@@ -121,8 +122,7 @@ class TimesheetEditForm extends AbstractType
$descriptionOptions['attr'] = ['autofocus' => 'autofocus'];
}
$builder->add('description', DescriptionType::class, $descriptionOptions);
$this->addTags($builder);
$builder->add('tags', TagsType::class, ['required' => false]);
$this->addRates($builder, $currency, $options);
$this->addUser($builder, $options);
$builder->add('metaFields', MetaFieldsCollectionType::class);

View File

@@ -69,6 +69,36 @@ final class MarkdownExtension implements RuntimeExtensionInterface
return $content;
}
/**
* Transforms the entities comment (customer, project, activity ...) into a one-liner.
*
* @param string|null $content
* @param bool $fullLength
* @return string
*/
public function commentOneLiner(?string $content, bool $fullLength = false): string
{
if (empty($content)) {
return '';
}
$addHellip = false;
if (!$fullLength && \strlen($content) > 52) {
$content = trim(substr($content, 0, 50));
$addHellip = true;
}
$content = explode(PHP_EOL, $content);
$result = $content[0];
if (\count($content) > 1 || $addHellip) {
$result .= ' &hellip;';
}
return $result;
}
/**
* Transforms the timesheet description content into HTML.
*

View File

@@ -44,6 +44,7 @@ class RuntimeExtensions extends AbstractExtension
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']]),
new TwigFilter('comment1line', [MarkdownExtension::class, 'commentOneLiner'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
];
}
}

View File

@@ -44,6 +44,9 @@
"doctrine/dbal": {
"version": "v2.6.3"
},
"doctrine/deprecations": {
"version": "v0.5.3"
},
"doctrine/doctrine-bundle": {
"version": "1.6",
"recipe": {

View File

@@ -60,7 +60,7 @@
{% endif %}
{% endif %}
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment2html }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment1line }}</td>
{% for field in metaColumns %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'mf_' ~ field.name) }}">
{{ tables.datatable_meta_column(entry, field) }}

View File

@@ -37,7 +37,7 @@
{% for project in projects %}
<tr class="{{ widgets.class_project_row(project, now) }}">
<td>{{ widgets.label_project(project) }}</td>
<td class="hidden-xs">{{ project.comment|comment2html }}</td>
<td class="hidden-xs">{{ project.comment|comment1line(false) }}</td>
<td class="w-min text-center">{% if project.end is not null %}{{ project.end|date_short }}{% endif %}</td>
<td class="w-min text-center">{{ widgets.label_visible(project.visible) }}</td>
<td class="w-min text-center">{{ widgets.badge_team_access(project.teams) }}</td>

View File

@@ -59,7 +59,7 @@
{% endif %}
<tr class="{{ class }}" data-href="{{ dataHref }}">
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_color_dot('customer', true, entry.name, null, entry.color) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment2html }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment1line }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'number') }}">{{ entry.number }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'company') }}">{{ entry.company }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'vat_id') }}">{{ entry.vatId }}</td>

View File

@@ -33,7 +33,7 @@
{% for activity in activities %}
<tr class="{{ widgets.class_activity_row(activity, now) }}">
<td>{{ widgets.label_activity(activity) }}</td>
<td class="hidden-xs">{{ activity.comment|comment2html }}</td>
<td class="hidden-xs">{{ activity.comment|comment1line(false) }}</td>
<td class="w-min text-center">{{ widgets.label_visible(activity.visible) }}</td>
<td class="w-min text-center">{{ widgets.badge_team_access(activity.teams) }}</td>
<td class="actions">{{ actions.activity(activity, 'custom') }}</td>

View File

@@ -53,7 +53,7 @@
<tr class="{{ class }}" data-href="{{ dataHref }}">
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_color_dot('project', true, entry.name, null, entry.color) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'customer') }}">{{ widgets.label_customer(entry.customer) }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment2html }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ entry.comment|comment1line }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'orderNumber') }}">{{ entry.orderNumber }}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'orderDate') }}">{% if entry.orderDate is not null %}{{ entry.orderDate|date_full }}{% endif %}</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'project_start') }}">{% if entry.start is not null %}{{ entry.start|date_full }}{% endif %}</td>

View File

@@ -45,6 +45,7 @@
}) %}
{% endfor %}
{% set columns = columns|merge({
'exported': {'class': 'text-center hidden w-min', 'orderBy': false},
'actions': {'class': 'actions alwaysVisible'},
}) %}
@@ -162,6 +163,9 @@
{{ tables.datatable_meta_column(entry, field) }}
</td>
{% endfor %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'exported') }}">
{{ widgets.label_boolean(entry.exported) }}
</td>
<td class="actions">
{{ block('row_action') }}
</td>

View File

@@ -60,21 +60,8 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
return $client;
}
/**
* @param string $url
* @param bool $json
* @return string
*/
protected function createUrl($url, $json = true)
protected function createUrl(string $url): string
{
if ($json) {
if (stripos($url, '?') !== false) {
$url = str_replace('?', '.json?', $url);
} else {
$url .= '.json';
}
}
return '/' . ltrim($url, '/');
}

View File

@@ -26,7 +26,7 @@ class ApiDocControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/doc');
$this->assertStringContainsString('<title>Kimai 2 - API Docs</title>', $client->getResponse()->getContent());
$this->assertStringContainsString('<title>Kimai - API Docs</title>', $client->getResponse()->getContent());
$result = $client->getCrawler()->filter('script#swagger-data');
$swaggerJson = json_decode($result->text(), true);
$tags = [];
@@ -51,17 +51,13 @@ class ApiDocControllerTest extends ControllerBaseTest
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
$this->assertAccessIsGranted($client, '/api/doc.json');
$this->assertStringContainsString('"title":"Kimai 2 - API Docs"', $client->getResponse()->getContent());
$this->assertStringContainsString('"title":"Kimai - API Docs"', $client->getResponse()->getContent());
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
}
/**
* @param string $url
* @return string
*/
protected function createUrl($url)
protected function createUrl(string $url): string
{
return '/' . ltrim($url, '/');
}

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\API;
use App\API\NotFoundException;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\API\NotFoundException
*/
class NotFoundExceptionTest extends TestCase
{
public function testConstructor()
{
$sut = new NotFoundException();
self::assertEquals('Not found', $sut->getMessage());
self::assertEquals(404, $sut->getCode());
}
}

View File

@@ -79,11 +79,7 @@ abstract class ControllerBaseTest extends WebTestCase
return $client;
}
/**
* @param string $url
* @return string
*/
protected function createUrl($url)
protected function createUrl(string $url): string
{
return '/' . self::DEFAULT_LANGUAGE . '/' . ltrim($url, '/');
}

View File

@@ -0,0 +1,62 @@
<?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\EventSubscriber;
use App\EventSubscriber\PagerfantaExceptionSubscriber;
use Pagerfanta\Exception\NotValidMaxPerPageException;
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* @covers \App\EventSubscriber\PagerfantaExceptionSubscriber
*/
class PagerfantaExceptionSubscriberTest extends TestCase
{
public function testGetSubscribedEvents()
{
$events = PagerfantaExceptionSubscriber::getSubscribedEvents();
$this->assertArrayHasKey(KernelEvents::EXCEPTION, $events);
$methodName = $events[KernelEvents::EXCEPTION][0];
$this->assertTrue(method_exists(PagerfantaExceptionSubscriber::class, $methodName));
}
public function testWithExceptions()
{
$sut = new PagerfantaExceptionSubscriber();
$kernel = $this->createMock(HttpKernelInterface::class);
$request = $this->createMock(Request::class);
$exception = new \Exception();
$requestType = HttpKernelInterface::MASTER_REQUEST;
$event = new ExceptionEvent($kernel, $request, $requestType, $exception);
$sut->onCoreException($event);
self::assertSame($exception, $event->getThrowable());
$event = new ExceptionEvent($kernel, $request, $requestType, new NotValidMaxPerPageException('Foo baaaaar!', 999));
$sut->onCoreException($event);
self::assertInstanceOf(NotFoundHttpException::class, $event->getThrowable());
self::assertEquals('Foo baaaaar!', $event->getThrowable()->getMessage());
self::assertEquals(999, $event->getThrowable()->getCode());
self::assertEquals(404, $event->getThrowable()->getStatusCode());
$event = new ExceptionEvent($kernel, $request, $requestType, new OutOfRangeCurrentPageException('Trölölölölölö', 123));
$sut->onCoreException($event);
self::assertInstanceOf(NotFoundHttpException::class, $event->getThrowable());
self::assertEquals('Trölölölölölö', $event->getThrowable()->getMessage());
self::assertEquals(123, $event->getThrowable()->getCode());
self::assertEquals(404, $event->getThrowable()->getStatusCode());
}
}

View File

@@ -77,4 +77,42 @@ class MarkdownExtensionTest extends TestCase
$sut->commentContent("- test\n- foo\n\nfoo __bar__")
);
}
public function testCommentOneLiner()
{
$loader = $this->createMock(ConfigLoaderInterface::class);
$config = new SystemConfiguration($loader, []);
$sut = new MarkdownExtension(new Markdown(), $config);
$loremIpsum = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
$this->assertEquals('', $sut->commentOneLiner(null));
$this->assertEquals('', $sut->commentOneLiner(''));
$this->assertEquals('', $sut->commentOneLiner(null, false));
$this->assertEquals('', $sut->commentOneLiner('', true));
$this->assertEquals(
'Lorem ipsum dolor sit amet, consetetur sadipscing &hellip;',
$sut->commentOneLiner($loremIpsum, false)
);
$this->assertEquals(
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. &hellip;',
$sut->commentOneLiner(implode(PHP_EOL, [$loremIpsum, $loremIpsum, $loremIpsum]), true)
);
$this->assertEquals(
'Lorem ipsum dolor sit amet, consetetur sadipscing &hellip;',
$sut->commentOneLiner(implode(PHP_EOL, [$loremIpsum, $loremIpsum, $loremIpsum]), false)
);
$this->assertEquals(
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt',
$sut->commentOneLiner(implode(PHP_EOL, ['Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt']), true)
);
$this->assertEquals(
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt &hellip;',
$sut->commentOneLiner(implode(PHP_EOL, ['Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt', 'ssdf']), true)
);
}
}

View File

@@ -22,7 +22,7 @@ class RuntimeExtensionsTest extends TestCase
{
public function testGetFilters()
{
$expected = ['md2html', 'desc2html', 'comment2html'];
$expected = ['md2html', 'desc2html', 'comment2html', 'comment1line'];
$i = 0;
$sut = new RuntimeExtensions();
@@ -67,6 +67,7 @@ class RuntimeExtensionsTest extends TestCase
$found_md2html = false;
$found_desc2html = false;
$found_comment2html = false;
$found_comment1line = false;
foreach ($filters as $filter) {
switch ($filter->getName()) {
@@ -85,11 +86,17 @@ class RuntimeExtensionsTest extends TestCase
self::assertEquals(['html'], $filters[2]->getSafe(new Node()));
$found_comment2html = true;
break;
case 'comment1line':
self::assertEquals('html', $filters[3]->getPreEscape());
self::assertEquals(['html'], $filters[3]->getSafe(new Node()));
$found_comment1line = true;
break;
}
}
self::assertTrue($found_md2html, 'Missing filter: md2html');
self::assertTrue($found_desc2html, 'Missing filter: desc2html');
self::assertTrue($found_comment2html, 'Missing filter: comment2html');
self::assertTrue($found_comment1line, 'Missing filter: comment1line');
}
}