Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/Backend/Dompdf/Tests/DomAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
use KNPLabs\Snappy\Core\Backend\Options;
use Nyholm\Psr7\Factory\Psr17Factory;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamInterface;

/**
* @internal
*/
#[CoversNothing]
#[Group('integration')]
final class DomAdapterTest extends TestCase
{
private DompdfFactory $factory;
Expand All @@ -23,6 +25,10 @@ final class DomAdapterTest extends TestCase

protected function setUp(): void
{
if (!\extension_loaded('imagick')) {
self::markTestSkipped('Imagick extension is required for PDF comparison tests');
}

$this->factory = new DompdfFactory(new Psr17Factory());
$this->options = Options::create()
->withExtraOptions(
Expand Down Expand Up @@ -77,7 +83,8 @@ private function assertPdfStreamEqualsFile(StreamInterface $stream, string $file

stream_copy_to_stream($from, $to);

$path = stream_get_meta_data($to)['uri'];
$metadata = stream_get_meta_data($to);
$path = $metadata['uri'] ?? throw new \RuntimeException('Stream metadata does not contain uri.');

$controlDocument = new \Imagick();
$compareDocument = new \Imagick();
Expand Down
44 changes: 22 additions & 22 deletions src/Backend/WkHtmlToPdf/Tests/ExtraOptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@
#[CoversNothing]
final class ExtraOptionTest extends TestCase
{
/**
* @param non-empty-array<string> $command
*/
#[DataProvider('provideRepeatableOptionCases')]
public function testRepeatableOption(ExtraOption $option, array $command): void
{
self::assertTrue($option->repeatable);
self::assertSame($option->command, $command);
}

/**
* @return iterable<array{ExtraOption, non-empty-array<string>}>
*/
public static function repeatableProvider(): iterable
public static function provideRepeatableOptionCases(): iterable
{
yield [
new ExtraOption\Allow('/the/path'),
Expand Down Expand Up @@ -76,10 +86,20 @@ public static function repeatableProvider(): iterable
];
}

/**
* @param non-empty-array<string> $command
*/
#[DataProvider('provideNonRepeatableOptionCases')]
public function testNonRepeatableOption(ExtraOption $option, array $command): void
{
self::assertFalse($option->repeatable);
self::assertSame($option->command, $command);
}

/**
* @return iterable<array{ExtraOption, non-empty-array<string>}>
*/
public static function nonRepeatableProvider(): iterable
public static function provideNonRepeatableOptionCases(): iterable
{
yield [
new ExtraOption\NoCollate(),
Expand Down Expand Up @@ -476,24 +496,4 @@ public static function nonRepeatableProvider(): iterable
['--xsl-style-sheet', '/the/path'],
];
}

/**
* @param non-empty-array<string> $command
*/
#[DataProvider('repeatableProvider')]
public function testRepeatableOption(ExtraOption $option, array $command): void
{
self::assertTrue($option->repeatable);
self::assertSame($option->command, $command);
}

/**
* @param non-empty-array<string> $command
*/
#[DataProvider('nonRepeatableProvider')]
public function testNonRepeatableOption(ExtraOption $option, array $command): void
{
self::assertFalse($option->repeatable);
self::assertSame($option->command, $command);
}
}
20 changes: 20 additions & 0 deletions src/Backend/WkHtmlToPdf/Tests/WkHtmlToPdfAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use KNPLabs\Snappy\Core\Backend\Options;
use Nyholm\Psr7\Uri;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamFactoryInterface;
Expand Down Expand Up @@ -54,8 +55,13 @@ protected function setUp(): void
$this->wkHtmlToPdfAdapter = $this->factory->create(new Options(null, []));
}

#[Group('integration')]
public function testGenerateFromHtmlFile(): void
{
if (!$this->isWkhtmltopdfAvailable()) {
self::markTestSkipped('wkhtmltopdf binary is not available');
}

$htmlContent = '<html><body><h1>Test PDF</h1></body></html>';
$testFilePath = $this->tempDir.'/test.html';
file_put_contents($testFilePath, $htmlContent);
Expand Down Expand Up @@ -94,8 +100,13 @@ public function testGenerateFromInvalidHtmlFile(): void
$this->wkHtmlToPdfAdapter->generateFromHtmlFile(new \SplFileInfo($this->tempDir.'/nonexistent.html'));
}

#[Group('integration')]
public function testGenerateWithAdditionalOptions(): void
{
if (!$this->isWkhtmltopdfAvailable()) {
self::markTestSkipped('wkhtmltopdf binary is not available');
}

$htmlContent = '<html><head><title>Test PDF</title></head><body><h1>Test PDF</h1></body></html>';
$testFilePath = $this->tempDir.'/test_with_options.html';
file_put_contents($testFilePath, $htmlContent);
Expand Down Expand Up @@ -140,4 +151,13 @@ public function testGenerateWithAdditionalOptions(): void

unlink($testFilePath);
}

private function isWkhtmltopdfAvailable(): bool
{
$output = null;
$returnCode = null;
exec('which wkhtmltopdf 2>/dev/null', $output, $returnCode);

return 0 === $returnCode;
}
}
4 changes: 3 additions & 1 deletion src/Core/Filesystem/SplResourceInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ final class SplResourceInfo extends \SplFileInfo
*/
public function __construct(public readonly mixed $resource)
{
parent::__construct(stream_get_meta_data($this->resource)['uri']);
$metadata = stream_get_meta_data($this->resource);
$uri = $metadata['uri'] ?? throw new \RuntimeException('Stream metadata does not contain uri.');
parent::__construct($uri);
}

public static function fromTmpFile(): self
Expand Down
52 changes: 52 additions & 0 deletions src/Core/Frontend/UriToPdf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Core\Frontend;

use KNPLabs\Snappy\Core\Backend\Adapter;
use KNPLabs\Snappy\Core\Backend\Options;
use KNPLabs\Snappy\Core\Exception\FrontendUnsupportedBackendException;
use KNPLabs\Snappy\Core\Filesystem\SplResourceInfo;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

final class UriToPdf implements Adapter\UriToPdf
{
public function __construct(private readonly Adapter $adapter, private readonly StreamFactoryInterface $streamFactory) {}

public function withOptions(callable|Options $options): static
{
return new self(
$this->adapter->withOptions($options),
$this->streamFactory
);
}

public function generateFromUri(UriInterface $uri): StreamInterface
{
if ($this->adapter instanceof Adapter\UriToPdf) {
return $this->adapter->generateFromUri($uri);
}

if ($this->adapter instanceof Adapter\HtmlFileToPdf) {
// Download URI content to temp file, delegate
$content = file_get_contents((string) $uri);

if (false === $content) {
throw new \RuntimeException(\sprintf('Failed to download URI: %s', (string) $uri));
}

$file = SplResourceInfo::fromTmpFile();
fwrite($file->resource, $content);

return $this->adapter->generateFromHtmlFile($file);
}

throw new FrontendUnsupportedBackendException(
self::class,
$this->adapter::class,
);
}
}
93 changes: 93 additions & 0 deletions src/Core/Tests/Frontend/UriToPdfTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace KNPLabs\Snappy\Core\Tests\Frontend;

use KNPLabs\Snappy\Core\Backend\Adapter;
use KNPLabs\Snappy\Core\Backend\Options;
use KNPLabs\Snappy\Core\Frontend\UriToPdf;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7\Uri;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Constraint;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

/**
* @internal
*/
#[CoversNothing]
final class UriToPdfTest extends TestCase
{
private StreamInterface $output;

private StreamFactoryInterface $streamFactory;

protected function setUp(): void
{
$this->output = self::createStub(StreamInterface::class);
$this->streamFactory = new Psr17Factory();
}

public function testWithUriToPdf(): void
{
$backend = $this->createMock(Adapter\UriToPdf::class);
$frontend = new UriToPdf($backend, $this->streamFactory);

$uri = new Uri('https://example.com');

$backend
->method('generateFromUri')
->with($uri)
->willReturn($this->output)
;

self::assertSame(
$frontend->generateFromUri($uri),
$this->output,
);
}

public function testWithHtmlFileToPdf(): void
{
$backend = $this->createMock(Adapter\HtmlFileToPdf::class);
$frontend = new UriToPdf($backend, $this->streamFactory);

$uri = new Uri('data:text/html,<html />');

$backend
->method('generateFromHtmlFile')
->with(
new Constraint\Callback(
static fn (\SplFileInfo $file): bool => '<html />' === file_get_contents($file->getPathname())
)
)
->willReturn($this->output)
;

self::assertSame(
$frontend->generateFromUri($uri),
$this->output,
);
}

public function testWithOptions(): void
{
$backend = $this->createMock(Adapter\UriToPdf::class);
$frontend = new UriToPdf($backend, $this->streamFactory);

$options = Options::create();

$backend
->method('withOptions')
->with($options)
->willReturnSelf()
;

$reconfigured = $frontend->withOptions($options);

self::assertNotSame($frontend, $reconfigured);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use KNPLabs\Snappy\Backend\WkHtmlToPdf\WkHtmlToPdfAdapter;
use KNPLabs\Snappy\Backend\WkHtmlToPdf\WkHtmlToPdfFactory;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
Expand Down Expand Up @@ -37,9 +38,10 @@ public function create(
new Definition(
WkHtmlToPdfFactory::class,
[
'$streamFactory' => $container->getDefinition(StreamFactoryInterface::class),
'$binary' => $configuration['binary'],
'$timeout' => $configuration['timeout'],
'$streamFactory' => $container->getDefinition(StreamFactoryInterface::class),
'$uriFactory' => $container->getDefinition(UriFactoryInterface::class),
]
),
)
Expand Down
5 changes: 3 additions & 2 deletions src/Framework/Symfony/DependencyInjection/SnappyExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ final class SnappyExtension extends Extension
Adapter\HtmlFileToPdf::class => Frontend\HtmlFileToPdf::class,
Adapter\HtmlToPdf::class => Frontend\HtmlToPdf::class,
Adapter\StreamToPdf::class => Frontend\StreamToPdf::class,
Adapter\UriToPdf::class => Frontend\UriToPdf::class,
];

public function load(array $configuration, ContainerBuilder $container): void
Expand Down Expand Up @@ -136,9 +137,9 @@ public function load(array $configuration, ContainerBuilder $container): void
),
)
;
}

$container->registerAliasForArgument($frontendId, $adapterClass, $backendName);
$container->registerAliasForArgument($frontendId, $frontendClass, $backendName);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

Expand All @@ -37,6 +38,11 @@ protected function setUp(): void
StreamFactoryInterface::class,
new Definition(Psr17Factory::class),
);

$this->container->setDefinition(
UriFactoryInterface::class,
new Definition(Psr17Factory::class),
);
}

public function testLoadEmptyConfiguration(): void
Expand All @@ -53,6 +59,7 @@ public function testLoadEmptyConfiguration(): void
[
'service_container',
StreamFactoryInterface::class,
UriFactoryInterface::class,
],
);
}
Expand Down Expand Up @@ -88,12 +95,14 @@ public function testDompdfBackendConfiguration(): void
[
'service_container',
StreamFactoryInterface::class,
UriFactoryInterface::class,
'knplabs.snappy.core.backend.factory.myBackend',
'knplabs.snappy.core.backend.adapter.myBackend',
'knplabs.snappy.core.frontend.domdocumenttopdf.myBackend',
'knplabs.snappy.core.frontend.htmlfiletopdf.myBackend',
'knplabs.snappy.core.frontend.htmltopdf.myBackend',
'knplabs.snappy.core.frontend.streamtopdf.myBackend',
'knplabs.snappy.core.frontend.uritopdf.myBackend',
]
);

Expand Down
Loading