From 883ce6051392d0f86dea3c972c6ba025fd13d84d Mon Sep 17 00:00:00 2001 From: reliq Date: Sat, 30 Mar 2024 17:53:12 +0100 Subject: [PATCH] wip: upgrade ImageDispenser to use Invervention 3 --- composer.json | 2 +- src/Concern/Guide.php | 44 ++------ src/Contract/ImageDemand.php | 7 +- src/Contract/ImageDispenser.php | 5 +- src/Demand/Dummy.php | 37 +------ src/Demand/ExistingImage.php | 30 ++---- src/Demand/Image.php | 41 +------- src/Demand/Resize.php | 43 +++----- src/Demand/Thumbnail.php | 35 ++++--- src/Service/ImageDispenser.php | 120 ++++++++++------------ src/Service/ImageUploader.php | 21 ++-- tests/Unit/Service/ImageDispenserTest.php | 22 +++- tests/Unit/Service/ImageUploaderTest.php | 66 ++++-------- tests/Unit/TestCase.php | 2 +- 14 files changed, 163 insertions(+), 312 deletions(-) diff --git a/composer.json b/composer.json index d966711..91cc3b2 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "orchestra/testbench": "^9.0", "phpro/grumphp": "^2.5", "phpspec/prophecy-phpunit": "^2.0", - "symplify/easy-coding-standard": ">=8.2", + "phpunit/phpunit": "^11.0", "yieldstudio/grumphp-laravel-pint": "^1.0" }, "autoload": { diff --git a/src/Concern/Guide.php b/src/Concern/Guide.php index 3aee764..61f5b0b 100644 --- a/src/Concern/Guide.php +++ b/src/Concern/Guide.php @@ -10,7 +10,6 @@ use Illuminate\Http\Request; use ReliqArts\GuidedImage\Contract\GuidedImage; use ReliqArts\GuidedImage\Contract\ImageDispenser; -use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; use ReliqArts\GuidedImage\Result; @@ -36,59 +35,30 @@ public function emptyCache(ImageDispenser $imageDispenser): JsonResponse ); } - /** - * @param mixed $width - * @param mixed $height - * @param mixed $aspect - * @param mixed $upSize - */ public function resized( ImageDispenser $imageDispenser, Request $request, GuidedImage $guidedImage, - $width, - $height, - $aspect = true, - $upSize = false + mixed $width, + mixed $height, + mixed $aspect = true, + mixed $upSize = false ): Response { $demand = new Resize($request, $guidedImage, $width, $height, $aspect, $upSize); return $imageDispenser->getResizedImage($demand); } - /** - * @param mixed $method - * @param mixed $width - * @param mixed $height - */ public function thumb( ImageDispenser $imageDispenser, Request $request, GuidedImage $guidedImage, - $method, - $width, - $height + mixed $method, + mixed $width, + mixed $height ): Response { $demand = new Thumbnail($request, $guidedImage, $method, $width, $height); return $imageDispenser->getImageThumbnail($demand); } - - /** - * @param mixed $width - * @param mixed $height - * @param mixed $color - * @param mixed $fill - */ - public function dummy( - ImageDispenser $imageDispenser, - $width, - $height, - $color = null, - $fill = null - ): Response { - $demand = new Dummy($width, $height, $color, $fill); - - return $imageDispenser->getDummyImage($demand); - } } diff --git a/src/Contract/ImageDemand.php b/src/Contract/ImageDemand.php index 2850090..8cdd675 100644 --- a/src/Contract/ImageDemand.php +++ b/src/Contract/ImageDemand.php @@ -12,10 +12,5 @@ public function getWidth(): ?int; public function getHeight(): ?int; - public function returnObject(): bool; - - /** - * @param mixed $value - */ - public function isValueConsideredNull($value): bool; + public function isValueConsideredNull(mixed $value): bool; } diff --git a/src/Contract/ImageDispenser.php b/src/Contract/ImageDispenser.php index 70eaf20..5657b1c 100644 --- a/src/Contract/ImageDispenser.php +++ b/src/Contract/ImageDispenser.php @@ -8,6 +8,7 @@ use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; +use RuntimeException; use Symfony\Component\HttpFoundation\Response; interface ImageDispenser @@ -27,9 +28,9 @@ public function getResizedImage(Resize $demand); /** * Get dummy Guided Image. * - * @return Image|Response + * @throws RuntimeException */ - public function getDummyImage(Dummy $demand); + public function getDummyImage(Dummy $demand): Image; public function emptyCache(): bool; } diff --git a/src/Demand/Dummy.php b/src/Demand/Dummy.php index 6189fb1..ec4588a 100644 --- a/src/Demand/Dummy.php +++ b/src/Demand/Dummy.php @@ -8,48 +8,19 @@ final class Dummy extends Image { public const DEFAULT_COLOR = 'eefefe'; - /** - * @var string - */ - private $color; - - /** - * @var mixed - */ - private $filling; - /** * Dummy constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $color - * @param mixed $filling - * @param null $returnObject */ public function __construct( - $width, - $height, - $color = null, - $filling = null, - $returnObject = null + mixed $width, + mixed $height, + private readonly mixed $color = null ) { - parent::__construct($width, $height, $returnObject); - - $this->color = $color; - $this->filling = $filling; + parent::__construct($width, $height); } public function getColor(): string { return $this->isValueConsideredNull($this->color) ? self::DEFAULT_COLOR : $this->color; } - - /** - * @return mixed - */ - public function fill() - { - return $this->isValueConsideredNull($this->filling) ? null : $this->filling; - } } diff --git a/src/Demand/ExistingImage.php b/src/Demand/ExistingImage.php index 1fa92b7..b2877a8 100644 --- a/src/Demand/ExistingImage.php +++ b/src/Demand/ExistingImage.php @@ -9,29 +9,13 @@ abstract class ExistingImage extends Image { - /** - * @var Request - */ - private Request $request; - - /** - * @var GuidedImage - */ - private GuidedImage $guidedImage; - - /** - * ExistingImage constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ - public function __construct(Request $request, GuidedImage $guidedImage, $width, $height, $returnObject = null) - { - parent::__construct($width, $height, $returnObject); - - $this->request = $request; - $this->guidedImage = $guidedImage; + public function __construct( + private readonly Request $request, + private readonly GuidedImage $guidedImage, + mixed $width, + mixed $height + ) { + parent::__construct($width, $height); } final public function getRequest(): Request diff --git a/src/Demand/Image.php b/src/Demand/Image.php index 43e61ba..d71ab36 100644 --- a/src/Demand/Image.php +++ b/src/Demand/Image.php @@ -8,54 +8,21 @@ abstract class Image implements ImageDemand { - /** - * @var mixed - */ - private $width; - - /** - * @var mixed - */ - private $height; - - /** - * @var mixed - */ - private $returnObject; - - /** - * Image constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ - public function __construct($width, $height, $returnObject = null) + public function __construct(private readonly mixed $width, private readonly mixed $height) { - $this->width = $width; - $this->height = $height; - $this->returnObject = $returnObject; } final public function getWidth(): ?int { - return $this->isValueConsideredNull($this->width) ? null : (int)$this->width; + return $this->isValueConsideredNull($this->width) ? null : (int) $this->width; } final public function getHeight(): ?int { - return $this->isValueConsideredNull($this->height) ? null : (int)$this->height; - } - - final public function returnObject(): bool - { - return !$this->isValueConsideredNull($this->returnObject); + return $this->isValueConsideredNull($this->height) ? null : (int) $this->height; } - /** - * @param mixed $value - */ - final public function isValueConsideredNull($value): bool + final public function isValueConsideredNull(mixed $value): bool { return in_array($value, static::NULLS, true); } diff --git a/src/Demand/Resize.php b/src/Demand/Resize.php index 5cb7018..2549e1a 100644 --- a/src/Demand/Resize.php +++ b/src/Demand/Resize.php @@ -11,47 +11,30 @@ final class Resize extends ExistingImage { public const ROUTE_TYPE_NAME = 'resize'; - /** - * @var mixed - */ - private $maintainAspectRatio; - - /** - * @var mixed - */ - private $allowUpSizing; - - /** - * Resized constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $aspect - * @param mixed $upSize - * @param mixed $returnObject - */ public function __construct( Request $request, GuidedImage $guidedImage, - $width, - $height, - $aspect = true, - $upSize = null, - $returnObject = null + mixed $width, + mixed $height, + private readonly mixed $maintainAspectRatio = true, + private readonly mixed $allowUpSizing = null, + private readonly bool $returnObject = false ) { - parent::__construct($request, $guidedImage, $width, $height, $returnObject); - - $this->maintainAspectRatio = $aspect; - $this->allowUpSizing = $upSize; + parent::__construct($request, $guidedImage, $width, $height); } public function maintainAspectRatio(): bool { - return !$this->isValueConsideredNull($this->maintainAspectRatio); + return ! $this->isValueConsideredNull($this->maintainAspectRatio); } public function allowUpSizing(): bool { - return !$this->isValueConsideredNull($this->allowUpSizing); + return ! $this->isValueConsideredNull($this->allowUpSizing); + } + + public function returnObject(): bool + { + return $this->returnObject; } } diff --git a/src/Demand/Thumbnail.php b/src/Demand/Thumbnail.php index 187c384..74a15e3 100644 --- a/src/Demand/Thumbnail.php +++ b/src/Demand/Thumbnail.php @@ -12,32 +12,22 @@ final class Thumbnail extends ExistingImage public const ROUTE_TYPE_NAME = 'thumb'; private const METHOD_CROP = 'crop'; + + private const METHOD_COVER = 'cover'; + private const METHOD_FIT = 'fit'; - private const METHODS = [self::METHOD_CROP, self::METHOD_FIT]; - /** - * @var string - */ - private string $method; + private const METHODS = [self::METHOD_CROP, self::METHOD_FIT, self::METHOD_COVER]; - /** - * Thumbnail constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ public function __construct( Request $request, GuidedImage $guidedImage, - string $method, + private readonly string $method, $width, $height, - $returnObject = null + private readonly bool $returnObject = false ) { - parent::__construct($request, $guidedImage, $width, $height, $returnObject); - - $this->method = $method; + parent::__construct($request, $guidedImage, $width, $height); } /** @@ -45,6 +35,12 @@ public function __construct( */ public function getMethod(): string { + // since intervention/image v3; fit() was replaced by cover() and other methods + // see https://image.intervention.io/v3/introduction/upgrade + if ($this->method === self::METHOD_FIT) { + return self::METHOD_COVER; + } + return $this->method; } @@ -52,4 +48,9 @@ public function isValid(): bool { return in_array($this->method, self::METHODS, true); } + + public function returnObject(): bool + { + return $this->returnObject; + } } diff --git a/src/Service/ImageDispenser.php b/src/Service/ImageDispenser.php index 62f44df..a12283d 100644 --- a/src/Service/ImageDispenser.php +++ b/src/Service/ImageDispenser.php @@ -9,8 +9,7 @@ use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Http\Request; use Illuminate\Http\Response; -use Intervention\Image\Constraint; -use Intervention\Image\Exception\NotReadableException; +use Intervention\Image\Exceptions\RuntimeException as InterventionRuntimeException; use Intervention\Image\Image; use Intervention\Image\ImageManager; use InvalidArgumentException; @@ -29,42 +28,42 @@ final class ImageDispenser implements ImageDispenserContract { private const KEY_IMAGE_URL = 'image url'; + private const KEY_CACHE_FILE = 'cache file'; - private const RESPONSE_HTTP_OK = Response::HTTP_OK; - private const RESPONSE_HTTP_NOT_FOUND = Response::HTTP_NOT_FOUND; + + private const RESPONSE_HTTP_OK = SymfonyResponse::HTTP_OK; + + private const RESPONSE_HTTP_NOT_FOUND = SymfonyResponse::HTTP_NOT_FOUND; + private const ONE_DAY_IN_SECONDS = 60 * 60 * 24; + private const DEFAULT_IMAGE_ENCODING_FORMAT = 'png'; + private const DEFAULT_IMAGE_ENCODING_QUALITY = 90; - private ConfigProvider $configProvider; private Filesystem $cacheDisk; + private Filesystem $uploadDisk; + private string $imageEncodingFormat; + private int $imageEncodingQuality; - private ImageManager $imageManager; - private Logger $logger; + private string $thumbsCachePath; + private string $resizedCachePath; - private FileHelper $fileHelper; - /** - * ImageDispenser constructor. - */ public function __construct( - ConfigProvider $configProvider, + private readonly ConfigProvider $configProvider, FilesystemManager $filesystemManager, - ImageManager $imageManager, - Logger $logger, - FileHelper $fileHelper + private readonly ImageManager $imageManager, + private readonly Logger $logger, + private readonly FileHelper $fileHelper ) { - $this->configProvider = $configProvider; $this->cacheDisk = $filesystemManager->disk($configProvider->getCacheDiskName()); $this->uploadDisk = $filesystemManager->disk($configProvider->getUploadDiskName()); - $this->imageManager = $imageManager; $this->imageEncodingFormat = $configProvider->getImageEncodingFormat(); $this->imageEncodingQuality = $configProvider->getImageEncodingQuality(); - $this->logger = $logger; - $this->fileHelper = $fileHelper; $this->prepCacheDirectories(); } @@ -72,30 +71,23 @@ public function __construct( /** * {@inheritdoc} * - * @return Image|SymfonyResponse + * @throws RuntimeException */ - public function getDummyImage(Dummy $demand) + public function getDummyImage(Dummy $demand): Image { - $image = $this->imageManager->canvas( + return $this->imageManager->create( $demand->getWidth(), - $demand->getHeight(), - $demand->getColor() - ); - $image = $image->fill($demand->fill()); - - // Return object or actual image - return $demand->returnObject() - ? $image - : $image->response(); + $demand->getHeight() + )->fill($demand->getColor()); } /** * {@inheritdoc} * * @return Image|SymfonyResponse|void + * * @throws InvalidArgumentException * @throws RuntimeException - * @noinspection PhpRedundantCatchClauseInspection */ public function getResizedImage(Resize $demand) { @@ -117,18 +109,12 @@ public function getResizedImage(Resize $demand) $image = $this->makeImageWithEncoding($this->cacheDisk->path($cacheFilePath)); } else { $image = $this->makeImageWithEncoding($this->getImageFullUrl($guidedImage)); - $image->resize( - $width, - $height, - static function (Constraint $constraint) use ($demand) { - if ($demand->maintainAspectRatio()) { - $constraint->aspectRatio(); - } - if ($demand->allowUpSizing()) { - $constraint->upsize(); - } - } - ); + $sizingMethod = $demand->allowUpSizing() ? 'resize' : 'resizeDown'; + if ($demand->maintainAspectRatio()) { + $sizingMethod = $demand->allowUpSizing() ? 'scale' : 'scaleDown'; + } + + $image->{$sizingMethod}($width, $height); $image->save($this->cacheDisk->path($cacheFilePath)); } @@ -141,8 +127,8 @@ static function (Constraint $constraint) use ($demand) { self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (NotReadableException $exception) { - return $this->handleNotReadableException($exception, $guidedImage); + } catch (InterventionRuntimeException $exception) { + return $this->handleInterventionRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -163,6 +149,7 @@ static function (Constraint $constraint) use ($demand) { * {@inheritdoc} * * @return Image|SymfonyResponse|void + * * @throws InvalidArgumentException * @throws RuntimeException * @@ -170,7 +157,7 @@ static function (Constraint $constraint) use ($demand) { */ public function getImageThumbnail(Thumbnail $demand) { - if (!$demand->isValid()) { + if (! $demand->isValid()) { $this->logger->warning( sprintf('Invalid demand for thumbnail image. Method: %s', $demand->getMethod()), [ @@ -200,7 +187,7 @@ public function getImageThumbnail(Thumbnail $demand) } else { /** @var Image $image */ $image = $this->imageManager - ->make($this->getImageFullUrl($guidedImage)) + ->read($this->getImageFullUrl($guidedImage)) ->{$method}( $width, $height @@ -218,8 +205,8 @@ public function getImageThumbnail(Thumbnail $demand) self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (NotReadableException $exception) { - return $this->handleNotReadableException($exception, $guidedImage); + } catch (InterventionRuntimeException $exception) { + return $this->handleInterventionRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -250,7 +237,7 @@ public function emptyCache(): bool */ private function getImageHeaders(string $cacheFilePath, Request $request, Image $image): array { - $filePath = sprintf('%s/%s', $image->dirname, $image->basename); + $filePath = $image->origin()->filePath(); $lastModified = $this->cacheDisk->lastModified($cacheFilePath); $modifiedSince = $request->header('If-Modified-Since', ''); $etagHeader = trim($request->header('If-None-Match', '')); @@ -268,8 +255,8 @@ private function getImageHeaders(string $cacheFilePath, Request $request, Image return array_merge( $this->getDefaultHeaders(), [ - 'Content-Type' => $image->mime, - 'Content-Disposition' => sprintf('inline; filename=%s', $image->filename), + 'Content-Type' => $image->origin()->mediaType(), + 'Content-Disposition' => sprintf('inline; filename=%s', basename($image->origin()->filePath())), 'Last-Modified' => date(DATE_RFC822, $lastModified), 'Etag' => $etagFile, ] @@ -281,11 +268,11 @@ private function prepCacheDirectories(): void $this->resizedCachePath = $this->configProvider->getResizedCachePath(); $this->thumbsCachePath = $this->configProvider->getThumbsCachePath(); - if (!$this->cacheDisk->exists($this->thumbsCachePath)) { + if (! $this->cacheDisk->exists($this->thumbsCachePath)) { $this->cacheDisk->makeDirectory($this->thumbsCachePath); } - if (!$this->cacheDisk->exists($this->resizedCachePath)) { + if (! $this->cacheDisk->exists($this->resizedCachePath)) { $this->cacheDisk->makeDirectory($this->resizedCachePath); } } @@ -304,10 +291,11 @@ private function getDefaultHeaders(): array } /** - * @param mixed $data - * @param mixed ...$encoding + * @param mixed ...$encoding + * + * @throws InterventionRuntimeException */ - private function makeImageWithEncoding($data, ...$encoding): Image + private function makeImageWithEncoding(mixed $data, ...$encoding): Image { if (empty($encoding)) { $encoding = [ @@ -316,9 +304,11 @@ private function makeImageWithEncoding($data, ...$encoding): Image ]; } - return $this->imageManager - ->make($data) + $encodedImage = $this->imageManager + ->read($data) ->encode(...$encoding); + + return $this->imageManager->read($encodedImage->toFilePointer()); } /** @@ -330,20 +320,16 @@ private function getImageFullUrl(GuidedImage $guidedImage): string } /** - * @param NotReadableException $exception - * @param GuidedImage $guidedImage - * - * @return BinaryFileResponse * @throws RuntimeException */ - private function handleNotReadableException( - NotReadableException $exception, + private function handleInterventionRuntimeException( + InterventionRuntimeException $exception, GuidedImage $guidedImage ): BinaryFileResponse { $errorMessage = 'Intervention image creation failed with NotReadableException;'; $context = ['imageUrl' => $this->getImageFullUrl($guidedImage)]; - if (!$this->configProvider->isRawImageFallbackEnabled()) { + if (! $this->configProvider->isRawImageFallbackEnabled()) { $this->logger->error( sprintf('%s %s. Raw image fallback is disabled.', $errorMessage, $exception->getMessage()), $context diff --git a/src/Service/ImageUploader.php b/src/Service/ImageUploader.php index 347d729..eb2c07c 100644 --- a/src/Service/ImageUploader.php +++ b/src/Service/ImageUploader.php @@ -23,17 +23,27 @@ final class ImageUploader implements ImageUploaderContract { private const ERROR_INVALID_IMAGE = 'Invalid image size or type.'; + private const KEY_FILE = 'file'; + private const MESSAGE_IMAGE_REUSED = 'Image reused.'; + private const UPLOAD_DATE_SUB_DIRECTORIES_PATTERN = 'Y/m/d/H/i'; + private const UPLOAD_VISIBILITY = Filesystem::VISIBILITY_PUBLIC; + private const TEMP_FILE_PREFIX = 'LGI_'; private ConfigProvider $configProvider; + private Filesystem $uploadDisk; + private FileHelper $fileHelper; + private ValidationFactory $validationFactory; + private GuidedImage $guidedImage; + private Logger $logger; /** @@ -62,7 +72,7 @@ public function __construct( */ public function upload(UploadedFile $imageFile, bool $isUrlUpload = false): Result { - if (!$this->validate($imageFile, $isUrlUpload)) { + if (! $this->validate($imageFile, $isUrlUpload)) { return new Result(false, self::ERROR_INVALID_IMAGE); } @@ -73,10 +83,9 @@ public function upload(UploadedFile $imageFile, bool $isUrlUpload = false): Resu ->where(UploadedImage::KEY_SIZE, $uploadedImage->getSize()) ->first(); - if (!empty($existing)) { + if (! empty($existing)) { $result = new Result(true); - /** @noinspection PhpIncompatibleReturnTypeInspection */ return $result ->addMessage(self::MESSAGE_IMAGE_REUSED) ->setExtra($existing); @@ -137,7 +146,7 @@ public function uploadFromUrl(string $url): Result $this->fileHelper->unlink($tempFile); return $result; - } catch (FileNotFoundException | FileException $exception) { + } catch (FileNotFoundException|FileException $exception) { throw UrlUploadFailed::forUrl($url, $exception); } } @@ -158,7 +167,7 @@ private function validatePostUpload(UploadedFile $imageFile): bool [self::KEY_FILE => $this->configProvider->getImageRules()] ); - return $this->validateFileExtension($imageFile) && !$validator->fails(); + return $this->validateFileExtension($imageFile) && ! $validator->fails(); } private function validateFileExtension(UploadedFile $imageFile): bool @@ -174,7 +183,7 @@ private function getUploadDestination(): string { $destination = $this->configProvider->getUploadDirectory(); - if (!$this->configProvider->generateUploadDateSubDirectories()) { + if (! $this->configProvider->generateUploadDateSubDirectories()) { return $destination; } diff --git a/tests/Unit/Service/ImageDispenserTest.php b/tests/Unit/Service/ImageDispenserTest.php index 2ae7dd1..ce0da01 100644 --- a/tests/Unit/Service/ImageDispenserTest.php +++ b/tests/Unit/Service/ImageDispenserTest.php @@ -1,5 +1,4 @@ configProvider = $this->prophesize(ConfigProvider::class); - $this->filesystemManager = $this->prophesize(FilesystemManager::class); + $filesystemManager = $this->prophesize(FilesystemManager::class); $this->fileHelper = $this->prophesize(FileHelper::class); $this->validationFactory = $this->prophesize(ValidationFactory::class); $this->validator = $this->prophesize(Validator::class); @@ -138,7 +111,7 @@ protected function setUp(): void ->shouldBeCalledTimes(1) ->willReturn(1); - $this->filesystemManager + $filesystemManager ->disk(self::UPLOAD_DISK_NAME) ->shouldBeCalledTimes(1) ->willReturn($this->uploadDisk); @@ -182,7 +155,7 @@ protected function setUp(): void $this->subject = new ImageUploader( $this->configProvider->reveal(), - $this->filesystemManager->reveal(), + $filesystemManager->reveal(), $this->fileHelper->reveal(), $this->validationFactory->reveal(), $this->guidedImage->reveal(), @@ -458,9 +431,6 @@ function ($argument) { self::assertTrue($result->isSuccess()); } - /** - * @return MockInterface|UploadedFile - */ private function getUploadedFileMock( string $filename = 'myimage', string $mimeType = 'image/jpeg', diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 8d3fa44..bf2fc20 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -17,6 +17,6 @@ protected function setUp(): void { parent::setUp(); - $this->setGroups(array_merge($this->getGroups(), [self::GROUP])); + $this->setGroups(array_merge($this->groups(), [self::GROUP])); } }