Files
InvoiceShelf/tests/Unit/PdfReportPageSetupTest.php
T
Darko Gjorgjijoski 5ef7804e60 refactor: adopt modular domain architecture (#747)
* refactor: stabilize model identities for domain migration

* refactor: extract module platform context

* refactor: assign models to domain contexts

* refactor: extract ai platform context

* refactor: extract storage platform context

* refactor: extract mail platform context

* refactor: extract pdf platform context

* refactor: extract operations platform context

* refactor: move installation into operations platform

* refactor: extract money domain context

* refactor: extract taxation domain context

* refactor: extract catalog domain context

* refactor: extract metadata domain context

* refactor: extract reporting domain context

* refactor: extract purchases domain context

* refactor: extract receivables domain context

* refactor: extract accounts domain context

* refactor: complete reporting statement boundary

* refactor: extract contacts domain context

* refactor: extract sales domain context

* refactor: remove legacy application layers

* fix: migrate legacy bouncer role identities
2026-08-05 17:40:03 +02:00

173 lines
6.1 KiB
PHP

<?php
use App\Platform\Pdf\Rendering\DompdfDriver;
use App\Platform\Pdf\Rendering\GotenbergPdfDriver;
use App\Platform\Pdf\Rendering\PdfPageSetup;
use Barryvdh\DomPDF\PDF;
/**
* The report templates carry no inset of their own: they set
* `.sub-container { padding: 0px 20px }` and nothing else, and were drawn
* against dompdf's built-in 1.2cm page margin. Once DompdfDriver started
* injecting an @page rule from config, the document default of 0 reached them
* and their content landed flush against the paper edge.
*
* That zero has to stay for documents (invoice2 and estimate2 bleed a header
* band to the edge), so the reports get a margin of their own instead.
*/
beforeEach(function () {
config([
'pdf.page.paper_width' => '210mm',
'pdf.page.paper_height' => '297mm',
'pdf.page.orientation' => 'portrait',
'pdf.page.margin_top' => '0',
'pdf.page.margin_right' => '0',
'pdf.page.margin_bottom' => '0',
'pdf.page.margin_left' => '0',
'pdf.page.report_margin' => '1.2cm',
]);
});
test('reports render with the configured report margin on all four sides', function () {
config(['pdf.page.report_margin' => '2cm']);
$page = PdfPageSetup::forReports();
expect($page->marginTop)->toBe('2cm')
->and($page->marginRight)->toBe('2cm')
->and($page->marginBottom)->toBe('2cm')
->and($page->marginLeft)->toBe('2cm')
->and($page->marginCss())->toBe('2cm 2cm 2cm 2cm');
});
test('reports keep the configured paper size and orientation', function () {
config([
'pdf.page.paper_width' => '8.5in',
'pdf.page.paper_height' => '14in',
'pdf.page.orientation' => 'landscape',
]);
$page = PdfPageSetup::forReports();
expect($page->width)->toBe('8.5in')
->and($page->height)->toBe('14in')
->and($page->isLandscape())->toBeTrue();
});
/**
* The whole point of the separate key: an operator who sets document margins to
* suit their own invoice template must not silently reflow every report too.
*/
test('the document margins do not leak into the report page', function () {
config([
'pdf.page.margin_top' => '40mm',
'pdf.page.margin_right' => '30mm',
'pdf.page.margin_bottom' => '20mm',
'pdf.page.margin_left' => '10mm',
]);
expect(PdfPageSetup::forReports()->marginCss())->toBe('1.2cm 1.2cm 1.2cm 1.2cm')
->and(PdfPageSetup::fromConfig()->marginCss())->toBe('40mm 30mm 20mm 10mm');
});
test('a blank or unset report margin falls back to dompdf\'s old default', function ($value) {
config(['pdf.page.report_margin' => $value]);
expect(PdfPageSetup::forReports()->marginTop)->toBe('1.2cm');
})->with([
'unset' => [null],
'blank' => [''],
'whitespace' => [' '],
]);
/**
* An env typo should fail loudly rather than render at some other size, and the
* message has to name the key so the fix is obvious.
*/
test('a malformed report margin is rejected', function () {
config(['pdf.page.report_margin' => '12']);
expect(fn () => PdfPageSetup::forReports())
->toThrow(InvalidArgumentException::class, 'pdf.page.report_margin');
});
test('withUniformMargin returns a new page and leaves the original alone', function () {
$original = PdfPageSetup::fromConfig();
$inset = $original->withUniformMargin('15mm');
expect($inset)->not->toBe($original)
->and($inset->marginCss())->toBe('15mm 15mm 15mm 15mm')
->and($original->marginCss())->toBe('0 0 0 0')
->and($inset->width)->toBe($original->width)
->and($inset->height)->toBe($original->height)
->and($inset->orientation)->toBe($original->orientation);
});
test('withUniformMargin rejects a value that is not a length', function () {
expect(fn () => PdfPageSetup::fromConfig()->withUniformMargin('wide'))
->toThrow(InvalidArgumentException::class, 'Invalid PDF page margin');
});
test('a bare zero is still an acceptable uniform margin', function () {
expect(PdfPageSetup::fromConfig()->withUniformMargin('0')->marginCss())->toBe('0 0 0 0');
});
/**
* dompdf has no margin API, so the injected @page rule is the only thing that
* carries the margin. It has to be built from the page it was handed, not read
* back out of config, or passing one would be a no-op.
*/
test('dompdf injects the page setup it is given rather than the configured one', function () {
$method = new ReflectionMethod(DompdfDriver::class, 'withPageMargins');
$html = $method->invoke(
new DompdfDriver,
'<html><head></head><body></body></html>',
PdfPageSetup::fromConfig()->withUniformMargin('1.2cm')
);
expect($html)->toContain('@page { margin: 1.2cm 1.2cm 1.2cm 1.2cm; }');
});
/**
* And that loadView() actually threads the page through to that injection
* rather than reading config again, which the reflection test above cannot see.
* The wrapper is captured instead of asserting on the rendered bytes: dompdf
* stamps a CreationDate, so two renders differ whatever the margins are.
*/
test('dompdf renders the report page setup it is handed', function () {
$driver = new class extends DompdfDriver
{
public string $html = '';
protected function wrapper(): PDF
{
$pdf = Mockery::mock(PDF::class);
$pdf->shouldReceive('setPaper')->andReturnSelf();
$pdf->shouldReceive('loadHTML')->andReturnUsing(function (string $html) use ($pdf) {
$this->html = $html;
return $pdf;
});
return $pdf;
}
};
$driver->loadView('app.pdf.partials.fonts', [], PdfPageSetup::forReports());
expect($driver->html)->toContain('@page { margin: 1.2cm 1.2cm 1.2cm 1.2cm; }');
});
test('gotenberg sends the margins of the page setup it is given', function () {
config(['pdf.connections.gotenberg.host' => 'http://gotenberg.example.com:3000']);
$body = (string) (new GotenbergPdfDriver)
->buildRequest('app.pdf.partials.fonts', [], PdfPageSetup::forReports())
->getBody();
expect($body)->toContain('marginTop')
->toContain('1.2cm')
->toContain('210mm');
});