added invoice archive & configurable invoice numbers (#1541)
This commit is contained in:
@@ -321,12 +321,12 @@ abstract class ControllerBaseTest extends WebTestCase
|
||||
*/
|
||||
protected function assertIsRedirect(HttpKernelBrowser $client, $url = null)
|
||||
{
|
||||
self::assertTrue($client->getResponse()->isRedirect());
|
||||
self::assertTrue($client->getResponse()->isRedirect(), 'Response is not a redirect');
|
||||
if (null === $url) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::assertTrue($client->getResponse()->headers->has('Location'));
|
||||
self::assertStringEndsWith($url, $client->getResponse()->headers->get('Location'));
|
||||
self::assertTrue($client->getResponse()->headers->has('Location'), 'Could not find "Location" header');
|
||||
self::assertStringEndsWith($url, $client->getResponse()->headers->get('Location'), 'Redirect URL does not match');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,44 @@ use App\Form\Type\DateRangeType;
|
||||
use App\Tests\DataFixtures\InvoiceFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class InvoiceControllerTest extends ControllerBaseTest
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$path = __DIR__ . '/../_data/invoices/';
|
||||
|
||||
if (is_dir($path)) {
|
||||
$files = glob($path . '*');
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
$this->clearInvoiceFiles();
|
||||
}
|
||||
|
||||
private function clearInvoiceFiles()
|
||||
{
|
||||
$path = __DIR__ . '/../_data/invoices/';
|
||||
|
||||
if (is_dir($path)) {
|
||||
$files = glob($path . '*');
|
||||
foreach ($files as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/invoice/');
|
||||
@@ -76,7 +108,6 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
'company' => 'Company name',
|
||||
'renderer' => 'default',
|
||||
'calculator' => 'default',
|
||||
'numberGenerator' => 'default',
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -111,7 +142,6 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
$this->assertEquals($template->getCompany(), $values['company']);
|
||||
$this->assertEquals($template->getAddress(), $values['address']);
|
||||
$this->assertEquals($template->getPaymentTerms(), $values['paymentTerms']);
|
||||
$this->assertEquals($template->getNumberGenerator(), $values['numberGenerator']);
|
||||
}
|
||||
|
||||
public function testPrintAction()
|
||||
@@ -153,8 +183,8 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
// no warning should be displayed
|
||||
$node = $client->getCrawler()->filter('div.callout.callout-warning.lead');
|
||||
$this->assertEquals(0, $node->count());
|
||||
// but the datatable with all timesheets
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice', 20);
|
||||
// but the datatable with all timesheets + 1 row for the total
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice', 21);
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
@@ -180,6 +210,92 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function testPrintActionAsAdminWithDownloadAndStatusChange()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
/** @var EntityManager $em */
|
||||
$em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager');
|
||||
|
||||
$fixture = new InvoiceFixtures();
|
||||
$this->importFixture($client, $fixture);
|
||||
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$end = new \DateTime('last day of this month');
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
->setUser($this->getUserByRole($em, User::ROLE_ADMIN))
|
||||
->setAmount(20)
|
||||
->setStartDate($begin)
|
||||
;
|
||||
$this->importFixture($client, $fixture);
|
||||
|
||||
$this->request($client, '/invoice/');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$dateRange = $begin->format('Y-m-d') . DateRangeType::DATE_SPACER . $end->format('Y-m-d');
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/?preview='));
|
||||
$node->setAttribute('method', 'GET');
|
||||
$client->submit($form, [
|
||||
'template' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customer' => 1,
|
||||
]);
|
||||
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
// no warning should be displayed
|
||||
$node = $client->getCrawler()->filter('div.callout.callout-warning.lead');
|
||||
$this->assertEquals(0, $node->count());
|
||||
// but the datatable with all timesheets + 1 row for the total
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoice', 21);
|
||||
|
||||
$form = $client->getCrawler()->filter('#invoice-print-form')->form();
|
||||
$node = $form->getFormNode();
|
||||
$node->setAttribute('action', $this->createUrl('/invoice/?create='));
|
||||
$node->setAttribute('method', 'GET');
|
||||
$client->submit($form, [
|
||||
'template' => 1,
|
||||
'daterange' => $dateRange,
|
||||
'customer' => 1,
|
||||
'project' => 1,
|
||||
'markAsExported' => 1,
|
||||
]);
|
||||
|
||||
$this->assertIsRedirect($client, '/invoice/show?id=1');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->assertHasFlashSuccess($client);
|
||||
|
||||
$this->assertHasDataTable($client);
|
||||
$this->assertDataTableRowCount($client, 'datatable_invoices', 1);
|
||||
|
||||
// make sure the invoice is saved
|
||||
$this->request($client, '/invoice/download/1');
|
||||
$response = $client->getResponse();
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
self::assertInstanceOf(BinaryFileResponse::class, $response);
|
||||
self::assertFileExists($response->getFile());
|
||||
|
||||
$this->request($client, '/invoice/change-status/1/pending');
|
||||
$this->assertIsRedirect($client, '/invoice/show');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->request($client, '/invoice/change-status/1/paid');
|
||||
$this->assertIsRedirect($client, '/invoice/show');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$this->request($client, '/invoice/change-status/1/new');
|
||||
$this->assertIsRedirect($client, '/invoice/show');
|
||||
$client->followRedirect();
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
}
|
||||
|
||||
public function testEditTemplateAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
@@ -196,7 +312,6 @@ class InvoiceControllerTest extends ControllerBaseTest
|
||||
'company' => 'Company name',
|
||||
'renderer' => 'default',
|
||||
'calculator' => 'default',
|
||||
'numberGenerator' => 'default',
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class PermissionControllerTest extends ControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/admin/permissions');
|
||||
$this->assertHasDataTable($client);
|
||||
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 108);
|
||||
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 109);
|
||||
$this->assertPageActions($client, [
|
||||
'back' => $this->createUrl('/admin/user/'),
|
||||
'roles modal-ajax-form' => $this->createUrl('/admin/permissions/roles/create'),
|
||||
|
||||
@@ -68,6 +68,7 @@ class SystemConfigurationControllerTest extends ControllerBaseTest
|
||||
{
|
||||
return [
|
||||
['form[name=system_configuration_form_timesheet]', $this->createUrl('/admin/system-config/update/timesheet')],
|
||||
['form[name=system_configuration_form_invoice]', $this->createUrl('/admin/system-config/update/invoice')],
|
||||
['form[name=system_configuration_form_rounding]', $this->createUrl('/admin/system-config/update/rounding')],
|
||||
['form[name=system_configuration_form_form_customer]', $this->createUrl('/admin/system-config/update/form_customer')],
|
||||
['form[name=system_configuration_form_form_user]', $this->createUrl('/admin/system-config/update/form_user')],
|
||||
|
||||
@@ -289,6 +289,8 @@ class ConfigurationTest extends TestCase
|
||||
0 => 'var/invoices/',
|
||||
1 => 'templates/invoice/renderer/',
|
||||
],
|
||||
'simple_form' => true,
|
||||
'number_format' => '{Y}/{cy,3}',
|
||||
],
|
||||
'languages' => [],
|
||||
'calendar' => [
|
||||
|
||||
169
tests/Entity/InvoiceTest.php
Normal file
169
tests/Entity/InvoiceTest.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?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\Entity;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\ActivityMeta;
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerMeta;
|
||||
use App\Entity\Invoice;
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Entity\Project;
|
||||
use App\Entity\ProjectMeta;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserPreference;
|
||||
use App\Invoice\Calculator\DefaultCalculator;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\NumberGenerator\DateNumberGenerator;
|
||||
use App\Repository\Query\InvoiceQuery;
|
||||
use App\Tests\Invoice\DebugFormatter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Entity\Invoice
|
||||
*/
|
||||
class InvoiceTest extends TestCase
|
||||
{
|
||||
public function testDefaultValues()
|
||||
{
|
||||
$sut = new Invoice();
|
||||
self::assertNull($sut->getCreatedAt());
|
||||
self::assertNull($sut->getCurrency());
|
||||
self::assertNull($sut->getCustomer());
|
||||
self::assertNull($sut->getDueDate());
|
||||
self::assertEquals(30, $sut->getDueDays());
|
||||
self::assertNull($sut->getId());
|
||||
self::assertNull($sut->getInvoiceFilename());
|
||||
self::assertNull($sut->getInvoiceNumber());
|
||||
self::assertEquals(0.0, $sut->getTax());
|
||||
self::assertEquals(0.0, $sut->getTotal());
|
||||
self::assertNull($sut->getUser());
|
||||
self::assertEquals(0.0, $sut->getVat());
|
||||
self::assertTrue($sut->isNew());
|
||||
self::assertFalse($sut->isPending());
|
||||
self::assertFalse($sut->isPaid());
|
||||
self::assertFalse($sut->isOverdue());
|
||||
}
|
||||
|
||||
public function testSetterAndGetter()
|
||||
{
|
||||
$date = new \DateTime('-2 months');
|
||||
$sut = new Invoice();
|
||||
|
||||
$sut->setIsPending();
|
||||
self::assertFalse($sut->isNew());
|
||||
self::assertTrue($sut->isPending());
|
||||
self::assertFalse($sut->isPaid());
|
||||
|
||||
$sut->setIsPaid();
|
||||
self::assertFalse($sut->isNew());
|
||||
self::assertFalse($sut->isPending());
|
||||
self::assertTrue($sut->isPaid());
|
||||
|
||||
$sut->setIsNew();
|
||||
self::assertTrue($sut->isNew());
|
||||
self::assertFalse($sut->isPending());
|
||||
self::assertFalse($sut->isPaid());
|
||||
self::assertFalse($sut->isOverdue());
|
||||
|
||||
$sut->setModel($this->getInvoiceModel($date));
|
||||
self::assertTrue($sut->isOverdue());
|
||||
|
||||
self::assertEquals($date, $sut->getCreatedAt());
|
||||
self::assertEquals('USD', $sut->getCurrency());
|
||||
self::assertNotNull($sut->getCustomer());
|
||||
self::assertNotNull($sut->getDueDate());
|
||||
self::assertEquals(9, $sut->getDueDays());
|
||||
self::assertNull($sut->getId());
|
||||
self::assertNull($sut->getInvoiceFilename());
|
||||
self::assertEquals(date('ymd', $date->getTimestamp()), $sut->getInvoiceNumber());
|
||||
self::assertEquals(55.72, $sut->getTax());
|
||||
self::assertEquals(348.99, $sut->getTotal());
|
||||
self::assertNotNull($sut->getUser());
|
||||
self::assertEquals(19, $sut->getVat());
|
||||
}
|
||||
|
||||
protected function getInvoiceModel(\DateTime $created): InvoiceModel
|
||||
{
|
||||
$user = new User();
|
||||
$user->setUsername('one-user');
|
||||
$user->setTitle('user title');
|
||||
$user->setAlias('genious alias');
|
||||
$user->setEmail('fantastic@four');
|
||||
$user->addPreference((new UserPreference())->setName('kitty')->setValue('kat'));
|
||||
$user->addPreference((new UserPreference())->setName('hello')->setValue('world'));
|
||||
|
||||
$customer = new Customer();
|
||||
$customer->setName('customer,with/special#name');
|
||||
$customer->setCurrency('USD');
|
||||
$customer->setMetaField((new CustomerMeta())->setName('foo-customer')->setValue('bar-customer')->setIsVisible(true));
|
||||
$customer->setVatId('kjuo8967');
|
||||
|
||||
$template = new InvoiceTemplate();
|
||||
$template->setTitle('a test invoice template title');
|
||||
$template->setVat(19);
|
||||
$template->setDueDays(9);
|
||||
|
||||
$project = new Project();
|
||||
$project->setName('project name');
|
||||
$project->setCustomer($customer);
|
||||
$project->setMetaField((new ProjectMeta())->setName('foo-project')->setValue('bar-project')->setIsVisible(true));
|
||||
|
||||
$activity = new Activity();
|
||||
$activity->setName('activity description');
|
||||
$activity->setProject($project);
|
||||
$activity->setMetaField((new ActivityMeta())->setName('foo-activity')->setValue('bar-activity')->setIsVisible(true));
|
||||
|
||||
$userMethods = ['getId', 'getPreferenceValue', 'getUsername'];
|
||||
$user1 = $this->getMockBuilder(User::class)->onlyMethods($userMethods)->disableOriginalConstructor()->getMock();
|
||||
$user1->method('getId')->willReturn(1);
|
||||
$user1->method('getPreferenceValue')->willReturn('50');
|
||||
$user1->method('getUsername')->willReturn('foo-bar');
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet
|
||||
->setDuration(3600)
|
||||
->setRate(293.27)
|
||||
->setUser($user1)
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
;
|
||||
|
||||
$entries = [$timesheet];
|
||||
|
||||
$query = new InvoiceQuery();
|
||||
$query->setActivity($activity);
|
||||
$query->setBegin(new \DateTime());
|
||||
$query->setEnd(new \DateTime());
|
||||
|
||||
$model = new InvoiceModel(new DebugFormatter());
|
||||
$model->setCustomer($customer);
|
||||
$model->setTemplate($template);
|
||||
$model->addEntries($entries);
|
||||
$model->setQuery($query);
|
||||
$model->setUser($user);
|
||||
$model->setInvoiceDate($created);
|
||||
|
||||
$calculator = new DefaultCalculator();
|
||||
$calculator->setModel($model);
|
||||
|
||||
$model->setCalculator($calculator);
|
||||
|
||||
$numberGenerator = new DateNumberGenerator();
|
||||
$numberGenerator->setModel($model);
|
||||
|
||||
$model->setNumberGenerator($numberGenerator);
|
||||
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
@@ -33,33 +33,6 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
|
||||
$this->assertEmptyModel(new ActivityInvoiceCalculator());
|
||||
}
|
||||
|
||||
public function testExceptionNoActivity()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$this->expectExceptionMessage('Cannot work with invoice items that do not have an activity');
|
||||
$timesheet = new Timesheet();
|
||||
|
||||
$sut = new ActivityInvoiceCalculator();
|
||||
$model = $this->getEmptyModel();
|
||||
$model->addEntries([$timesheet]);
|
||||
$sut->setModel($model);
|
||||
$sut->getEntries();
|
||||
}
|
||||
|
||||
public function testExceptionNoId()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$this->expectExceptionMessage('Cannot handle un-persisted activities');
|
||||
$timesheet = new Timesheet();
|
||||
$timesheet->setActivity(new Activity());
|
||||
|
||||
$sut = new ActivityInvoiceCalculator();
|
||||
$model = $this->getEmptyModel();
|
||||
$model->addEntries([$timesheet]);
|
||||
$sut->setModel($model);
|
||||
$sut->getEntries();
|
||||
}
|
||||
|
||||
public function testWithMultipleEntries()
|
||||
{
|
||||
$customer = new Customer();
|
||||
@@ -128,7 +101,35 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
|
||||
->setActivity($activity3)
|
||||
->setProject((new Project())->setName('bar'));
|
||||
|
||||
$entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5];
|
||||
$timesheet6 = new Timesheet();
|
||||
$timesheet6
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
->setDuration(0)
|
||||
->setRate(0)
|
||||
->setUser(new User())
|
||||
->setProject((new Project())->setName('bar'));
|
||||
|
||||
$timesheet7 = new Timesheet();
|
||||
$timesheet7
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
->setDuration(0)
|
||||
->setRate(0)
|
||||
->setUser(new User())
|
||||
->setActivity(new Activity())
|
||||
->setProject((new Project())->setName('bar'));
|
||||
|
||||
$timesheet8 = new Timesheet();
|
||||
$timesheet8
|
||||
->setBegin(new \DateTime())
|
||||
->setEnd(new \DateTime())
|
||||
->setDuration(0)
|
||||
->setRate(0)
|
||||
->setUser(new User())
|
||||
->setProject((new Project())->setName('bar'));
|
||||
|
||||
$entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5, $timesheet6, $timesheet7, $timesheet8];
|
||||
|
||||
$query = new InvoiceQuery();
|
||||
$query->setActivity($activity1);
|
||||
@@ -148,7 +149,7 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
|
||||
$this->assertEquals('EUR', $model->getCurrency());
|
||||
$this->assertEquals(2521.12, $sut->getSubtotal());
|
||||
$this->assertEquals(6600, $sut->getTimeWorked());
|
||||
$this->assertEquals(3, count($sut->getEntries()));
|
||||
$this->assertEquals(5, count($sut->getEntries()));
|
||||
|
||||
$entries = $sut->getEntries();
|
||||
$this->assertEquals(404.38, $entries[0]->getRate());
|
||||
|
||||
53
tests/Invoice/InvoiceFilenameTest.php
Normal file
53
tests/Invoice/InvoiceFilenameTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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\Invoice;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Invoice\InvoiceFilename;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\NumberGenerator\DateNumberGenerator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Invoice\InvoiceFilename
|
||||
*/
|
||||
class InvoiceFilenameTest extends TestCase
|
||||
{
|
||||
public function testInvoiceFilename()
|
||||
{
|
||||
$customer = new Customer();
|
||||
$template = new InvoiceTemplate();
|
||||
|
||||
$model = new InvoiceModel(new DebugFormatter());
|
||||
$model->setNumberGenerator(new DateNumberGenerator());
|
||||
$model->setTemplate($template);
|
||||
$model->setCustomer($customer);
|
||||
|
||||
$datePrefix = date('ymd');
|
||||
|
||||
$sut = new InvoiceFilename($model);
|
||||
|
||||
self::assertEquals($datePrefix, $sut->getFilename());
|
||||
self::assertEquals($datePrefix, (string) $sut);
|
||||
|
||||
$customer->setName('foo');
|
||||
$sut = new InvoiceFilename($model);
|
||||
|
||||
self::assertEquals($datePrefix . '-foo', $sut->getFilename());
|
||||
self::assertEquals($datePrefix . '-foo', (string) $sut);
|
||||
|
||||
$customer->setCompany('barß / laölala # ldksjf 123');
|
||||
$sut = new InvoiceFilename($model);
|
||||
|
||||
self::assertEquals($datePrefix . '-barß_laölala_ldksjf123', $sut->getFilename());
|
||||
self::assertEquals($datePrefix . '-barß_laölala_ldksjf123', (string) $sut);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?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\Invoice\NumberGenerator;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\NumberGenerator\ConfigurableNumberGenerator;
|
||||
use App\Repository\InvoiceRepository;
|
||||
use App\Tests\Invoice\DebugFormatter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\Invoice\NumberGenerator\ConfigurableNumberGenerator
|
||||
*/
|
||||
class ConfigurableNumberGeneratorTest extends TestCase
|
||||
{
|
||||
private function getSut(string $format)
|
||||
{
|
||||
$config = $this->createMock(SystemConfiguration::class);
|
||||
$config->expects($this->any())
|
||||
->method('find')
|
||||
->willReturn($format);
|
||||
|
||||
$repository = $this->createMock(InvoiceRepository::class);
|
||||
$repository
|
||||
->expects($this->any())
|
||||
->method('getCounterForAllTime')
|
||||
->willReturn(1);
|
||||
$repository
|
||||
->expects($this->any())
|
||||
->method('getCounterForYear')
|
||||
->willReturn(1);
|
||||
$repository
|
||||
->expects($this->any())
|
||||
->method('getCounterForMonth')
|
||||
->willReturn(1);
|
||||
$repository
|
||||
->expects($this->any())
|
||||
->method('getCounterForDay')
|
||||
->willReturn(1);
|
||||
|
||||
return new ConfigurableNumberGenerator($repository, $config);
|
||||
}
|
||||
|
||||
public function getTestData()
|
||||
{
|
||||
$timestamp = time();
|
||||
|
||||
return [
|
||||
// simple tests for single calls
|
||||
['{date}', date('ymd'), $timestamp],
|
||||
['{Y}', date('Y'), $timestamp],
|
||||
['{y}', date('y'), $timestamp],
|
||||
['{M}', date('m'), $timestamp],
|
||||
['{m}', date('n'), $timestamp],
|
||||
['{D}', date('d'), $timestamp],
|
||||
['{d}', date('j'), $timestamp],
|
||||
['{c}', '2', $timestamp],
|
||||
['{cy}', '2', $timestamp],
|
||||
['{cm}', '2', $timestamp],
|
||||
['{cd}', '2', $timestamp],
|
||||
// number formatting (not testing the lower case versions, as the tests might break depending on the date)
|
||||
['{date,10}', '0000' . date('ymd'), $timestamp],
|
||||
['{Y,6}', '00' . date('Y'), $timestamp],
|
||||
['{M,3}', '0' . date('m'), $timestamp],
|
||||
['{D,3}', '0' . date('d'), $timestamp],
|
||||
['{c,2}', '02', $timestamp],
|
||||
['{cy,2}', '02', $timestamp],
|
||||
['{cm,2}', '02', $timestamp],
|
||||
['{cd,2}', '02', $timestamp],
|
||||
// mixing identifiers
|
||||
['{Y}{cy}', date('Y') . '2', $timestamp],
|
||||
['{Y}{cy}{m}', date('Y') . '2' . date('n'), $timestamp],
|
||||
['{Y}-{cy}/{m}', date('Y') . '-2/' . date('n'), $timestamp],
|
||||
['{Y}-{cy}/{m}', date('Y') . '-2/' . date('n'), $timestamp],
|
||||
['{Y,5}/{cy,5}', '0' . date('Y') . '/00002', $timestamp],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getTestData
|
||||
*/
|
||||
public function testGetInvoiceNumber(string $format, string $expectedInvoiceNumber, int $timestamp)
|
||||
{
|
||||
$sut = $this->getSut($format);
|
||||
$model = new InvoiceModel(new DebugFormatter());
|
||||
$model->setInvoiceDate((new \DateTime())->setTimestamp($timestamp));
|
||||
$sut->setModel($model);
|
||||
|
||||
$this->assertEquals($expectedInvoiceNumber, $sut->getInvoiceNumber());
|
||||
$this->assertEquals('default', $sut->getId());
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,6 @@ class DateNumberGeneratorTest extends TestCase
|
||||
$sut->setModel(new InvoiceModel(new DebugFormatter()));
|
||||
|
||||
$this->assertEquals(date('ymd'), $sut->getInvoiceNumber());
|
||||
$this->assertEquals('default', $sut->getId());
|
||||
$this->assertEquals('date', $sut->getId());
|
||||
}
|
||||
}
|
||||
|
||||
65
tests/Invoice/Renderer/PdfRendererTest.php
Normal file
65
tests/Invoice/Renderer/PdfRendererTest.php
Normal 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\Invoice\Renderer;
|
||||
|
||||
use App\Invoice\Renderer\PdfRenderer;
|
||||
use App\Utils\MPdfConverter;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Invoice\Renderer\PdfRenderer
|
||||
* @group integration
|
||||
*/
|
||||
class PdfRendererTest extends KernelTestCase
|
||||
{
|
||||
use RendererTestTrait;
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$loader = new FilesystemLoader();
|
||||
$env = new Environment($loader);
|
||||
$sut = new PdfRenderer($env, $this->createMock(MPdfConverter::class));
|
||||
$this->assertTrue($sut->supports($this->getInvoiceDocument('default.pdf.twig', true)));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx')));
|
||||
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
|
||||
}
|
||||
|
||||
public function testRender()
|
||||
{
|
||||
$kernel = self::bootKernel();
|
||||
/** @var Environment $twig */
|
||||
$twig = $kernel->getContainer()->get('twig');
|
||||
$stack = $kernel->getContainer()->get('request_stack');
|
||||
$cacheDir = $kernel->getContainer()->getParameter('kernel.cache_dir');
|
||||
|
||||
$request = new Request();
|
||||
$request->setLocale('en');
|
||||
$stack->push($request);
|
||||
|
||||
/** @var FilesystemLoader $loader */
|
||||
$loader = $twig->getLoader();
|
||||
$loader->addPath(__DIR__ . '/../templates/', 'invoice');
|
||||
|
||||
$sut = new PdfRenderer($twig, new MPdfConverter($cacheDir));
|
||||
$model = $this->getInvoiceModel();
|
||||
$document = $this->getInvoiceDocument('default.pdf.twig', true);
|
||||
|
||||
$response = $sut->render($document, $model);
|
||||
$this->assertEquals('application/pdf', $response->headers->get('Content-Type'));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use App\Invoice\NumberGenerator\DateNumberGenerator;
|
||||
use App\Invoice\Renderer\TwigRenderer;
|
||||
use App\Invoice\ServiceInvoice;
|
||||
use App\Repository\InvoiceDocumentRepository;
|
||||
use App\Utils\FileHelper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\Environment;
|
||||
|
||||
@@ -23,10 +24,16 @@ use Twig\Environment;
|
||||
*/
|
||||
class ServiceInvoiceTest extends TestCase
|
||||
{
|
||||
private function getSut(array $paths): ServiceInvoice
|
||||
{
|
||||
$repo = new InvoiceDocumentRepository($paths);
|
||||
|
||||
return new ServiceInvoice($repo, new FileHelper(realpath(__DIR__ . '/../../var/data/')));
|
||||
}
|
||||
|
||||
public function testEmptyObject()
|
||||
{
|
||||
$repo = new InvoiceDocumentRepository([]);
|
||||
$sut = new ServiceInvoice($repo);
|
||||
$sut = $this->getSut([]);
|
||||
|
||||
$this->assertEmpty($sut->getCalculator());
|
||||
$this->assertIsArray($sut->getCalculator());
|
||||
@@ -44,8 +51,7 @@ class ServiceInvoiceTest extends TestCase
|
||||
|
||||
public function testWithDocumentDirectory()
|
||||
{
|
||||
$repo = new InvoiceDocumentRepository(['templates/invoice/renderer/']);
|
||||
$sut = new ServiceInvoice($repo);
|
||||
$sut = $this->getSut(['templates/invoice/renderer/']);
|
||||
|
||||
$actual = $sut->getDocuments();
|
||||
$this->assertNotEmpty($actual);
|
||||
@@ -59,8 +65,7 @@ class ServiceInvoiceTest extends TestCase
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
$repo = new InvoiceDocumentRepository([]);
|
||||
$sut = new ServiceInvoice($repo);
|
||||
$sut = $this->getSut([]);
|
||||
|
||||
$sut->addCalculator(new DefaultCalculator());
|
||||
$sut->addNumberGenerator(new DateNumberGenerator());
|
||||
@@ -74,7 +79,7 @@ class ServiceInvoiceTest extends TestCase
|
||||
$this->assertInstanceOf(DefaultCalculator::class, $sut->getCalculatorByName('default'));
|
||||
|
||||
$this->assertEquals(1, count($sut->getNumberGenerator()));
|
||||
$this->assertInstanceOf(DateNumberGenerator::class, $sut->getNumberGeneratorByName('default'));
|
||||
$this->assertInstanceOf(DateNumberGenerator::class, $sut->getNumberGeneratorByName('date'));
|
||||
|
||||
$this->assertEquals(1, count($sut->getRenderer()));
|
||||
}
|
||||
|
||||
146
tests/Invoice/templates/default.pdf.twig
Normal file
146
tests/Invoice/templates/default.pdf.twig
Normal file
@@ -0,0 +1,146 @@
|
||||
{% extends 'invoice/layout.html.twig' %}
|
||||
{% set language = model.template.language|default(app.request.locale) %}
|
||||
{% set isDecimal = model.template.decimalDuration|default(false) %}
|
||||
|
||||
{% block invoice %}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h2 class="page-header">
|
||||
<span contenteditable="true">{{ model.template.title }}</span>
|
||||
<small class="pull-right">{{ 'label.date'|trans({}, 'messages', language) }}: {{ model.invoiceDate|date_short }}</small>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{ 'invoice.from'|trans({}, 'messages', language) }}
|
||||
<address contenteditable="true">
|
||||
<strong>{{ model.template.company }}</strong><br>
|
||||
{{ model.template.address|trim|nl2br }}
|
||||
{% if model.template.vatId is not empty %}
|
||||
<br>
|
||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}:
|
||||
{{ model.template.vatId }}
|
||||
{% endif %}
|
||||
</address>
|
||||
</div>
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-5">
|
||||
{{ 'invoice.to'|trans({}, 'messages', language) }}
|
||||
<address contenteditable="true">
|
||||
<strong>{{ model.customer.company|default(model.customer.name) }}</strong><br>
|
||||
{{ model.customer.address|nl2br }}
|
||||
{% if model.customer.vatId is not empty %}
|
||||
<br>
|
||||
{{ 'label.vat_id'|trans({}, 'messages', language) }}: {{ model.customer.vatId }}
|
||||
{% endif %}
|
||||
{% if model.customer.number is not empty %}
|
||||
<br>
|
||||
{{ 'label.number'|trans({}, 'messages', language) }}: {{ model.customer.number }}
|
||||
{% endif %}
|
||||
{% if model.query.project is not empty and model.query.project.orderNumber is not empty %}
|
||||
<br>
|
||||
{{ 'label.orderNumber'|trans({}, 'messages', language) }}: {{ model.query.project.orderNumber }}
|
||||
{% endif %}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<p contenteditable="true">
|
||||
<strong>{{ 'invoice.number'|trans({}, 'messages', language) }}:</strong>
|
||||
{{ model.numberGenerator.invoiceNumber }}
|
||||
|
||||
<br>
|
||||
<strong>{{ 'invoice.due_days'|trans({}, 'messages', language) }}:</strong>
|
||||
{{ model.dueDate|date_short }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-7"></div>
|
||||
</div>
|
||||
|
||||
<div class="row invoice-items">
|
||||
<div class="col-xs-12 table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ 'label.date'|trans({}, 'messages', language) }}</th>
|
||||
<th>{{ 'label.description'|trans({}, 'messages', language) }}</th>
|
||||
<th class="text-right">{{ 'label.unit_price'|trans({}, 'messages', language) }}</th>
|
||||
<th class="text-right">{{ 'label.amount'|trans({}, 'messages', language) }}</th>
|
||||
<th class="text-right">{{ 'label.total_rate'|trans({}, 'messages', language) }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in model.calculator.entries %}
|
||||
{% set duration = entry.duration|duration(isDecimal) %}
|
||||
{% if entry.fixedRate %}
|
||||
{% set rate = entry.fixedRate %}
|
||||
{% set duration = entry.amount|amount %}
|
||||
{% else %}
|
||||
{% set rate = entry.hourlyRate %}
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td nowrap class="text-nowrap">{{ entry.begin|date_short }}</td>
|
||||
<td contenteditable="true">
|
||||
{% if entry.description is not empty %}
|
||||
{{ entry.description|nl2br }}
|
||||
{% else %}
|
||||
{% if entry.activity is not null %}{{ entry.activity.name }} / {% endif %}{{ entry.project.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td nowrap class="text-nowrap text-right">{{ rate|money(model.calculator.currency) }}</td>
|
||||
<td nowrap class="text-nowrap text-right">{{ duration }}</td>
|
||||
<td nowrap class="text-nowrap text-right">{{ entry.rate|money(model.calculator.currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4" class="text-right">
|
||||
{{ 'invoice.subtotal'|trans({}, 'messages', language) }}
|
||||
</td>
|
||||
<td class="text-right">{{ model.calculator.subtotal|money(model.calculator.currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="text-right">
|
||||
{{ 'invoice.tax'|trans({}, 'messages', language) }} ({{ model.calculator.vat }}%)
|
||||
</td>
|
||||
<td class="text-right">{{ model.calculator.tax|money(model.calculator.currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="text-right text-nowrap">
|
||||
<strong>{{ 'invoice.total'|trans({}, 'messages', language) }}</strong>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<strong>{{ model.calculator.total|money(model.calculator.currency) }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
{% if model.template.paymentTerms is not empty %}
|
||||
<div contenteditable="true" class="paymentTerms">
|
||||
{{ model.template.paymentTerms|nl2br|md2html }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<p>
|
||||
<strong>{{ 'label.address'|trans({}, 'messages', language) }}</strong>: {{ model.template.company }} – {{ model.template.address|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||
<br>
|
||||
<strong>{{ 'label.invoice_bank_account'|trans({}, 'messages', language) }}</strong>: {{ model.template.paymentDetails|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||
<br>
|
||||
<strong>{{ 'label.contact'|trans({}, 'messages', language) }}</strong>: {{ model.template.contact|replace({"\n": ' – ', "\r\n": ' – ', "\r": ' – '})|raw }}
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
{% endblock %}
|
||||
43
tests/Repository/Loader/AbstractLoaderTest.php
Normal file
43
tests/Repository/Loader/AbstractLoaderTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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\Loader;
|
||||
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class AbstractLoaderTest extends TestCase
|
||||
{
|
||||
protected function getEntityManagerMock(int $createQueryBuilderCount)
|
||||
{
|
||||
$em = $this->createMock(EntityManager::class);
|
||||
$qb = $this->createMock(QueryBuilder::class);
|
||||
$query = $this->createMock(AbstractQuery::class);
|
||||
$expr = $this->createMock(Expr::class);
|
||||
|
||||
$expr->expects($this->any())->method('isNotNull')->willReturn('');
|
||||
$expr->expects($this->any())->method('in')->willReturn('');
|
||||
|
||||
$qb->expects($this->any())->method('andWhere')->willReturnSelf();
|
||||
$qb->expects($this->any())->method('from')->willReturnSelf();
|
||||
$qb->expects($this->any())->method('expr')->willReturn($expr);
|
||||
$qb->expects($this->any())->method('from')->willReturnSelf();
|
||||
$qb->expects($this->any())->method('select')->willReturnSelf();
|
||||
$qb->expects($this->any())->method('leftJoin')->willReturnSelf();
|
||||
$qb->expects($this->any())->method('getQuery')->willReturn($query);
|
||||
$query->expects($this->any())->method('execute')->willReturn(null);
|
||||
|
||||
$em->expects($this->exactly($createQueryBuilderCount))->method('createQueryBuilder')->willReturn($qb);
|
||||
|
||||
return $em;
|
||||
}
|
||||
}
|
||||
33
tests/Repository/Loader/ActivityLoaderTest.php
Normal file
33
tests/Repository/Loader/ActivityLoaderTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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\Loader;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use App\Repository\Loader\ActivityLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\Loader\ActivityLoader
|
||||
* @covers \App\Repository\Loader\ActivityIdLoader
|
||||
*/
|
||||
class ActivityLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
public function testLoadResults()
|
||||
{
|
||||
// mock needs improvements, because it should be 5
|
||||
$em = $this->getEntityManagerMock(2);
|
||||
|
||||
$sut = new ActivityLoader($em);
|
||||
|
||||
$entity = $this->createMock(Activity::class);
|
||||
$entity->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut->loadResults([$entity]);
|
||||
}
|
||||
}
|
||||
32
tests/Repository/Loader/CustomerLoaderTest.php
Normal file
32
tests/Repository/Loader/CustomerLoaderTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Loader;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Repository\Loader\CustomerLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\Loader\CustomerLoader
|
||||
* @covers \App\Repository\Loader\CustomerIdLoader
|
||||
*/
|
||||
class CustomerLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
public function testLoadResults()
|
||||
{
|
||||
$em = $this->getEntityManagerMock(2);
|
||||
|
||||
$sut = new CustomerLoader($em);
|
||||
|
||||
$entity = $this->createMock(Customer::class);
|
||||
$entity->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut->loadResults([$entity]);
|
||||
}
|
||||
}
|
||||
32
tests/Repository/Loader/InvoiceLoaderTest.php
Normal file
32
tests/Repository/Loader/InvoiceLoaderTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Loader;
|
||||
|
||||
use App\Entity\Invoice;
|
||||
use App\Repository\Loader\InvoiceLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\Loader\InvoiceLoader
|
||||
* @covers \App\Repository\Loader\InvoiceIdLoader
|
||||
*/
|
||||
class InvoiceLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
public function testLoadResults()
|
||||
{
|
||||
$em = $this->getEntityManagerMock(2);
|
||||
|
||||
$sut = new InvoiceLoader($em);
|
||||
|
||||
$entity = $this->createMock(Invoice::class);
|
||||
$entity->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut->loadResults([$entity]);
|
||||
}
|
||||
}
|
||||
32
tests/Repository/Loader/ProjectLoaderTest.php
Normal file
32
tests/Repository/Loader/ProjectLoaderTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Loader;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Repository\Loader\ProjectLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\Loader\ProjectLoader
|
||||
* @covers \App\Repository\Loader\ProjectIdLoader
|
||||
*/
|
||||
class ProjectLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
public function testLoadResults()
|
||||
{
|
||||
$em = $this->getEntityManagerMock(4);
|
||||
|
||||
$sut = new ProjectLoader($em);
|
||||
|
||||
$entity = $this->createMock(Project::class);
|
||||
$entity->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut->loadResults([$entity]);
|
||||
}
|
||||
}
|
||||
32
tests/Repository/Loader/TeamLoaderTest.php
Normal file
32
tests/Repository/Loader/TeamLoaderTest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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\Loader;
|
||||
|
||||
use App\Entity\Team;
|
||||
use App\Repository\Loader\TeamLoader;
|
||||
|
||||
/**
|
||||
* @covers \App\Repository\Loader\TeamLoader
|
||||
* @covers \App\Repository\Loader\TeamIdLoader
|
||||
*/
|
||||
class TeamLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
public function testLoadResults()
|
||||
{
|
||||
$em = $this->getEntityManagerMock(1);
|
||||
|
||||
$sut = new TeamLoader($em);
|
||||
|
||||
$entity = $this->createMock(Team::class);
|
||||
$entity->expects($this->once())->method('getId')->willReturn(1);
|
||||
|
||||
$sut->loadResults([$entity]);
|
||||
}
|
||||
}
|
||||
0
tests/_data/.gitignore
vendored
Normal file
0
tests/_data/.gitignore
vendored
Normal file
Reference in New Issue
Block a user