diff --git a/README.md b/README.md
index 578f1dd..2570e37 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,15 @@ JavaScript is disabled by default. Enable it with `javascript()` when needed. Ca
The default viewport is 1024 × 768 pixels. Full-page capture is enabled by default, cropped to a maximum of 2048 × 10000 pixels.
+Viewport dimensions control page layout. Full-page capture sizes the image to the document without changing the viewport:
+
+| Configuration | Effect |
+| --- | --- |
+| `capture.width` / `capture.height` | Sets the viewport used to lay out the page |
+| `image.full_page` (`true`) | Captures the document within the maximum image bounds |
+| `image.full_page` (`false`) | Captures only the viewport within the maximum image bounds |
+| `image.max_width` / `image.max_height` | Crops the image without scaling it |
+
```php
use DirectoryTree\Screenshot\Facades\Screenshot;
@@ -145,6 +154,27 @@ File extensions do not select the format. Use `jpeg()` for JPEG output or `pdf()
Switching between `png()` and `jpeg()` on an image builder preserves its other settings, including JPEG quality.
+### Capturing an Element
+
+Call `element()` on an HTML, view, or URL source before selecting an output. It captures the first element matching the CSS selector:
+
+```php
+use DirectoryTree\Screenshot\Facades\Screenshot;
+
+$source = Screenshot::view('invoices.preview', ['invoice' => $invoice])
+ ->size(1200, 800)
+ ->element('#invoice');
+
+$source->png()->save($imagePath);
+$source->pdf()->save($pdfPath);
+```
+
+Images crop to the element's bounds, including padding and borders. The viewport still controls page layout. Element selection takes precedence over `fullPage()` and `viewport()`; `maxSize()` crops from the element's top-left corner.
+
+PDFs move the selected element directly into the document body and print it using the configured paper, margins, print styles, and page breaks. Document stylesheets are retained. Styles inherited from removed wrappers or selectors that depend on those wrappers may need adjustment. The package does not reset the element's layout; use print CSS to control it.
+
+Selection works with JavaScript disabled. Missing elements and invalid selectors throw exceptions. Images require an element with nonzero layout bounds; PDFs follow the document's print styles. Each output builder keeps the selector chosen when it was created.
+
### Saving and Reading Captures
Call `save()` or `store()` on a source or output builder, or capture once and reuse the result:
@@ -205,7 +235,7 @@ $source->jpeg(90)->maxSize(1600, 6000)->save($imagePath);
$source->pdf()->save($pdfPath);
```
-Select each output on the source. Each builder starts with the source's current browser settings and its output defaults. Later changes to one builder do not affect the source or other builders.
+Select each output on the source. Each builder starts with the source's current browser settings, element selector, and its output defaults. Later changes to one builder do not affect the source or other builders.
### Timeouts
@@ -270,7 +300,7 @@ Publish the [configuration file](config/screenshot.php) to set application defau
php artisan vendor:publish --tag=screenshot-config
```
-The `capture` settings apply to both images and PDFs. The `image` and `pdf` settings control their respective outputs. For example, these entries configure a shared viewport, JPEG quality, and PDF paper size:
+The `capture` settings apply to both images and PDFs. The `image` and `pdf` settings control their respective outputs. For example, these entries configure a shared viewport, full-page image bounds, JPEG quality, and PDF paper size:
```php
// config/screenshot.php
@@ -283,6 +313,9 @@ return [
],
'image' => [
+ 'full_page' => true,
+ 'max_width' => 1600,
+ 'max_height' => 6000,
'quality' => 85,
],
@@ -292,6 +325,8 @@ return [
];
```
+Images use a 1280 × 720 viewport for layout and capture the document up to 1600 × 6000 pixels. Set `image.full_page` to `false` to capture only the viewport; the maximum image bounds still apply.
+
See the [configuration file](config/screenshot.php) for all available settings. Omitted entries use the package defaults. Capture dimensions are in pixels, timeouts are in milliseconds, and PDF margins are in millimeters. Use `socket => null` for dedicated browsers.
Fluent options override defaults for an individual capture. Restart long-running workers after changing configuration.
@@ -329,12 +364,13 @@ Pass `BrowserOptions`, `ImageOptions`, or `PdfOptions` objects through the `brow
Chrome is the built-in driver. To supply your own, implement [`Driver`](src/Contracts/Driver.php) and bind it in your service provider's `register()` method, or pass it to the standalone `Screenshot` constructor. Drivers supporting `close()` should also implement [`ManagesBrowsers`](src/Contracts/ManagesBrowsers.php).
-Drivers receive `SourceType::Html` or `SourceType::Url` and handle every output through `capture(string $source, SourceType $type, CaptureOptions $options)`. Blade views are rendered to HTML first. `CaptureOptions` combines two independent objects:
+Drivers receive `SourceType::Html` or `SourceType::Url` and handle every output through `capture(string $source, SourceType $type, CaptureOptions $options)`. Blade views are rendered to HTML first. `CaptureOptions` contains:
-- `$options->browser` is a `BrowserOptions` object containing viewport, timeout, JavaScript, and socket settings.
-- `$options->output` is an `ImageOptions` or `PdfOptions` object containing output settings.
+- `$options->browser()` is a `BrowserOptions` object containing viewport, timeout, JavaScript, and socket settings.
+- `$options->output()` is an `ImageOptions` or `PdfOptions` object containing output settings.
+- `$options->element()` is the selected CSS selector, or `null` for the whole page.
-`$options->getFormat()` returns `OutputFormat::Png`, `OutputFormat::Jpeg`, or `OutputFormat::Pdf`. Return an `ImageResult` for images or a `PdfResult` for PDFs. Each driver call receives an independent snapshot of both option objects.
+`$options->format()` returns `OutputFormat::Png`, `OutputFormat::Jpeg`, or `OutputFormat::Pdf`. Return an `ImageResult` for images or a `PdfResult` for PDFs. Each driver call receives an independent snapshot of both option objects.
## Testing
@@ -353,7 +389,7 @@ Screenshot::html('
Invoice ')
Screenshot::assertCapturedCount(count: 1);
Screenshot::assertCaptured(function (string $source, SourceType $type, CaptureOptions $options) {
- return $source === 'Invoice ' && $options->browser->getWidth() === 800;
+ return $source === 'Invoice ' && $options->browser()->getWidth() === 800;
});
```
@@ -373,7 +409,7 @@ Screenshot::html('Invoice ')->pdf()->landscape()->capture();
Screenshot::assertCapturedCount(1);
Screenshot::assertCaptured(function (string $source, SourceType $type, CaptureOptions $options) {
- return $options->getFormat() === OutputFormat::Pdf && $options->output->getLandscape();
+ return $options->format() === OutputFormat::Pdf && $options->output()->getLandscape();
});
```
diff --git a/src/CaptureOptions.php b/src/CaptureOptions.php
index ab34d82..56bcfcb 100644
--- a/src/CaptureOptions.php
+++ b/src/CaptureOptions.php
@@ -10,13 +10,14 @@
class CaptureOptions
{
/**
- * Combine browser settings with the selected output settings.
+ * Combine browser and output settings with an optional element selector.
*
* @param TOutput $output
*/
public function __construct(
- public BrowserOptions $browser,
- public ImageOptions|PdfOptions $output,
+ protected BrowserOptions $browser,
+ protected ImageOptions|PdfOptions $output,
+ protected ?string $element = null,
) {}
/**
@@ -28,10 +29,36 @@ public function __clone(): void
$this->output = clone $this->output;
}
+ /**
+ * Get the browser settings.
+ */
+ public function browser(): BrowserOptions
+ {
+ return $this->browser;
+ }
+
+ /**
+ * Get the output settings.
+ *
+ * @return TOutput
+ */
+ public function output(): ImageOptions|PdfOptions
+ {
+ return $this->output;
+ }
+
+ /**
+ * Get the selected CSS selector.
+ */
+ public function element(): ?string
+ {
+ return $this->element;
+ }
+
/**
* Get the selected output format.
*/
- public function getFormat(): OutputFormat
+ public function format(): OutputFormat
{
return $this->output->getFormat();
}
diff --git a/src/Drivers/Chrome/ImageCapture.php b/src/Drivers/Chrome/ImageCapture.php
index 8d98111..cd28285 100644
--- a/src/Drivers/Chrome/ImageCapture.php
+++ b/src/Drivers/Chrome/ImageCapture.php
@@ -8,6 +8,7 @@
use DirectoryTree\Screenshot\ImageResult;
use HeadlessChromium\Clip;
use HeadlessChromium\Page;
+use RuntimeException;
class ImageCapture implements CapturesPage
{
@@ -16,23 +17,22 @@ class ImageCapture implements CapturesPage
*/
public function __construct(
protected BrowserOptions $browser,
- protected ImageOptions $options
+ protected ImageOptions $options,
+ protected ?string $element = null,
) {}
/**
- * Capture the full page or viewport within the configured limits.
+ * Capture the selected element, full page, or viewport within the limits.
*/
public function capture(Page $page): ImageResult
{
- $clip = $this->options->getFullPage()
- ? $page->getFullPageClip($this->browser->getBrowserTimeout())
- : new Clip(0, 0, $this->browser->getWidth(), $this->browser->getHeight());
+ $clip = $this->clip($page);
$parameters = [
'format' => $this->options->getFormat()->value,
'captureBeyondViewport' => true,
'clip' => new Clip(
- 0, 0,
+ $clip->getX(), $clip->getY(),
max(1, min($clip->getWidth(), $this->options->getMaxWidth())),
max(1, min($clip->getHeight(), $this->options->getMaxHeight())),
),
@@ -48,4 +48,37 @@ public function capture(Page $page): ImageResult
)
);
}
+
+ /**
+ * Resolve the selected element, full page, or viewport bounds.
+ */
+ protected function clip(Page $page): Clip
+ {
+ if ($this->element !== null) {
+ return $this->getElementClip($page);
+ }
+
+ if ($this->options->getFullPage()) {
+ return $page->getFullPageClip($this->browser->getBrowserTimeout());
+ }
+
+ return new Clip(0, 0, $this->browser->getWidth(), $this->browser->getHeight());
+ }
+
+ /**
+ * Get the element's border bounds using Chrome's DOM API.
+ */
+ protected function getElementClip(Page $page): Clip
+ {
+ $element = $page->dom()->querySelector($this->element)
+ ?? throw new RuntimeException("No element matches the selector [{$this->element}].");
+
+ $position = $element->getPosition('border');
+
+ if (! $position || $position->getWidth() <= 0 || $position->getHeight() <= 0) {
+ throw new RuntimeException("The element [{$this->element}] has no layout bounds.");
+ }
+
+ return new Clip($position->getX(), $position->getY(), $position->getWidth(), $position->getHeight());
+ }
}
diff --git a/src/Drivers/Chrome/PageCapture.php b/src/Drivers/Chrome/PageCapture.php
index b2a161b..37dd097 100644
--- a/src/Drivers/Chrome/PageCapture.php
+++ b/src/Drivers/Chrome/PageCapture.php
@@ -57,7 +57,7 @@ public function capture(string $source, SourceType $type): ImageResult|PdfResult
*/
protected function configurePage(Page $page): void
{
- $options = $this->options->browser;
+ $options = $this->options->browser();
if ($this->shared) {
$page->setViewport($options->getWidth(), $options->getHeight())->await($options->getBrowserTimeout());
@@ -78,7 +78,7 @@ protected function configurePage(Page $page): void
*/
protected function loadPage(Page $page, string $source, SourceType $type): void
{
- $options = $this->options->browser;
+ $options = $this->options->browser();
try {
if ($type === SourceType::Html) {
@@ -106,9 +106,17 @@ protected function newCapture(): CapturesPage
{
$options = $this->options;
- return match ($options->getFormat()) {
- OutputFormat::Png, OutputFormat::Jpeg => new ImageCapture($options->browser, $options->output),
- OutputFormat::Pdf => new PdfCapture($options->browser, $options->output),
+ return match ($options->format()) {
+ OutputFormat::Png, OutputFormat::Jpeg => new ImageCapture(
+ $options->browser(),
+ $options->output(),
+ $options->element()
+ ),
+ OutputFormat::Pdf => new PdfCapture(
+ $options->browser(),
+ $options->output(),
+ $options->element()
+ ),
};
}
diff --git a/src/Drivers/Chrome/PdfCapture.php b/src/Drivers/Chrome/PdfCapture.php
index 8a4f209..a94e7ee 100644
--- a/src/Drivers/Chrome/PdfCapture.php
+++ b/src/Drivers/Chrome/PdfCapture.php
@@ -6,6 +6,7 @@
use DirectoryTree\Screenshot\PdfOptions;
use DirectoryTree\Screenshot\PdfResult;
use HeadlessChromium\Page;
+use RuntimeException;
class PdfCapture implements CapturesPage
{
@@ -14,7 +15,8 @@ class PdfCapture implements CapturesPage
*/
public function __construct(
protected BrowserOptions $browser,
- protected PdfOptions $options
+ protected PdfOptions $options,
+ protected ?string $element = null,
) {}
/**
@@ -22,6 +24,10 @@ public function __construct(
*/
public function capture(Page $page): PdfResult
{
+ if ($this->element !== null) {
+ $this->isolateElement($page);
+ }
+
return new PdfResult(
$page->pdf(
$this->options->toArray()
@@ -30,4 +36,33 @@ public function capture(Page $page): PdfResult
)
);
}
+
+ /**
+ * Move the selected content into the body, preserving document stylesheets.
+ */
+ protected function isolateElement(Page $page): void
+ {
+ $selector = json_encode($this->element, JSON_THROW_ON_ERROR);
+
+ $found = $page->evaluate(<< {
+ const element = document.querySelector({$selector});
+
+ if (!element) return false;
+ if (element === document.body || element === document.documentElement) return true;
+
+ for (const stylesheet of document.querySelectorAll('body style, body link[rel~="stylesheet"]')) {
+ if (!element.contains(stylesheet)) document.head.appendChild(stylesheet);
+ }
+
+ document.body.replaceChildren(element);
+
+ return true;
+ })()
+ JS)->getReturnValue($this->browser->getBrowserTimeout());
+
+ if (! $found) {
+ throw new RuntimeException("No element matches the selector [{$this->element}].");
+ }
+ }
}
diff --git a/src/Drivers/ChromeDriver.php b/src/Drivers/ChromeDriver.php
index e2e92db..fd92550 100644
--- a/src/Drivers/ChromeDriver.php
+++ b/src/Drivers/ChromeDriver.php
@@ -34,15 +34,15 @@ public function capture(string $source, SourceType $type, CaptureOptions $option
{
$options->validate();
- if ($socket = $this->socket($options->browser)) {
+ if ($socket = $this->socket($options->browser())) {
return $socket->run(
- fn () => $this->createBrowser($options->browser),
+ fn () => $this->createBrowser($options->browser()),
fn (Browser $browser) => $this->capturePage($browser, $source, $type, $options, shared: true),
- array_merge($this->browserOptions, ['sendSyncDefaultTimeout' => $options->browser->getBrowserTimeout()]),
+ array_merge($this->browserOptions, ['sendSyncDefaultTimeout' => $options->browser()->getBrowserTimeout()]),
);
}
- return $this->capturePage($this->createBrowser($options->browser), $source, $type, $options);
+ return $this->capturePage($this->createBrowser($options->browser()), $source, $type, $options);
}
/**
diff --git a/src/PendingImage.php b/src/PendingImage.php
index bbae30b..79003eb 100644
--- a/src/PendingImage.php
+++ b/src/PendingImage.php
@@ -23,7 +23,7 @@ public function __construct(
*/
public function size(int $width, int $height): static
{
- $this->options->browser->setSize($width, $height);
+ $this->options->browser()->setSize($width, $height);
return $this;
}
@@ -33,7 +33,7 @@ public function size(int $width, int $height): static
*/
public function timeouts(?int $load = null, ?int $browser = null): static
{
- $this->options->browser->setTimeouts($load, $browser);
+ $this->options->browser()->setTimeouts($load, $browser);
return $this;
}
@@ -43,7 +43,7 @@ public function timeouts(?int $load = null, ?int $browser = null): static
*/
public function javascript(bool $value = true): static
{
- $this->options->browser->setJavascript($value);
+ $this->options->browser()->setJavascript($value);
return $this;
}
@@ -53,7 +53,7 @@ public function javascript(bool $value = true): static
*/
public function failOnTimeout(bool $value = true): static
{
- $this->options->browser->setFailOnTimeout($value);
+ $this->options->browser()->setFailOnTimeout($value);
return $this;
}
@@ -63,7 +63,7 @@ public function failOnTimeout(bool $value = true): static
*/
public function socket(string $key): static
{
- $this->options->browser->setSocket($key);
+ $this->options->browser()->setSocket($key);
return $this;
}
@@ -73,7 +73,7 @@ public function socket(string $key): static
*/
public function withoutSocket(): static
{
- $this->options->browser->setSocket(null);
+ $this->options->browser()->setSocket(null);
return $this;
}
@@ -83,7 +83,7 @@ public function withoutSocket(): static
*/
public function maxSize(int $width, int $height): static
{
- $this->options->output->setMaxSize($width, $height);
+ $this->options->output()->setMaxSize($width, $height);
return $this;
}
@@ -93,7 +93,7 @@ public function maxSize(int $width, int $height): static
*/
public function fullPage(bool $value = true): static
{
- $this->options->output->setFullPage($value);
+ $this->options->output()->setFullPage($value);
return $this;
}
@@ -111,7 +111,7 @@ public function viewport(): static
*/
public function png(): static
{
- $this->options->output->setFormat(OutputFormat::Png);
+ $this->options->output()->setFormat(OutputFormat::Png);
return $this;
}
@@ -121,7 +121,7 @@ public function png(): static
*/
public function jpeg(?int $quality = null): static
{
- $this->options->output->setFormat(OutputFormat::Jpeg, $quality);
+ $this->options->output()->setFormat(OutputFormat::Jpeg, $quality);
return $this;
}
diff --git a/src/PendingPdf.php b/src/PendingPdf.php
index d211606..4ae60de 100644
--- a/src/PendingPdf.php
+++ b/src/PendingPdf.php
@@ -23,7 +23,7 @@ public function __construct(
*/
public function size(int $width, int $height): static
{
- $this->options->browser->setSize($width, $height);
+ $this->options->browser()->setSize($width, $height);
return $this;
}
@@ -33,7 +33,7 @@ public function size(int $width, int $height): static
*/
public function timeouts(?int $load = null, ?int $browser = null): static
{
- $this->options->browser->setTimeouts($load, $browser);
+ $this->options->browser()->setTimeouts($load, $browser);
return $this;
}
@@ -43,7 +43,7 @@ public function timeouts(?int $load = null, ?int $browser = null): static
*/
public function javascript(bool $value = true): static
{
- $this->options->browser->setJavascript($value);
+ $this->options->browser()->setJavascript($value);
return $this;
}
@@ -53,7 +53,7 @@ public function javascript(bool $value = true): static
*/
public function failOnTimeout(bool $value = true): static
{
- $this->options->browser->setFailOnTimeout($value);
+ $this->options->browser()->setFailOnTimeout($value);
return $this;
}
@@ -63,7 +63,7 @@ public function failOnTimeout(bool $value = true): static
*/
public function socket(string $key): static
{
- $this->options->browser->setSocket($key);
+ $this->options->browser()->setSocket($key);
return $this;
}
@@ -73,7 +73,7 @@ public function socket(string $key): static
*/
public function withoutSocket(): static
{
- $this->options->browser->setSocket(null);
+ $this->options->browser()->setSocket(null);
return $this;
}
@@ -83,7 +83,7 @@ public function withoutSocket(): static
*/
public function paper(PaperSize $size): static
{
- $this->options->output->setPaper($size);
+ $this->options->output()->setPaper($size);
return $this;
}
@@ -93,7 +93,7 @@ public function paper(PaperSize $size): static
*/
public function paperSize(float $width, float $height): static
{
- $this->options->output->setPaperSize($width, $height);
+ $this->options->output()->setPaperSize($width, $height);
return $this;
}
@@ -103,7 +103,7 @@ public function paperSize(float $width, float $height): static
*/
public function landscape(bool $value = true): static
{
- $this->options->output->setLandscape($value);
+ $this->options->output()->setLandscape($value);
return $this;
}
@@ -113,7 +113,7 @@ public function landscape(bool $value = true): static
*/
public function margins(float $top = 10, float $right = 10, float $bottom = 10, float $left = 10): static
{
- $this->options->output->setMargins($top, $right, $bottom, $left);
+ $this->options->output()->setMargins($top, $right, $bottom, $left);
return $this;
}
@@ -123,7 +123,7 @@ public function margins(float $top = 10, float $right = 10, float $bottom = 10,
*/
public function background(bool $value = true): static
{
- $this->options->output->setBackground($value);
+ $this->options->output()->setBackground($value);
return $this;
}
@@ -133,7 +133,7 @@ public function background(bool $value = true): static
*/
public function preferCssPageSize(bool $value = true): static
{
- $this->options->output->setPreferCssPageSize($value);
+ $this->options->output()->setPreferCssPageSize($value);
return $this;
}
diff --git a/src/PendingSource.php b/src/PendingSource.php
index 473cdfd..3a8d350 100644
--- a/src/PendingSource.php
+++ b/src/PendingSource.php
@@ -4,6 +4,11 @@
class PendingSource
{
+ /**
+ * The CSS selector to capture from the source.
+ */
+ protected ?string $element = null;
+
/**
* Prepare a source with independent browser settings and shared output defaults.
*/
@@ -26,6 +31,16 @@ public function size(int $width, int $height): static
return $this;
}
+ /**
+ * Capture the first element matching the given CSS selector.
+ */
+ public function element(string $selector): static
+ {
+ $this->element = $selector;
+
+ return $this;
+ }
+
/**
* Set page-load and individual browser-command timeouts in milliseconds.
*/
@@ -81,7 +96,10 @@ public function withoutSocket(): static
*/
public function png(): PendingImage
{
- return (new PendingImage($this->capture, new CaptureOptions($this->browser, $this->image)))->png();
+ return (new PendingImage(
+ $this->capture,
+ $this->newCaptureOptions($this->image),
+ ))->png();
}
/**
@@ -89,7 +107,10 @@ public function png(): PendingImage
*/
public function jpeg(?int $quality = null): PendingImage
{
- return (new PendingImage($this->capture, new CaptureOptions($this->browser, $this->image)))->jpeg($quality);
+ return (new PendingImage(
+ $this->capture,
+ $this->newCaptureOptions($this->image),
+ ))->jpeg($quality);
}
/**
@@ -97,7 +118,10 @@ public function jpeg(?int $quality = null): PendingImage
*/
public function pdf(): PendingPdf
{
- return new PendingPdf($this->capture, new CaptureOptions($this->browser, $this->pdf));
+ return new PendingPdf(
+ $this->capture,
+ $this->newCaptureOptions($this->pdf),
+ );
}
/**
@@ -121,6 +145,22 @@ public function store(string $path, ?string $disk = null, array $options = []):
*/
public function capture(): ImageResult
{
- return (new PendingImage($this->capture, new CaptureOptions($this->browser, $this->image)))->capture();
+ return (new PendingImage(
+ $this->capture,
+ $this->newCaptureOptions($this->image),
+ ))->capture();
+ }
+
+ /**
+ * Combine the source's browser settings and selector with the output options.
+ *
+ * @template TOutput of ImageOptions|PdfOptions
+ *
+ * @param TOutput $output
+ * @return CaptureOptions
+ */
+ protected function newCaptureOptions(ImageOptions|PdfOptions $output): CaptureOptions
+ {
+ return new CaptureOptions($this->browser, $output, $this->element);
}
}
diff --git a/src/Testing/ScreenshotFake.php b/src/Testing/ScreenshotFake.php
index a7a5315..1024641 100644
--- a/src/Testing/ScreenshotFake.php
+++ b/src/Testing/ScreenshotFake.php
@@ -73,7 +73,7 @@ public function capture(string $source, SourceType $type, CaptureOptions $option
{
$this->captures[] = [$source, $type, clone $options];
- if ($options->getFormat() === OutputFormat::Pdf) {
+ if ($options->format() === OutputFormat::Pdf) {
return $this->result instanceof PdfResult
? $this->result
: new PdfResult(base64_encode(file_get_contents(__DIR__.'/../../resources/blank.pdf')));
diff --git a/tests/Browser/ElementTest.php b/tests/Browser/ElementTest.php
new file mode 100644
index 0000000..464b43e
--- /dev/null
+++ b/tests/Browser/ElementTest.php
@@ -0,0 +1,250 @@
+group('browser');
+
+test('element images include borders and padding beyond the viewport', function ($format, $fullPage) {
+ $source = (new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ )))->html(<<<'HTML'
+
+ Selected card
+ Other card
+
+ HTML)->size(400, 300)->element('.card[data-label=\'a"b\']');
+
+ $image = ($format === OutputFormat::Png ? $source->png() : $source->jpeg())
+ ->fullPage($fullPage)
+ ->capture();
+
+ expect($image->width())->toBe(230);
+ expect($image->height())->toBe(130);
+ expect($image->mimeType())->toBe($format === OutputFormat::Png ? 'image/png' : 'image/jpeg');
+
+ $bounded = $source->png()->maxSize(120, 80)->capture();
+
+ expect($bounded->width())->toBe(120);
+ expect($bounded->height())->toBe(80);
+})->with([OutputFormat::Png, OutputFormat::Jpeg])->with([true, false]);
+
+test('element selection uses the first match and the current JavaScript layout', function () {
+ $result = (new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ )))->html(<<<'HTML'
+
+
+
+ HTML)->javascript()->element('.card')->capture();
+
+ expect($result->width())->toBe(250);
+ expect($result->height())->toBe(100);
+});
+
+test('missing elements fail clearly for images and PDFs', function ($pdf) {
+ $factory = new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ ));
+ $source = $factory->html('Content
')->element('#missing');
+
+ expect(fn () => ($pdf ? $source->pdf() : $source->png())->capture())
+ ->toThrow(RuntimeException::class, 'No element matches the selector [#missing].');
+})->with(['image' => false, 'PDF' => true]);
+
+test('image elements need layout bounds', function ($html, $exception, $message) {
+ $factory = new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ ));
+
+ expect(fn () => $factory->html($html)->element('#invoice')->capture())
+ ->toThrow($exception, $message);
+})->with([
+ 'hidden' => ['Hidden
', DomException::class, 'Could not compute box model'],
+ 'empty' => ['
', RuntimeException::class, 'The element [#invoice] has no layout bounds.'],
+]);
+
+test('invalid CSS selectors propagate the browser error', function ($pdf, $exception) {
+ $factory = new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ ));
+ $source = $factory->html('Content
')->element('[');
+
+ expect(fn () => ($pdf ? $source->pdf() : $source->png())->capture())->toThrow($exception);
+})->with([
+ 'image' => [false, DomException::class],
+ 'PDF' => [true, JavascriptException::class],
+]);
+
+test('element PDFs paginate selected content using the configured paper and margins', function () {
+ $source = (new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ )))->html(<<<'HTML'
+
+
+
+
+
+
+ HTML)->element('#invoice');
+
+ $pdf = $source->pdf()->paperSize(100, 150)->margins(5, 5, 5, 5)->capture();
+
+ expect(preg_match_all('~/Type\s*/Page\b~', $pdf->contents()))->toBe(2);
+ expect($pdf->contents())->toContain('/Type /Font');
+ expect($pdf->contents())->not->toContain('/Subtype /Image');
+
+ preg_match('~/MediaBox\s*\[\s*0\s+0\s+([\d.]+)\s+([\d.]+)\s*\]~', $pdf->contents(), $matches);
+ [$width, $height] = [(float) $matches[1], (float) $matches[2]];
+
+ expect($width)->toEqualWithDelta(100 / 25.4 * 72, 1);
+ expect($height)->toEqualWithDelta(150 / 25.4 * 72, 1);
+
+ $image = $source->png()->capture();
+
+ expect($image->width())->toBe(200);
+ expect($image->height())->toBe(3000);
+});
+
+test('PDF isolation preserves document stylesheets and element state without correcting layout', function () {
+ $browser = (new BrowserFactory(getenv('CHROME_PATH') ?: null))->createBrowser([
+ 'noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1',
+ ]);
+
+ try {
+ $page = $browser->createPage();
+ $page->getSession()->sendMessageSync(new Message('Emulation.setScriptExecutionDisabled', ['value' => true]));
+ $page->setHtml(<<<'HTML'
+
+
+
+ Invoice details
Screen only
+
+
+
+
+
+
+
+ HTML);
+ $page->evaluate("document.querySelector('input').value = 'Current value'")->getReturnValue();
+
+ (new PdfCapture(new BrowserOptions, new PdfOptions, '#invoice'))->capture($page);
+
+ $page->getSession()->sendMessageSync(new Message('Emulation.setEmulatedMedia', ['media' => 'print']));
+ $layout = $page->evaluate(<<<'JS'
+ (() => {
+ const element = document.querySelector('#invoice');
+ return {
+ parent: element.parentElement.tagName,
+ color: getComputedStyle(element).color,
+ border: getComputedStyle(element).borderTopWidth,
+ margin: getComputedStyle(element).marginTop,
+ padding: getComputedStyle(element).paddingTop,
+ weight: getComputedStyle(element.querySelector('p')).fontWeight,
+ value: element.querySelector('input').value,
+ wrappers: document.querySelectorAll('.shell, header, footer').length,
+ stylesheets: document.head.querySelectorAll('style, link[rel="stylesheet"]').length,
+ text: document.body.innerText,
+ };
+ })()
+ JS)->getReturnValue();
+
+ expect($layout['parent'])->toBe('BODY');
+ expect($layout['color'])->toBe('rgb(0, 0, 255)');
+ expect($layout['border'])->toBe('0px');
+ expect($layout['margin'])->toBe('50px');
+ expect($layout['padding'])->toBe('7px');
+ expect($layout['weight'])->toBe('700');
+ expect($layout['value'])->toBe('Current value');
+ expect($layout['wrappers'])->toBe(0);
+ expect($layout['stylesheets'])->toBe(3);
+ expect($layout['text'])->toContain('Invoice details');
+ expect($layout['text'])->not->toContain('Unrelated', 'Screen only');
+ } finally {
+ $browser->close();
+ }
+});
+
+test('PDF isolation accepts the body and document element', function ($selector) {
+ $factory = new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ ));
+ $result = $factory->html('Whole document ')->element($selector)->pdf()->capture();
+
+ expect($result->mimeType())->toBe('application/pdf');
+ expect($result->contents())->toContain('/Type /Font');
+})->with(['body', 'html']);
+
+test('PDF element visibility follows print styles', function () {
+ $factory = new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ ));
+ $source = $factory->html(<<<'HTML'
+
+ Print-only invoice
+ HTML)->element('#invoice');
+
+ expect($source->pdf()->capture()->mimeType())->toBe('application/pdf');
+ expect(fn () => $source->png()->capture())
+ ->toThrow(DomException::class, 'Could not compute box model');
+});
+
+test('URL sources can select elements for both image and PDF captures', function () {
+ $socket = stream_socket_server('tcp://127.0.0.1:0');
+ $address = stream_socket_get_name($socket, false);
+ fclose($socket);
+ $server = new Process([PHP_BINARY, '-S', $address, __DIR__.'/../Fixtures/server.php']);
+ $server->start();
+
+ try {
+ $server->waitUntil(fn ($type, $output) => str_contains($output, 'Development Server'));
+ $source = (new Screenshot(new ChromeDriver(
+ getenv('CHROME_PATH') ?: null,
+ ['noSandbox' => getenv('SCREENSHOT_NO_SANDBOX') === '1'],
+ )))->url('http://'.$address)->element('.card');
+ $image = $source->png()->capture();
+ $pdf = $source->pdf()->capture();
+
+ expect($image->width())->toBe(544);
+ expect($image->height())->toBeLessThan(960);
+ expect(preg_match_all('~/Type\s*/Page\b~', $pdf->contents()))->toBe(1);
+ } finally {
+ $server->stop();
+ }
+});
diff --git a/tests/Browser/SharedBrowserTest.php b/tests/Browser/SharedBrowserTest.php
index 7f4037f..aac8a01 100644
--- a/tests/Browser/SharedBrowserTest.php
+++ b/tests/Browser/SharedBrowserTest.php
@@ -183,6 +183,32 @@ function sharedScreenshot(): Screenshot
expect(file_exists($second))->toBeTrue();
});
+test('element capture failures and PDF preparation do not leak into shared browser pages', function () {
+ $factory = sharedScreenshot();
+ $source = $factory->html('Invoice
')
+ ->size(400, 300)
+ ->socket('elements');
+ $wholePage = $source->png();
+
+ expect(fn () => $source->element('#missing')->pdf()->capture())
+ ->toThrow(RuntimeException::class, 'No element matches');
+
+ $source->element('#invoice');
+
+ expect($source->pdf()->capture()->mimeType())->toBe('application/pdf');
+ expect($source->png()->capture()->height())->toBe(100);
+ expect($wholePage->capture()->height())->toBeGreaterThan(900);
+
+ $uri = file_get_contents(BrowserSocket::named('elements', temporaryDirectory())->path());
+ $browser = BrowserFactory::connectToBrowser($uri);
+
+ try {
+ expect(count($browser->getPages()))->toBeLessThanOrEqual(1);
+ } finally {
+ $browser->getConnection()->disconnect();
+ }
+});
+
test('failed page initialization does not leak tabs or close another workers page', function () {
$factory = sharedScreenshot();
diff --git a/tests/Feature/ScreenshotTest.php b/tests/Feature/ScreenshotTest.php
index 79d60cd..de7e20b 100644
--- a/tests/Feature/ScreenshotTest.php
+++ b/tests/Feature/ScreenshotTest.php
@@ -21,7 +21,7 @@
$driver->shouldReceive('capture')
->once()
- ->withArgs(fn ($source, $type, $options) => $source === 'hello' && $options->browser->getWidth() === 900)
+ ->withArgs(fn ($source, $type, $options) => $source === 'hello' && $options->browser()->getWidth() === 900)
->andReturn(new ImageResult(imageFixture()));
app()->instance(Driver::class, $driver);
@@ -45,8 +45,8 @@
Screenshot::assertCaptured(
fn ($source, $type, $options) => str_contains($source, '<Steve>')
&& $type === SourceType::Html
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 85,
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 85,
);
Screenshot::assertCapturedCount(1);
});
@@ -134,11 +134,11 @@
Screenshot::assertCaptured(
fn ($source, $type, $options) => $source === 'shared'
- && $options->browser->getSocket() === 'emails',
+ && $options->browser()->getSocket() === 'emails',
);
Screenshot::assertCaptured(
fn ($source, $type, $options) => $source === 'dedicated'
- && $options->browser->getSocket() === null,
+ && $options->browser()->getSocket() === null,
);
Screenshot::assertCapturedCount(2);
@@ -151,7 +151,7 @@
Screenshot::assertCaptured(
fn ($source, $type, $options) => $type === SourceType::Url
- && $options->browser->getSocket() === 'emails',
+ && $options->browser()->getSocket() === 'emails',
);
});
@@ -225,10 +225,10 @@
expect(Storage::disk('pdfs')->get('invoice.pdf'))->toBe($result->contents());
expect(Storage::disk('pdfs')->getVisibility('invoice.pdf'))->toBe('private');
Screenshot::assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
&& str_contains($source, '<Steve>')
- && $options->browser->getSocket() === 'invoices'
- && $options->output->getPaperWidth() === 215.9,
+ && $options->browser()->getSocket() === 'invoices'
+ && $options->output()->getPaperWidth() === 215.9,
);
Screenshot::assertCapturedCount(1);
@@ -262,7 +262,7 @@
$fake->assertCapturedCount(6);
})->with(['image' => false, 'PDF' => true])->with(['positional' => false, 'named' => true]);
-test('fake assertions receive the source enum for every source and output format', function ($source, $format) {
+test('fake assertions receive the source type and element selector for every source and output format', function ($source, $format) {
Screenshot::fake();
$pending = match ($source) {
@@ -271,6 +271,8 @@
'view' => Screenshot::view('card', ['name' => 'Steve']),
};
+ $pending->element('#invoice');
+
(match ($format) {
OutputFormat::Png => $pending->png(),
OutputFormat::Jpeg => $pending->jpeg(),
@@ -281,7 +283,8 @@
$expected = $source === 'url' ? SourceType::Url : SourceType::Html;
$callback = fn (string $contents, SourceType $type, CaptureOptions $options) => $type === $expected
- && $options->getFormat() === $format;
+ && $options->format() === $format
+ && $options->element() === '#invoice';
Screenshot::assertCaptured($callback);
})->with(['html', 'url', 'view'])->with(OutputFormat::cases());
@@ -317,39 +320,39 @@
Screenshot::html('next')->capture();
Screenshot::assertCaptured(function ($source, $type, CaptureOptions $options) {
- if ($options->getFormat() !== OutputFormat::Pdf) {
+ if ($options->format() !== OutputFormat::Pdf) {
return false;
}
- expect($options->browser->getWidth())->toBe(900);
- expect($options->browser->getHeight())->toBe(600);
- expect($options->browser->getLoadTimeout())->toBe(2000);
- expect($options->browser->getBrowserTimeout())->toBe(4000);
- expect($options->browser->getJavascript())->toBeTrue();
- expect($options->browser->getFailOnTimeout())->toBeTrue();
- expect($options->browser->getSocket())->toBe('documents');
- expect($options->output->getPaperWidth())->toBe(215.9);
- expect($options->output->getPaperHeight())->toBe(279.4);
- expect($options->output->getLandscape())->toBeTrue();
- expect($options->output->getMargins())->toBe(['top' => 1.0, 'right' => 2.0, 'bottom' => 3.0, 'left' => 4.0]);
- expect($options->output->getBackground())->toBeFalse();
- expect($options->output->getPreferCssPageSize())->toBeTrue();
+ expect($options->browser()->getWidth())->toBe(900);
+ expect($options->browser()->getHeight())->toBe(600);
+ expect($options->browser()->getLoadTimeout())->toBe(2000);
+ expect($options->browser()->getBrowserTimeout())->toBe(4000);
+ expect($options->browser()->getJavascript())->toBeTrue();
+ expect($options->browser()->getFailOnTimeout())->toBeTrue();
+ expect($options->browser()->getSocket())->toBe('documents');
+ expect($options->output()->getPaperWidth())->toBe(215.9);
+ expect($options->output()->getPaperHeight())->toBe(279.4);
+ expect($options->output()->getLandscape())->toBeTrue();
+ expect($options->output()->getMargins())->toBe(['top' => 1.0, 'right' => 2.0, 'bottom' => 3.0, 'left' => 4.0]);
+ expect($options->output()->getBackground())->toBeFalse();
+ expect($options->output()->getPreferCssPageSize())->toBeTrue();
return true;
});
Screenshot::assertCaptured(
fn ($source, $type, $options) => $source === 'invoice'
- && $options->getFormat() === OutputFormat::Png
- && $options->browser->getWidth() === 320,
+ && $options->format() === OutputFormat::Png
+ && $options->browser()->getWidth() === 320,
);
Screenshot::assertCaptured(
fn ($source, $type, $options) => $source === 'next'
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->browser->getWidth() === 900
- && $options->output->getQuality() === 92
- && $options->output->getMaxWidth() === 500
- && $options->output->getMaxHeight() === 700
- && ! $options->output->getFullPage(),
+ && $options->format() === OutputFormat::Jpeg
+ && $options->browser()->getWidth() === 900
+ && $options->output()->getQuality() === 92
+ && $options->output()->getMaxWidth() === 500
+ && $options->output()->getMaxHeight() === 700
+ && ! $options->output()->getFullPage(),
);
});
@@ -366,15 +369,15 @@
Screenshot::html('pdf')->pdf()->capture();
Screenshot::assertCaptured(
- fn ($source, $type, $options) => $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 768
- && $options->output->getQuality() === 95
- && $options->output->getFullPage(),
+ fn ($source, $type, $options) => $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 768
+ && $options->output()->getQuality() === 95
+ && $options->output()->getFullPage(),
);
Screenshot::assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 210.0
- && $options->output->getMargins() === ['top' => 5.0, 'right' => 10.0, 'bottom' => 10.0, 'left' => 10.0],
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 210.0
+ && $options->output()->getMargins() === ['top' => 5.0, 'right' => 10.0, 'bottom' => 10.0, 'left' => 10.0],
);
});
@@ -390,10 +393,10 @@
Screenshot::html('landscape')->pdf()->capture();
Screenshot::assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 148.0
- && $options->output->getLandscape()
- && $options->output->getMargins()['left'] === 80.0,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 148.0
+ && $options->output()->getLandscape()
+ && $options->output()->getMargins()['left'] === 80.0,
);
});
diff --git a/tests/Unit/ChromeCaptureTest.php b/tests/Unit/ChromeCaptureTest.php
index 7c47c1c..66c8fbb 100644
--- a/tests/Unit/ChromeCaptureTest.php
+++ b/tests/Unit/ChromeCaptureTest.php
@@ -10,6 +10,9 @@
use DirectoryTree\Screenshot\PdfOptions;
use DirectoryTree\Screenshot\PdfResult;
use HeadlessChromium\Clip;
+use HeadlessChromium\Dom\Dom;
+use HeadlessChromium\Dom\Node;
+use HeadlessChromium\Dom\NodePosition;
use HeadlessChromium\Page;
use HeadlessChromium\PageUtils\PagePdf;
use HeadlessChromium\PageUtils\PageScreenshot;
@@ -97,3 +100,32 @@
expect((new PdfCapture($browser, $options))->capture($page))->toBeInstanceOf(PdfResult::class);
});
+
+test('element image bounds retain their document coordinates when capped', function ($fullPage) {
+ $page = Mockery::mock(Page::class);
+ $dom = Mockery::mock(Dom::class);
+ $element = Mockery::mock(Node::class);
+ $image = Mockery::mock(PageScreenshot::class);
+ $browser = (new BrowserOptions)->setTimeouts(browser: 1234);
+ $options = (new ImageOptions)->setFullPage($fullPage)->setMaxSize(120, 80);
+
+ $page->shouldNotReceive('getFullPageClip');
+ $page->shouldNotReceive('evaluate');
+ $page->shouldReceive('dom')->once()->andReturn($dom);
+ $dom->shouldReceive('querySelector')->once()->with('#invoice')->andReturn($element);
+ $element->shouldReceive('getPosition')->once()->with('border')
+ ->andReturn(new NodePosition([90, 900, 320, 900, 320, 1030, 90, 1030]));
+
+ $page->shouldReceive('screenshot')->once()
+ ->withArgs(function ($parameters) {
+ expect($parameters['clip']->getX())->toBe(90.0);
+ expect($parameters['clip']->getY())->toBe(900.0);
+ expect($parameters['clip']->getWidth())->toBe(120);
+ expect($parameters['clip']->getHeight())->toBe(80);
+
+ return true;
+ })->andReturn($image);
+ $image->shouldReceive('getBase64')->once()->with(1234)->andReturn(imageFixture());
+
+ expect((new ImageCapture($browser, $options, '#invoice'))->capture($page))->toBeInstanceOf(ImageResult::class);
+})->with(['full page' => true, 'viewport' => false]);
diff --git a/tests/Unit/ConfigurationTest.php b/tests/Unit/ConfigurationTest.php
index 39be71e..30e1e32 100644
--- a/tests/Unit/ConfigurationTest.php
+++ b/tests/Unit/ConfigurationTest.php
@@ -27,23 +27,23 @@
$pending->pdf()->capture();
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() === OutputFormat::Png
- && $options->browser->getWidth() === 900
- && $options->browser->getSocket() === 'captures'
- && ! $options->output->getFullPage(),
+ fn ($source, $type, CaptureOptions $options) => $options->format() === OutputFormat::Png
+ && $options->browser()->getWidth() === 900
+ && $options->browser()->getSocket() === 'captures'
+ && ! $options->output()->getFullPage(),
);
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() === OutputFormat::Jpeg
- && $options->browser->getLoadTimeout() === 2000
- && $options->browser->getBrowserTimeout() === 90000
- && $options->output->getQuality() === 85,
+ fn ($source, $type, CaptureOptions $options) => $options->format() === OutputFormat::Jpeg
+ && $options->browser()->getLoadTimeout() === 2000
+ && $options->browser()->getBrowserTimeout() === 90000
+ && $options->output()->getQuality() === 85,
);
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 900
- && $options->browser->getSocket() === 'captures'
- && $options->output->getPaperWidth() === 215.9
- && $options->output->getLandscape(),
+ fn ($source, $type, CaptureOptions $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 900
+ && $options->browser()->getSocket() === 'captures'
+ && $options->output()->getPaperWidth() === 215.9
+ && $options->output()->getLandscape(),
);
});
@@ -83,32 +83,32 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'override'
- && $options->browser->getWidth() === 1024
- && $options->browser->getHeight() === 768
- && $options->browser->getSocket() === null
- && ! $options->browser->getJavascript()
- && ! $options->browser->getFailOnTimeout()
- && $options->browser->getLoadTimeout() === 15000
- && $options->browser->getBrowserTimeout() === 90000
- && $options->output->getQuality() === 80
- && $options->output->getFullPage(),
+ && $options->browser()->getWidth() === 1024
+ && $options->browser()->getHeight() === 768
+ && $options->browser()->getSocket() === null
+ && ! $options->browser()->getJavascript()
+ && ! $options->browser()->getFailOnTimeout()
+ && $options->browser()->getLoadTimeout() === 15000
+ && $options->browser()->getBrowserTimeout() === 90000
+ && $options->output()->getQuality() === 80
+ && $options->output()->getFullPage(),
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getSocket() === null
- && ! $options->browser->getJavascript()
- && $options->output->getPaperWidth() === 210.0
- && ! $options->output->getLandscape()
- && ! $options->output->getBackground()
- && ! $options->output->getPreferCssPageSize()
- && $options->output->getMargins() === ['top' => 1.0, 'right' => 2.0, 'bottom' => 3.0, 'left' => 4.0],
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getSocket() === null
+ && ! $options->browser()->getJavascript()
+ && $options->output()->getPaperWidth() === 210.0
+ && ! $options->output()->getLandscape()
+ && ! $options->output()->getBackground()
+ && ! $options->output()->getPreferCssPageSize()
+ && $options->output()->getMargins() === ['top' => 1.0, 'right' => 2.0, 'bottom' => 3.0, 'left' => 4.0],
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'default'
- && $options->browser->getWidth() === 1280
- && $options->browser->getSocket() === 'defaults'
- && $options->output->getQuality() === 95
- && ! $options->output->getFullPage(),
+ && $options->browser()->getWidth() === 1280
+ && $options->browser()->getSocket() === 'defaults'
+ && $options->output()->getQuality() === 95
+ && ! $options->output()->getFullPage(),
);
});
@@ -136,19 +136,19 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'latest'
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 70
- && $options->browser->getWidth() === 640
- && $options->browser->getHeight() === 480
- && $options->output->getMaxWidth() === 1200
- && $options->output->getMaxHeight() === 1600
- && ! $options->output->getFullPage()
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 2000,
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 70
+ && $options->browser()->getWidth() === 640
+ && $options->browser()->getHeight() === 480
+ && $options->output()->getMaxWidth() === 1200
+ && $options->output()->getMaxHeight() === 1600
+ && ! $options->output()->getFullPage()
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 2000,
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'default quality'
- && $options->output->getQuality() === 95,
+ && $options->output()->getQuality() === 95,
);
});
@@ -178,24 +178,24 @@
$source->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 640
- && $options->browser->getHeight() === 480
- && $options->browser->getJavascript()
- && $options->browser->getSocket() === 'shared'
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 4000
- && $options->browser->getFailOnTimeout()
- && $options->output->getPaperWidth() === 148.0,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 640
+ && $options->browser()->getHeight() === 480
+ && $options->browser()->getJavascript()
+ && $options->browser()->getSocket() === 'shared'
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 4000
+ && $options->browser()->getFailOnTimeout()
+ && $options->output()->getPaperWidth() === 148.0,
);
- $fake->assertCaptured(fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 215.9);
+ $fake->assertCaptured(fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 215.9);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->browser->getWidth() === 320
- && $options->browser->getSocket() === null
- && ! $options->browser->getJavascript()
- && $options->output->getMaxWidth() === 100
- && $options->output->getQuality() === 95,
+ fn ($source, $type, $options) => $options->browser()->getWidth() === 320
+ && $options->browser()->getSocket() === null
+ && ! $options->browser()->getJavascript()
+ && $options->output()->getMaxWidth() === 100
+ && $options->output()->getQuality() === 95,
);
});
@@ -217,14 +217,14 @@
$fake->html('next')->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->browser->getWidth() === 900
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 85,
+ fn ($source, $type, $options) => $options->browser()->getWidth() === 900
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 85,
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 900
- && $options->output->getPaperWidth() === 215.9,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 900
+ && $options->output()->getPaperWidth() === 215.9,
);
$fake->assertCapturedCount(3);
@@ -233,8 +233,8 @@
$other->html('default')->capture();
$other->assertCaptured(
- fn ($source, $type, $options) => $options->browser->getWidth() === 1024
- && $options->getFormat() === OutputFormat::Png,
+ fn ($source, $type, $options) => $options->browser()->getWidth() === 1024
+ && $options->format() === OutputFormat::Png,
);
});
@@ -246,8 +246,8 @@
$pending->size(900, 600)->capture();
$pending->capture();
- $fake->assertCaptured(fn ($source, $type, $options) => $options->browser->getWidth() === 800);
- $fake->assertCaptured(fn ($source, $type, $options) => $options->browser->getWidth() === 900);
+ $fake->assertCaptured(fn ($source, $type, $options) => $options->browser()->getWidth() === 800);
+ $fake->assertCaptured(fn ($source, $type, $options) => $options->browser()->getWidth() === 900);
$fake->assertCapturedCount(3);
});
@@ -263,8 +263,8 @@
->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 30.0
- && $options->output->getMargins()['top'] === 0.0,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 30.0
+ && $options->output()->getMargins()['top'] === 0.0,
);
});
diff --git a/tests/Unit/PaperSizeTest.php b/tests/Unit/PaperSizeTest.php
index 7a7a15f..0a8cabf 100644
--- a/tests/Unit/PaperSizeTest.php
+++ b/tests/Unit/PaperSizeTest.php
@@ -16,9 +16,9 @@
->capture();
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === $width
- && $options->output->getPaperHeight() === $height,
+ fn ($source, $type, CaptureOptions $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === $width
+ && $options->output()->getPaperHeight() === $height,
);
})->with([
'A3' => [PaperSize::A3, 297.0, 420.0],
diff --git a/tests/Unit/PdfTest.php b/tests/Unit/PdfTest.php
index 02136c0..5521a41 100644
--- a/tests/Unit/PdfTest.php
+++ b/tests/Unit/PdfTest.php
@@ -39,25 +39,25 @@ function pdfFixture(): string
->capture();
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() === OutputFormat::Pdf
+ fn ($source, $type, CaptureOptions $options) => $options->format() === OutputFormat::Pdf
&& $source === 'invoice'
&& $type === SourceType::Html
- && $options->browser->getSocket() === 'invoices'
- && $options->browser->getLoadTimeout() === 5000
- && $options->browser->getBrowserTimeout() === 10000
- && $options->output->getPaperWidth() === 215.9
- && $options->output->getLandscape()
- && $options->output->getMargins() === ['top' => 5.0, 'right' => 6.0, 'bottom' => 7.0, 'left' => 8.0]
- && ! $options->output->getBackground()
- && $options->output->getPreferCssPageSize(),
+ && $options->browser()->getSocket() === 'invoices'
+ && $options->browser()->getLoadTimeout() === 5000
+ && $options->browser()->getBrowserTimeout() === 10000
+ && $options->output()->getPaperWidth() === 215.9
+ && $options->output()->getLandscape()
+ && $options->output()->getMargins() === ['top' => 5.0, 'right' => 6.0, 'bottom' => 7.0, 'left' => 8.0]
+ && ! $options->output()->getBackground()
+ && $options->output()->getPreferCssPageSize(),
);
$fake->assertCapturedCount(1);
$image->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->browser->getSocket() === 'images'
- && $options->getFormat() === OutputFormat::Jpeg,
+ fn ($source, $type, $options) => $options->browser()->getSocket() === 'images'
+ && $options->format() === OutputFormat::Jpeg,
);
});
@@ -77,28 +77,28 @@ function pdfFixture(): string
$second->capture();
$fake->assertCaptured(function ($source, $type, $options) {
- if ($options->getFormat() !== OutputFormat::Pdf) {
+ if ($options->format() !== OutputFormat::Pdf) {
return false;
}
- $options->output->setPaper(PaperSize::Legal);
- $options->browser->setSocket('mutated');
+ $options->output()->setPaper(PaperSize::Legal);
+ $options->browser()->setSocket('mutated');
return true;
});
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 100.0
- && $options->browser->getSocket() === null
- && $options->browser->getJavascript()
- && $options->browser->getFailOnTimeout()
- && $options->browser->getBrowserTimeout() === 2000,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 100.0
+ && $options->browser()->getSocket() === null
+ && $options->browser()->getJavascript()
+ && $options->browser()->getFailOnTimeout()
+ && $options->browser()->getBrowserTimeout() === 2000,
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 210.0
- && $options->browser->getSocket() === 'shared'
- && ! $options->browser->getJavascript(),
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 210.0
+ && $options->browser()->getSocket() === 'shared'
+ && ! $options->browser()->getJavascript(),
);
});
@@ -112,7 +112,7 @@ function pdfFixture(): string
->withArgs(
fn ($source, $type, CaptureOptions $options) => $source === 'invoice'
&& $type === SourceType::Html
- && $options->getFormat() === OutputFormat::Jpeg,
+ && $options->format() === OutputFormat::Jpeg,
)
->andReturn($image);
@@ -121,8 +121,8 @@ function pdfFixture(): string
->withArgs(
fn ($source, $type, CaptureOptions $options) => $source === 'invoice'
&& $type === SourceType::Html
- && $options->getFormat() === OutputFormat::Pdf
- && $options->output->getLandscape(),
+ && $options->format() === OutputFormat::Pdf
+ && $options->output()->getLandscape(),
)
->andReturn($pdf);
diff --git a/tests/Unit/PendingCaptureTest.php b/tests/Unit/PendingCaptureTest.php
index 27069d4..533f2d1 100644
--- a/tests/Unit/PendingCaptureTest.php
+++ b/tests/Unit/PendingCaptureTest.php
@@ -33,16 +33,16 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'https://example.com/invoice'
&& $type === SourceType::Url
- && $options->browser->getWidth() === 800
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 95,
+ && $options->browser()->getWidth() === 800
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 95,
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
&& $source === 'https://example.com/invoice'
&& $type === SourceType::Url
- && $options->browser->getWidth() === 400
- && $options->output->getLandscape(),
+ && $options->browser()->getWidth() === 400
+ && $options->output()->getLandscape(),
);
$fake->assertCapturedCount(3);
});
@@ -58,11 +58,11 @@
expect($source)->toBe('invoice');
expect($type)->toBe(SourceType::Html);
expect($snapshot)->not->toBe($options);
- expect($snapshot->browser)->not->toBe($options->browser);
- expect($snapshot->output)->not->toBe($options->output);
+ expect($snapshot->browser())->not->toBe($options->browser());
+ expect($snapshot->output())->not->toBe($options->output());
- $snapshot->browser->setSize(400, 300);
- $snapshot->output->setFormat(OutputFormat::Jpeg, 50);
+ $snapshot->browser()->setSize(400, 300);
+ $snapshot->output()->setFormat(OutputFormat::Jpeg, 50);
return true;
})
@@ -71,9 +71,9 @@
$capture = new PendingCapture($driver, 'invoice', SourceType::Html);
expect($capture->capture($options))->toBe($result);
- expect($options->browser->getWidth())->toBe(800);
- expect($options->getFormat())->toBe(OutputFormat::Png);
- expect($options->output->getQuality())->toBe(80);
+ expect($options->browser()->getWidth())->toBe(800);
+ expect($options->format())->toBe(OutputFormat::Png);
+ expect($options->output()->getQuality())->toBe(80);
});
test('the executor validates settings before invoking the driver', function () {
diff --git a/tests/Unit/PendingImageTest.php b/tests/Unit/PendingImageTest.php
index bcfcd9a..616f6f9 100644
--- a/tests/Unit/PendingImageTest.php
+++ b/tests/Unit/PendingImageTest.php
@@ -30,17 +30,17 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'Hello '
&& $type === SourceType::Html
- && $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 600
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 90
- && ! $options->output->getFullPage()
- && $options->output->getMaxWidth() === 1000
- && $options->output->getMaxHeight() === 2000
- && $options->browser->getBrowserTimeout() === 2000
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getJavascript()
- && $options->browser->getFailOnTimeout(),
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 600
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 90
+ && ! $options->output()->getFullPage()
+ && $options->output()->getMaxWidth() === 1000
+ && $options->output()->getMaxHeight() === 2000
+ && $options->browser()->getBrowserTimeout() === 2000
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getJavascript()
+ && $options->browser()->getFailOnTimeout(),
);
});
@@ -54,13 +54,13 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'override'
- && $options->browser->getLoadTimeout() === $load
- && $options->browser->getBrowserTimeout() === $browser,
+ && $options->browser()->getLoadTimeout() === $load
+ && $options->browser()->getBrowserTimeout() === $browser,
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'default'
- && $options->browser->getLoadTimeout() === 20000
- && $options->browser->getBrowserTimeout() === 80000,
+ && $options->browser()->getLoadTimeout() === 20000
+ && $options->browser()->getBrowserTimeout() === 80000,
);
})->with([
'load only' => [['load' => 30000], 30000, 80000],
@@ -77,8 +77,8 @@
->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->browser->getLoadTimeout() === 30000
- && $options->browser->getBrowserTimeout() === 90000,
+ fn ($source, $type, $options) => $options->browser()->getLoadTimeout() === 30000
+ && $options->browser()->getBrowserTimeout() === 90000,
);
});
@@ -93,8 +93,8 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'second'
- && $options->browser->getWidth() === 1024
- && ! $options->browser->getJavascript(),
+ && $options->browser()->getWidth() === 1024
+ && ! $options->browser()->getJavascript(),
);
$fake->assertCapturedCount(2);
});
@@ -105,24 +105,24 @@
$first = new PendingImage(new PendingCapture($fake, 'first', SourceType::Html), $settings);
$second = new PendingImage(new PendingCapture($fake, 'second', SourceType::Html), $settings);
- $settings->browser->setSize(1200, $settings->browser->getHeight());
- $settings->output->setFormat(OutputFormat::Jpeg, 50);
+ $settings->browser()->setSize(1200, $settings->browser()->getHeight());
+ $settings->output()->setFormat(OutputFormat::Jpeg, 50);
$first->size(500, 400)->capture();
$second->capture();
- expect($settings->browser->getWidth())->toBe(1200);
- expect($settings->browser->getHeight())->toBe(768);
+ expect($settings->browser()->getWidth())->toBe(1200);
+ expect($settings->browser()->getHeight())->toBe(768);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'first'
- && $options->browser->getWidth() === 500
- && $options->browser->getHeight() === 400,
+ && $options->browser()->getWidth() === 500
+ && $options->browser()->getHeight() === 400,
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'second'
- && $options->browser->getWidth() === 900
- && $options->browser->getHeight() === 768
- && $options->getFormat() === OutputFormat::Png
- && $options->output->getQuality() === 80,
+ && $options->browser()->getWidth() === 900
+ && $options->browser()->getHeight() === 768
+ && $options->format() === OutputFormat::Png
+ && $options->output()->getQuality() === 80,
);
});
@@ -146,17 +146,17 @@
$pending->jpeg(95)->capture();
$pending->size(640, 480);
- expect($snapshots[0]->browser->getWidth())->toBe(1024);
- expect($snapshots[0]->browser->getHeight())->toBe(768);
+ expect($snapshots[0]->browser()->getWidth())->toBe(1024);
+ expect($snapshots[0]->browser()->getHeight())->toBe(768);
- $snapshots[0]->browser->setSize(1, $snapshots[0]->browser->getHeight());
- $snapshots[0]->output->setFormat(OutputFormat::Png, 50);
+ $snapshots[0]->browser()->setSize(1, $snapshots[0]->browser()->getHeight());
+ $snapshots[0]->output()->setFormat(OutputFormat::Png, 50);
$pending->capture();
- expect($snapshots[1]->browser->getWidth())->toBe(640);
- expect($snapshots[1]->browser->getHeight())->toBe(480);
- expect($snapshots[1]->getFormat())->toBe(OutputFormat::Jpeg);
- expect($snapshots[1]->output->getQuality())->toBe(95);
+ expect($snapshots[1]->browser()->getWidth())->toBe(640);
+ expect($snapshots[1]->browser()->getHeight())->toBe(480);
+ expect($snapshots[1]->format())->toBe(OutputFormat::Jpeg);
+ expect($snapshots[1]->output()->getQuality())->toBe(95);
});
test('invalid mutations leave all pending screenshot options unchanged', function ($method, $arguments) {
@@ -228,15 +228,15 @@
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'named'
- && $options->browser->getSocket() === 'emails',
+ && $options->browser()->getSocket() === 'emails',
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'dedicated'
- && $options->browser->getSocket() === null,
+ && $options->browser()->getSocket() === null,
);
$fake->assertCaptured(
fn ($source, $type, $options) => $source === 'default'
- && $options->browser->getSocket() === 'default',
+ && $options->browser()->getSocket() === 'default',
);
});
diff --git a/tests/Unit/PendingPdfTest.php b/tests/Unit/PendingPdfTest.php
index 90bffad..a4c7fdc 100644
--- a/tests/Unit/PendingPdfTest.php
+++ b/tests/Unit/PendingPdfTest.php
@@ -17,8 +17,8 @@
$first = new PendingPdf($capture, $options);
$second = new PendingPdf($capture, $options);
- $options->browser->setSocket('changed');
- $options->output->setPaper(PaperSize::Legal);
+ $options->browser()->setSocket('changed');
+ $options->output()->setPaper(PaperSize::Legal);
expect($first->size(800, 600))->toBe($first);
expect($first->timeouts(load: 1000))->toBe($first);
@@ -36,28 +36,28 @@
$second->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 600
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 2000
- && $options->browser->getJavascript()
- && $options->browser->getFailOnTimeout()
- && $options->browser->getSocket() === 'documents'
- && $options->output->getPaperWidth() === 215.9,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 600
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 2000
+ && $options->browser()->getJavascript()
+ && $options->browser()->getFailOnTimeout()
+ && $options->browser()->getSocket() === 'documents'
+ && $options->output()->getPaperWidth() === 215.9,
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && ! $options->browser->getJavascript()
- && ! $options->browser->getFailOnTimeout()
- && $options->browser->getSocket() === null,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && ! $options->browser()->getJavascript()
+ && ! $options->browser()->getFailOnTimeout()
+ && $options->browser()->getSocket() === null,
);
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 1024
- && $options->browser->getSocket() === 'default'
- && $options->output->getPaperWidth() === 210.0,
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 1024
+ && $options->browser()->getSocket() === 'default'
+ && $options->output()->getPaperWidth() === 210.0,
);
$fake->assertCapturedCount(3);
});
@@ -76,12 +76,12 @@
$pending->capture();
$fake->assertCaptured(
- fn ($source, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 600
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 2000
- && $options->browser->getSocket() === 'documents',
+ fn ($source, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 600
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 2000
+ && $options->browser()->getSocket() === 'documents',
);
})->with([
'size' => ['size', [400, 0]],
diff --git a/tests/Unit/PendingSourceTest.php b/tests/Unit/PendingSourceTest.php
index 27ca27c..810a79e 100644
--- a/tests/Unit/PendingSourceTest.php
+++ b/tests/Unit/PendingSourceTest.php
@@ -12,6 +12,39 @@
use DirectoryTree\Screenshot\PendingSource;
use DirectoryTree\Screenshot\Testing\ScreenshotFake;
+test('element selection is snapshotted for each output and direct image capture', function () {
+ $fake = new ScreenshotFake;
+ $source = $fake->html('invoice');
+ $wholePage = $source->png();
+
+ expect($source->element('#invoice'))->toBe($source);
+
+ $png = $source->png();
+ $jpeg = $source->jpeg();
+ $pdf = $source->pdf();
+
+ $source->element('#receipt');
+
+ $wholePage->capture();
+ $png->capture();
+ $jpeg->capture();
+ $pdf->capture();
+ $source->capture();
+ $fake->html('another document')->pdf()->capture();
+
+ foreach (OutputFormat::cases() as $format) {
+ $fake->assertCaptured(fn ($contents, $type, $options) => $options->element() === '#invoice'
+ && $options->format() === $format);
+ }
+
+ $fake->assertCaptured(fn ($contents, $type, $options) => $contents === 'invoice'
+ && $options->element() === null);
+ $fake->assertCaptured(fn ($contents, $type, $options) => $options->element() === '#receipt');
+ $fake->assertCaptured(fn ($contents, $type, $options) => $contents === 'another document'
+ && $options->element() === null);
+ $fake->assertCapturedCount(6);
+});
+
test('sources only copy output options when that output is selected', function () {
$copies = (object) ['image' => 0, 'pdf' => 0];
$image = new class($copies) extends ImageOptions
@@ -97,40 +130,40 @@ public function __clone(): void
$fake->assertCaptured(
fn ($contents, $type, $options) => $contents === 'invoice'
&& $type === SourceType::Html
- && $options->getFormat() === OutputFormat::Png
- && $options->browser->getWidth() === 640
- && $options->output->getMaxWidth() === 500
- && ! $options->output->getFullPage(),
+ && $options->format() === OutputFormat::Png
+ && $options->browser()->getWidth() === 640
+ && $options->output()->getMaxWidth() === 500
+ && ! $options->output()->getFullPage(),
);
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Jpeg
- && $options->browser->getWidth() === 800
- && $options->output->getQuality() === 90
- && $options->output->getMaxWidth() === 2048
- && $options->output->getFullPage(),
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Jpeg
+ && $options->browser()->getWidth() === 800
+ && $options->output()->getQuality() === 90
+ && $options->output()->getMaxWidth() === 2048
+ && $options->output()->getFullPage(),
);
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && $options->browser->getSocket() === 'documents'
- && $options->browser->getJavascript()
- && $options->browser->getFailOnTimeout()
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 2000
- && $options->output->getPaperWidth() === 148.0,
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getSocket() === 'documents'
+ && $options->browser()->getJavascript()
+ && $options->browser()->getFailOnTimeout()
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 2000
+ && $options->output()->getPaperWidth() === 148.0,
);
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 320
- && $options->browser->getSocket() === null
- && ! $options->browser->getJavascript()
- && ! $options->browser->getFailOnTimeout()
- && $options->output->getPaperWidth() === 215.9,
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 320
+ && $options->browser()->getSocket() === null
+ && ! $options->browser()->getJavascript()
+ && ! $options->browser()->getFailOnTimeout()
+ && $options->output()->getPaperWidth() === 215.9,
);
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->browser->getWidth() === 320
- && $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 85,
+ fn ($contents, $type, $options) => $options->browser()->getWidth() === 320
+ && $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 85,
);
});
@@ -148,9 +181,9 @@ public function __clone(): void
$source->pdf()->capture();
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 600,
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 600,
);
});
@@ -180,12 +213,12 @@ public function __clone(): void
expect($image->getMaxWidth())->toBe(2048);
expect($pdf->getPaperWidth())->toBe(215.9);
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Jpeg
- && $options->output->getQuality() === 85
- && $options->output->getMaxWidth() === 2048,
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Jpeg
+ && $options->output()->getQuality() === 85
+ && $options->output()->getMaxWidth() === 2048,
);
- $fake->assertCaptured(fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->output->getPaperWidth() === 215.9);
+ $fake->assertCaptured(fn ($contents, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->output()->getPaperWidth() === 215.9);
});
test('invalid source browser changes preserve current settings', function ($method, $arguments) {
@@ -200,12 +233,12 @@ public function __clone(): void
$source->pdf()->capture();
$fake->assertCaptured(
- fn ($contents, $type, $options) => $options->getFormat() === OutputFormat::Pdf
- && $options->browser->getWidth() === 800
- && $options->browser->getHeight() === 600
- && $options->browser->getLoadTimeout() === 1000
- && $options->browser->getBrowserTimeout() === 2000
- && $options->browser->getSocket() === 'documents',
+ fn ($contents, $type, $options) => $options->format() === OutputFormat::Pdf
+ && $options->browser()->getWidth() === 800
+ && $options->browser()->getHeight() === 600
+ && $options->browser()->getLoadTimeout() === 1000
+ && $options->browser()->getBrowserTimeout() === 2000
+ && $options->browser()->getSocket() === 'documents',
);
})->with([
'size' => ['size', [400, 0]],
diff --git a/tests/Unit/ScreenshotFakeTest.php b/tests/Unit/ScreenshotFakeTest.php
index 88c928d..c091ca9 100644
--- a/tests/Unit/ScreenshotFakeTest.php
+++ b/tests/Unit/ScreenshotFakeTest.php
@@ -86,14 +86,14 @@
$fake->assertCaptured(function ($source, $type, CaptureOptions $options) use (&$sources) {
$sources[] = $source;
- return $source === 'second pdf' && $options->getFormat() === OutputFormat::Pdf;
+ return $source === 'second pdf' && $options->format() === OutputFormat::Pdf;
});
expect($sources)->toBe(['png', 'first pdf', 'jpeg', 'second pdf']);
$fake->assertCaptured(
fn ($source, $type, CaptureOptions $options) => $source === 'jpeg'
- && $options->getFormat() === OutputFormat::Jpeg,
+ && $options->format() === OutputFormat::Jpeg,
);
});
@@ -113,7 +113,7 @@
$fake->assertCaptured(
fn ($source, $type, CaptureOptions $options) => $source === 'invoice'
&& $type === SourceType::Html
- && $options->getFormat() === $format,
+ && $options->format() === $format,
);
expect(fn () => $fake->assertNothingCaptured())->toThrow(AssertionFailedError::class);
@@ -121,7 +121,7 @@
expect(fn () => $fake->assertCapturedCount(2))->toThrow(AssertionFailedError::class);
expect(fn () => $fake->assertCaptured(fn () => false))->toThrow(AssertionFailedError::class);
expect(fn () => $fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->getFormat() !== $format,
+ fn ($source, $type, CaptureOptions $options) => $options->format() !== $format,
))->toThrow(AssertionFailedError::class);
})->with(OutputFormat::cases());
@@ -153,22 +153,22 @@
})->capture();
$fake->assertCaptured(function ($source, $type, CaptureOptions $options) {
- $options->browser->setSize(320, 240);
+ $options->browser()->setSize(320, 240);
- if ($options->getFormat() === OutputFormat::Pdf) {
- $options->output->setLandscape(true);
+ if ($options->format() === OutputFormat::Pdf) {
+ $options->output()->setLandscape(true);
} else {
- $options->output->setFormat(OutputFormat::Jpeg, 20);
+ $options->output()->setFormat(OutputFormat::Jpeg, 20);
}
return true;
});
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->browser->getWidth() === 1024
- && $options->getFormat() === $format
+ fn ($source, $type, CaptureOptions $options) => $options->browser()->getWidth() === 1024
+ && $options->format() === $format
&& ($format === OutputFormat::Pdf
- ? ! $options->output->getLandscape()
- : $options->output->getQuality() === 80),
+ ? ! $options->output()->getLandscape()
+ : $options->output()->getQuality() === 80),
);
})->with(OutputFormat::cases());
@@ -178,15 +178,15 @@
$fake->html('invoice')->capture();
expect(fn () => $fake->assertCaptured(function ($source, $type, CaptureOptions $options) {
- $options->browser->setSize(320, 240);
- $options->output->setFormat(OutputFormat::Jpeg, 20);
+ $options->browser()->setSize(320, 240);
+ $options->output()->setFormat(OutputFormat::Jpeg, 20);
return false;
}))->toThrow(AssertionFailedError::class);
$fake->assertCaptured(
- fn ($source, $type, CaptureOptions $options) => $options->browser->getWidth() === 1024
- && $options->getFormat() === OutputFormat::Png
- && $options->output->getQuality() === 80,
+ fn ($source, $type, CaptureOptions $options) => $options->browser()->getWidth() === 1024
+ && $options->format() === OutputFormat::Png
+ && $options->output()->getQuality() === 80,
);
});