added annotation based exporter (#1831)

* added user exporter
* added project exporter
* added customer exporter
* added activity exporter
This commit is contained in:
Kevin Papst
2020-07-21 03:37:51 +02:00
committed by GitHub
parent 93a23f3fa3
commit aee063bb3c
79 changed files with 2980 additions and 215 deletions

View File

@@ -0,0 +1,56 @@
<?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\Export\Spreadsheet;
use App\Entity\Customer;
use App\Entity\Project;
use App\Export\Spreadsheet\AnnotatedObjectExporter;
use App\Export\Spreadsheet\Extractor\AnnotationExtractor;
use App\Export\Spreadsheet\SpreadsheetExporter;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Spreadsheet\AnnotatedObjectExporter
*/
class AnnotatedObjectExporterTest extends TestCase
{
public function testExport()
{
$spreadsheetExporter = new SpreadsheetExporter($this->createMock(TranslatorInterface::class));
$annotationExtractor = new AnnotationExtractor(new AnnotationReader());
$project = new Project();
$project->setName('test project');
$project->setCustomer((new Customer())->setName('A customer'));
$project->setComment('Lorem Ipsum');
$project->setOrderNumber('1234567890');
$project->setBudget(123456.7890);
$project->setTimeBudget(1234567890);
$project->setColor('#ababab');
$project->setVisible(false);
$sut = new AnnotatedObjectExporter($spreadsheetExporter, $annotationExtractor);
$spreadsheet = $sut->export(Project::class, [$project]);
$worksheet = $spreadsheet->getActiveSheet();
self::assertNull($worksheet->getCellByColumnAndRow(1, 2, false)->getValue());
self::assertEquals('test project', $worksheet->getCellByColumnAndRow(2, 2, false)->getValue());
self::assertEquals('A customer', $worksheet->getCellByColumnAndRow(3, 2, false)->getValue());
self::assertEquals(1234567890, $worksheet->getCellByColumnAndRow(4, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(5, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(6, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(7, 2, false)->getValue());
self::assertEquals('#ababab', $worksheet->getCellByColumnAndRow(8, 2, false)->getValue());
self::assertFalse($worksheet->getCellByColumnAndRow(9, 2, false)->getValue());
self::assertEquals('Lorem Ipsum', $worksheet->getCellByColumnAndRow(10, 2, false)->getValue());
}
}

View File

@@ -0,0 +1,64 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Style;
use PHPUnit\Framework\TestCase;
abstract class AbstractFormatterTest extends TestCase
{
abstract protected function getFormatter(): CellFormatterInterface;
abstract protected function getActualValue();
abstract protected function getExpectedValue();
protected function assertCellStyle(Style $style)
{
}
protected function assertCellValue(Cell $cell)
{
self::assertEquals($this->getExpectedValue(), $cell->getValue());
}
public function testSetFormattedValue()
{
$sut = $this->getFormatter();
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut->setFormattedValue($worksheet, 1, 1, $this->getActualValue());
$cell = $worksheet->getCellByColumnAndRow(1, 1, false);
$this->assertCellValue($cell);
$this->assertCellStyle($worksheet->getStyleByColumnAndRow(1, 1));
}
public function testSetNull()
{
$sut = $this->getFormatter();
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut->setFormattedValue($worksheet, 1, 1, null);
$cell = $worksheet->getCellByColumnAndRow(1, 1, false);
$this->assertNullValue($cell);
}
protected function assertNullValue(Cell $cell)
{
self::assertEquals('', $cell->getValue());
}
}

View File

@@ -0,0 +1,47 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\ArrayFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\ArrayFormatter
*/
class ArrayFormatterTest extends AbstractFormatterTest
{
protected function getFormatter(): CellFormatterInterface
{
return new ArrayFormatter();
}
protected function getActualValue()
{
return ['test', 'foo', 'bar'];
}
protected function getExpectedValue()
{
return 'test;foo;bar';
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only array is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
}

View File

@@ -0,0 +1,47 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\BooleanFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\BooleanFormatter
*/
class BooleanFormatterTest extends AbstractFormatterTest
{
protected function getFormatter(): CellFormatterInterface
{
return new BooleanFormatter();
}
protected function getActualValue()
{
return false;
}
protected function getExpectedValue()
{
return false;
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only boolean is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
}

View File

@@ -0,0 +1,57 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use App\Export\Spreadsheet\CellFormatter\DateFormatter;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Style;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\DateFormatter
*/
class DateFormatterTest extends AbstractFormatterTest
{
private $date;
protected function getFormatter(): CellFormatterInterface
{
return new DateFormatter();
}
protected function getActualValue()
{
return $this->date = new \DateTime();
}
protected function getExpectedValue()
{
return Date::PHPToExcel($this->date);
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
protected function assertCellStyle(Style $style)
{
self::assertEquals(NumberFormat::FORMAT_DATE_YYYYMMDD2, $style->getNumberFormat()->getFormatCode());
}
}

View File

@@ -0,0 +1,56 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use App\Export\Spreadsheet\CellFormatter\DateTimeFormatter;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Style;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\DateTimeFormatter
*/
class DateTimeFormatterTest extends AbstractFormatterTest
{
private $date;
protected function getFormatter(): CellFormatterInterface
{
return new DateTimeFormatter();
}
protected function getActualValue()
{
return $this->date = new \DateTime();
}
protected function getExpectedValue()
{
return Date::PHPToExcel($this->date);
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
protected function assertCellStyle(Style $style)
{
self::assertEquals(DateTimeFormatter::DATETIME_FORMAT, $style->getNumberFormat()->getFormatCode());
}
}

View File

@@ -0,0 +1,59 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use App\Export\Spreadsheet\CellFormatter\DurationFormatter;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Style;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\DurationFormatter
*/
class DurationFormatterTest extends AbstractFormatterTest
{
protected function getFormatter(): CellFormatterInterface
{
return new DurationFormatter();
}
protected function getActualValue()
{
return 3600;
}
protected function getExpectedValue()
{
return '=3600/86400';
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only int is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
protected function assertNullValue(Cell $cell)
{
self::assertEquals('=0/86400', $cell->getValue());
}
protected function assertCellStyle(Style $style)
{
self::assertEquals(DurationFormatter::DURATION_FORMAT, $style->getNumberFormat()->getFormatCode());
}
}

View File

@@ -0,0 +1,56 @@
<?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\Export\Spreadsheet\CellFormatter;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use App\Export\Spreadsheet\CellFormatter\TimeFormatter;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Style;
/**
* @covers \App\Export\Spreadsheet\CellFormatter\TimeFormatter
*/
class TimeFormatterTest extends AbstractFormatterTest
{
private $date;
protected function getFormatter(): CellFormatterInterface
{
return new TimeFormatter();
}
protected function getActualValue()
{
return $this->date = new \DateTime();
}
protected function getExpectedValue()
{
return Date::PHPToExcel($this->date);
}
public function testFormattedValueWithInvalidValue()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported value given, only DateTime is supported');
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$sut = $this->getFormatter();
$sut->setFormattedValue($worksheet, 1, 1, 'sdfsdf');
}
protected function assertCellStyle(Style $style)
{
self::assertEquals(TimeFormatter::TIME_FORMAT, $style->getNumberFormat()->getFormatCode());
}
}

View File

@@ -0,0 +1,30 @@
<?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\Export\Spreadsheet;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Export\Spreadsheet\ColumnDefinition
*/
class ColumnDefinitionTest extends TestCase
{
public function testConstruct()
{
$sut = new \App\Export\Spreadsheet\ColumnDefinition('foo', 'bar', function () {
return 'hello world';
});
self::assertEquals('foo', $sut->getLabel());
self::assertEquals('bar', $sut->getType());
self::assertIsCallable($sut->getAccessor());
self::assertEquals('hello world', \call_user_func($sut->getAccessor()));
}
}

View File

@@ -0,0 +1,76 @@
<?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\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
/**
* @Exporter\Order({"a-time", "publicProperty", "a-date", "something", "privateProperty"})
* @Exporter\Expose("accessor", label="label.accessor", exp="object.accessorMethod()")
* @Exporter\Expose("a-date", label="label.type-date", exp="object.getDateTime()", type="date")
* @Exporter\Expose("a-time", label="label.type-time", exp="object.getDateTime()", type="time")
*/
class DemoFull
{
/**
* @Exporter\Expose(label="label.Public-Property", type="string")
*/
public $publicProperty = 'public-property';
/**
* @Exporter\Expose("fake-name", label="label.Protected-Property", type="boolean")
*/
protected $protectedProperty = false;
/**
* @Exporter\Expose(label="label.Private-Property", type="integer")
*/
private $privateProperty = 123;
/**
* @Exporter\Expose(label="label.Public-Method")
*/
public function publicMethod(): string
{
return 'public-method';
}
/**
* @Exporter\Expose(label="label.Protected-Method", type="datetime")
*/
protected function protectedMethod(): \DateTime
{
return new \DateTime();
}
public function getDateTime(): \DateTime
{
return new \DateTime();
}
/**
* @Exporter\Expose("renamedDuration", label="label.duration", type="duration")
*/
protected function duration(): int
{
return 12345;
}
/**
* @Exporter\Expose(name="fake-method", label="label.Private-Method", type="boolean")
*/
private function privateMethod(): bool
{
return true;
}
public function accessorMethod(): string
{
return 'accessor-method';
}
}

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\Tests\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
class ExpressionOnMethod
{
/**
* @Exporter\Expose("accessor", label="label.accessor", exp="object.foo")
*/
public function foo()
{
}
}

View File

@@ -0,0 +1,20 @@
<?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\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
class ExpressionOnProperty
{
/**
* @Exporter\Expose("accessor", label="label.accessor", exp="object.foo")
*/
private $foo;
}

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\Tests\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
class MethodRequiresParams
{
/**
* @Exporter\Expose("accessor", label="label.accessor")
*/
public function foo(string $foo)
{
}
}

View File

@@ -0,0 +1,19 @@
<?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\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
/**
* @Exporter\Expose("accessor", label="label.accessor")
*/
class MissingExpressionOnClass
{
}

View File

@@ -0,0 +1,19 @@
<?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\Export\Spreadsheet\Entities;
use App\Export\Annotation as Exporter;
/**
* @Exporter\Expose(label="label.accessor", exp="foo")
*/
class MissingNameOnClass
{
}

View File

@@ -0,0 +1,61 @@
<?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\Export\Spreadsheet;
use App\Entity\Customer;
use App\Entity\Project;
use App\Event\ProjectMetaDisplayEvent;
use App\Export\Spreadsheet\EntityWithMetaFieldsExporter;
use App\Export\Spreadsheet\Extractor\AnnotationExtractor;
use App\Export\Spreadsheet\Extractor\MetaFieldExtractor;
use App\Export\Spreadsheet\SpreadsheetExporter;
use App\Repository\Query\ProjectQuery;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Spreadsheet\EntityWithMetaFieldsExporter
*/
class EntityWithMetaFieldsExporterTest extends TestCase
{
public function testExport()
{
$spreadsheetExporter = new SpreadsheetExporter($this->createMock(TranslatorInterface::class));
$annotationExtractor = new AnnotationExtractor(new AnnotationReader());
$metaFieldExtractor = new MetaFieldExtractor($this->createMock(EventDispatcherInterface::class));
$project = new Project();
$project->setName('test project');
$project->setCustomer((new Customer())->setName('A customer'));
$project->setComment('Lorem Ipsum');
$project->setOrderNumber('1234567890');
$project->setBudget(123456.7890);
$project->setTimeBudget(1234567890);
$project->setColor('#ababab');
$project->setVisible(false);
$sut = new EntityWithMetaFieldsExporter($spreadsheetExporter, $annotationExtractor, $metaFieldExtractor);
$spreadsheet = $sut->export(Project::class, [$project], new ProjectMetaDisplayEvent(new ProjectQuery(), ProjectMetaDisplayEvent::EXPORT));
$worksheet = $spreadsheet->getActiveSheet();
self::assertNull($worksheet->getCellByColumnAndRow(1, 2, false)->getValue());
self::assertEquals('test project', $worksheet->getCellByColumnAndRow(2, 2, false)->getValue());
self::assertEquals('A customer', $worksheet->getCellByColumnAndRow(3, 2, false)->getValue());
self::assertEquals(1234567890, $worksheet->getCellByColumnAndRow(4, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(5, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(6, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(7, 2, false)->getValue());
self::assertEquals('#ababab', $worksheet->getCellByColumnAndRow(8, 2, false)->getValue());
self::assertFalse($worksheet->getCellByColumnAndRow(9, 2, false)->getValue());
self::assertEquals('Lorem Ipsum', $worksheet->getCellByColumnAndRow(10, 2, false)->getValue());
}
}

View File

@@ -0,0 +1,132 @@
<?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\Export\Spreadsheet\Extractor;
use App\Export\Spreadsheet\ColumnDefinition;
use App\Export\Spreadsheet\Extractor\AnnotationExtractor;
use App\Export\Spreadsheet\Extractor\ExtractorException;
use App\Tests\Export\Spreadsheet\Entities\DemoFull;
use App\Tests\Export\Spreadsheet\Entities\ExpressionOnMethod;
use App\Tests\Export\Spreadsheet\Entities\ExpressionOnProperty;
use App\Tests\Export\Spreadsheet\Entities\MethodRequiresParams;
use App\Tests\Export\Spreadsheet\Entities\MissingExpressionOnClass;
use App\Tests\Export\Spreadsheet\Entities\MissingNameOnClass;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Export\Spreadsheet\Extractor\AnnotationExtractor
* @covers \App\Export\Annotation\Expose
* @covers \App\Export\Annotation\Order
* @covers \App\Export\Spreadsheet\Extractor\ExtractorException
*/
class AnnotationExtractorTest extends TestCase
{
public function testExtract()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$columns = $sut->extract(DemoFull::class);
self::assertIsArray($columns);
self::assertCount(10, $columns);
foreach ($columns as $column) {
self::assertInstanceOf(ColumnDefinition::class, $column);
}
$expected = [
['label.type-time', 'time', new \DateTime()],
['label.Public-Property', 'string', 'public-property'],
['label.type-date', 'date', new \DateTime()],
['label.Private-Property', 'integer', 123],
['label.accessor', 'string', 'accessor-method'],
['label.Protected-Property', 'boolean', false],
['label.Public-Method', 'string', 'public-method'],
['label.Protected-Method', 'datetime', new \DateTime()],
['label.duration', 'duration', 12345],
['label.Private-Method', 'boolean', true],
];
$i = 0;
$object = new DemoFull();
foreach ($expected as $item) {
$column = $columns[$i++];
self::assertEquals($item[0], $column->getLabel());
self::assertEquals($item[1], $column->getType());
$result = \call_user_func($column->getAccessor(), $object);
self::assertEquals(get_debug_type($item[2]), get_debug_type($result));
if (\in_array(get_debug_type($result), ['string', 'int', 'bool', 'float'])) {
self::assertEquals($item[2], $result);
}
}
}
public function testExceptionOnInvalidType()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('AnnotationExtractor needs a class name (string) for work');
$sut->extract(new \stdClass());
}
public function testExceptionOnMissingExpression()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('@Expose needs an expression attribute on class level hierarchy, check App\Tests\Export\Spreadsheet\Entities\MissingExpressionOnClass::class');
$sut->extract(MissingExpressionOnClass::class);
}
public function testExceptionOnMissingName()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('@Expose needs a name attribute on class level hierarchy, check App\Tests\Export\Spreadsheet\Entities\MissingNameOnClass::class');
$sut->extract(MissingNameOnClass::class);
}
public function testExceptionExpressionOnProperty()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('@Expose only supports the expression attribute on class level hierarchy, check App\Tests\Export\Spreadsheet\Entities\ExpressionOnProperty::$foo');
$sut->extract(ExpressionOnProperty::class);
}
public function testExceptionExpressionOnMethod()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('@Expose only supports the expression attribute on class level hierarchy, check App\Tests\Export\Spreadsheet\Entities\ExpressionOnMethod::foo()');
$sut->extract(ExpressionOnMethod::class);
}
public function testExceptionExpressionOnMethodWithRequiredParameters()
{
$sut = new AnnotationExtractor(new AnnotationReader());
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('@Expose does not support method App\Tests\Export\Spreadsheet\Entities\MethodRequiresParams::foo(...), it has required parameters.');
$sut->extract(MethodRequiresParams::class);
}
}

View File

@@ -0,0 +1,66 @@
<?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\Export\Spreadsheet\Extractor;
use App\Entity\Project;
use App\Entity\ProjectMeta;
use App\Event\ProjectMetaDisplayEvent;
use App\Export\Spreadsheet\ColumnDefinition;
use App\Export\Spreadsheet\Extractor\ExtractorException;
use App\Export\Spreadsheet\Extractor\MetaFieldExtractor;
use App\Repository\Query\ProjectQuery;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* @covers \App\Export\Spreadsheet\Extractor\MetaFieldExtractor
* @covers \App\Export\Spreadsheet\Extractor\ExtractorException
*/
class MetaFieldExtractorTest extends TestCase
{
public function testExtract()
{
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$dispatcher->expects(self::once())->method('dispatch')->willReturnCallback(function (ProjectMetaDisplayEvent $event) {
$event->addField((new ProjectMeta())->setName('foo')->setIsVisible(true));
$event->addField((new ProjectMeta())->setName('no')->setIsVisible(false));
$event->addField((new ProjectMeta())->setName('bar')->setIsVisible(true));
});
$sut = new MetaFieldExtractor($dispatcher);
$event = new ProjectMetaDisplayEvent(new ProjectQuery(), 'somewhere');
$columns = $sut->extract($event);
self::assertIsArray($columns);
self::assertCount(2, $columns);
foreach ($columns as $column) {
self::assertInstanceOf(ColumnDefinition::class, $column);
}
$definition = $columns[1];
self::assertEquals('bar', $definition->getLabel());
self::assertEquals('string', $definition->getType());
self::assertEquals('tralalalala', \call_user_func($definition->getAccessor(), (new Project())->setMetaField((new ProjectMeta())->setName('bar')->setValue('tralalalala'))));
}
public function testCheckType()
{
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$sut = new MetaFieldExtractor($dispatcher);
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('MetaFieldExtractor needs a MetaDisplayEventInterface instance for work');
$sut->extract(new \stdClass());
}
}

View File

@@ -0,0 +1,65 @@
<?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\Export\Spreadsheet\Extractor;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Event\UserPreferenceDisplayEvent;
use App\Export\Spreadsheet\ColumnDefinition;
use App\Export\Spreadsheet\Extractor\ExtractorException;
use App\Export\Spreadsheet\Extractor\UserPreferenceExtractor;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* @covers \App\Export\Spreadsheet\Extractor\UserPreferenceExtractor
* @covers \App\Export\Spreadsheet\Extractor\ExtractorException
*/
class UserPreferenceExtractorTest extends TestCase
{
public function testExtract()
{
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$dispatcher->expects(self::once())->method('dispatch')->willReturnCallback(function (UserPreferenceDisplayEvent $event) {
$event->addPreference((new UserPreference())->setName('foo')->setEnabled(true));
$event->addPreference((new UserPreference())->setName('no')->setEnabled(false));
$event->addPreference((new UserPreference())->setName('bar')->setEnabled(true));
});
$sut = new UserPreferenceExtractor($dispatcher);
$event = new UserPreferenceDisplayEvent('somewhere');
$columns = $sut->extract($event);
self::assertIsArray($columns);
self::assertCount(2, $columns);
foreach ($columns as $column) {
self::assertInstanceOf(ColumnDefinition::class, $column);
}
$definition = $columns[1];
self::assertEquals('bar', $definition->getLabel());
self::assertEquals('string', $definition->getType());
self::assertEquals('tralalalala', \call_user_func($definition->getAccessor(), (new User())->addPreference((new UserPreference())->setName('bar')->setValue('tralalalala'))));
}
public function testCheckType()
{
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$sut = new UserPreferenceExtractor($dispatcher);
$this->expectException(ExtractorException::class);
$this->expectExceptionMessage('UserPreferenceExtractor needs a UserPreferenceDisplayEvent instance for work');
$sut->extract(new \stdClass());
}
}

View File

@@ -0,0 +1,69 @@
<?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\Export\Spreadsheet;
use App\Entity\Project;
use App\Export\Spreadsheet\CellFormatter\CellFormatterInterface;
use App\Export\Spreadsheet\ColumnDefinition;
use App\Export\Spreadsheet\SpreadsheetExporter;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Spreadsheet\SpreadsheetExporter
*/
class SpreadsheetExporterTest extends TestCase
{
public function testExport()
{
$sut = new SpreadsheetExporter($this->createMock(TranslatorInterface::class));
$sut->registerCellFormatter('foo', new class() implements CellFormatterInterface {
public function setFormattedValue(Worksheet $sheet, int $column, int $row, $value): void
{
$sheet->setCellValueByColumnAndRow($column, $row, '##' . $value . '##');
}
});
$sut->registerCellFormatter('bar', new class() implements CellFormatterInterface {
public function setFormattedValue(Worksheet $sheet, int $column, int $row, $value): void
{
$sheet->setCellValueByColumnAndRow($column, $row, '~' . $value . '~');
}
});
$project = new Project();
$project->setName('test project');
$project->setVisible(false);
$columns = [
new ColumnDefinition('test1', 'foo', function (Project $project) {
return $project->getName();
}),
new ColumnDefinition('test2', 'bar', function (Project $project) {
return $project->getName();
}),
new ColumnDefinition('test3', 'boolean', function (Project $project) {
return $project->isVisible();
}),
];
$entries = [
$project
];
$spreadsheet = $sut->export($columns, $entries);
$worksheet = $spreadsheet->getActiveSheet();
self::assertEquals('##test project##', $worksheet->getCellByColumnAndRow(1, 2, false)->getValue());
self::assertEquals('~test project~', $worksheet->getCellByColumnAndRow(2, 2, false)->getValue());
self::assertFalse($worksheet->getCellByColumnAndRow(3, 2, false)->getValue());
}
}

View File

@@ -0,0 +1,60 @@
<?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\Export\Spreadsheet;
use App\Entity\User;
use App\Event\UserPreferenceDisplayEvent;
use App\Export\Spreadsheet\Extractor\AnnotationExtractor;
use App\Export\Spreadsheet\Extractor\UserPreferenceExtractor;
use App\Export\Spreadsheet\SpreadsheetExporter;
use App\Export\Spreadsheet\UserExporter;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Spreadsheet\UserExporter
*/
class UserExporterTest extends TestCase
{
public function testExport()
{
$spreadsheetExporter = new SpreadsheetExporter($this->createMock(TranslatorInterface::class));
$annotationExtractor = new AnnotationExtractor(new AnnotationReader());
$userPreferenceExtractor = new UserPreferenceExtractor($this->createMock(EventDispatcherInterface::class));
$user = new User();
$user->setUsername('test user');
$user->setAvatar('Lorem Ipsum');
$user->setTimezone('Europe/Berlin');
$user->setAlias('Another name');
$user->setTitle('Mr. Title');
$user->setLanguage('de');
$user->setEmail('test@example.com');
$user->setEnabled(false);
$user->addRole(User::ROLE_TEAMLEAD);
$sut = new UserExporter($spreadsheetExporter, $annotationExtractor, $userPreferenceExtractor);
$spreadsheet = $sut->export([$user], new UserPreferenceDisplayEvent(UserPreferenceDisplayEvent::EXPORT));
$worksheet = $spreadsheet->getActiveSheet();
self::assertNull($worksheet->getCellByColumnAndRow(1, 2, false)->getValue());
self::assertEquals('test user', $worksheet->getCellByColumnAndRow(2, 2, false)->getValue());
self::assertEquals('Another name', $worksheet->getCellByColumnAndRow(3, 2, false)->getValue());
self::assertEquals('Mr. Title', $worksheet->getCellByColumnAndRow(4, 2, false)->getValue());
self::assertEquals('test@example.com', $worksheet->getCellByColumnAndRow(5, 2, false)->getValue());
self::assertEquals('', $worksheet->getCellByColumnAndRow(6, 2, false)->getValue());
self::assertEquals('de', $worksheet->getCellByColumnAndRow(7, 2, false)->getValue());
self::assertEquals('Europe/Berlin', $worksheet->getCellByColumnAndRow(8, 2, false)->getValue());
self::assertFalse($worksheet->getCellByColumnAndRow(9, 2, false)->getValue());
self::assertEquals('ROLE_TEAMLEAD;ROLE_USER', $worksheet->getCellByColumnAndRow(11, 2, false)->getValue());
}
}

View File

@@ -0,0 +1,57 @@
<?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\Export\Spreadsheet\Writer;
use App\Export\Spreadsheet\Writer\BinaryFileResponseWriter;
use App\Export\Spreadsheet\Writer\XlsxWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Export\Spreadsheet\Writer\BinaryFileResponseWriter
*/
class BinaryFileResponseWriterTest extends TestCase
{
public function testSave()
{
$sut = new BinaryFileResponseWriter(new XlsxWriter(), 'foobar');
self::assertEquals('xlsx', $sut->getFileExtension());
self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $sut->getContentType());
$spreadsheet = new Spreadsheet();
$file = $sut->save($spreadsheet);
self::assertInstanceOf(\SplFileInfo::class, $file);
self::assertTrue(file_exists($file->getRealPath()));
}
public function testGetResponse()
{
$sut = new BinaryFileResponseWriter(new XlsxWriter(), 'foobar');
$spreadsheet = new Spreadsheet();
$response = $sut->getFileResponse($spreadsheet);
self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $response->headers->get('Content-Type'));
self::assertStringContainsString('attachment; filename=foobar', $response->headers->get('Content-Disposition'));
$file = $response->getFile();
self::assertTrue(file_exists($file->getRealPath()));
ob_start();
$response->sendContent();
$content2 = ob_get_clean();
self::assertNotEmpty($content2);
self::assertFalse(file_exists($file->getRealPath()));
}
}

View File

@@ -0,0 +1,37 @@
<?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\Export\Spreadsheet\Writer;
use App\Export\Spreadsheet\Writer\XlsxWriter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Export\Spreadsheet\Writer\XlsxWriter
*/
class XlsxWriterTest extends TestCase
{
public function testWriter()
{
$sut = new XlsxWriter();
self::assertEquals('xlsx', $sut->getFileExtension());
self::assertEquals('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', $sut->getContentType());
$spreadsheet = new Spreadsheet();
$file = $sut->save($spreadsheet);
self::assertInstanceOf(\SplFileInfo::class, $file);
self::assertTrue(file_exists($file->getRealPath()));
// TODO test autofilter
// TODO test freeze pane
}
}