From f7089ca06b22353f3ee801f09c9d2b87fd0f2e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Glawaty?= Date: Wed, 22 Nov 2023 07:03:58 +0100 Subject: [PATCH] Position Renderer - added services for rendering positions (banners) - added default rendering bridge for `.phtml` template with the default templates for all types of positions - added unit tests for renderer --- composer.json | 6 +- phpstan.neon | 5 + src/Exception/RendererException.php | 53 ++++++ src/Renderer/BannersResolver.php | 83 ++++++++ src/Renderer/BannersResolverInterface.php | 20 ++ .../BreakpointStyle/BreakpointStyle.php | 136 +++++++++++++ src/Renderer/BreakpointStyle/Media.php | 18 ++ src/Renderer/BreakpointStyle/Property.php | 18 ++ src/Renderer/BreakpointStyle/Selector.php | 18 ++ src/Renderer/Fingerprint.php | 66 +++++++ src/Renderer/OutputBuffer.php | 29 +++ src/Renderer/Phtml/Helpers.php | 37 ++++ src/Renderer/Phtml/PhtmlRendererBridge.php | 115 +++++++++++ .../Phtml/Templates/contents.fragment.phtml | 42 +++++ src/Renderer/Phtml/Templates/multiple.phtml | 28 +++ src/Renderer/Phtml/Templates/not-found.phtml | 11 ++ src/Renderer/Phtml/Templates/random.phtml | 21 +++ src/Renderer/Phtml/Templates/single.phtml | 21 +++ src/Renderer/Renderer.php | 70 +++++++ src/Renderer/RendererBridgeInterface.php | 22 +++ src/Renderer/RendererInterface.php | 16 ++ tests/Renderer/AssertHtml.php | 36 ++++ tests/Renderer/BannersResolverTest.php | 83 ++++++++ .../BreakpointStyle/BreakpointStyleTest.php | 140 ++++++++++++++ tests/Renderer/OutputBufferTest.php | 49 +++++ .../Phtml/PhtmlRendererBridgeTest.php | 157 +++++++++++++++ tests/Renderer/RendererTest.php | 178 ++++++++++++++++++ .../renderer/multiple/data-provider.php | 88 +++++++++ .../multiple/multipleBannersFullFeatured.html | 33 ++++ .../multipleBannersWithoutContents.html | 1 + .../resources/renderer/multiple/noBanner.html | 1 + .../multiple/singleBannerWithoutContents.html | 1 + .../renderer/not-found/data-provider.php | 12 ++ .../renderer/not-found/notFound.html | 1 + .../bannerWithBreakpointHtmlContentOnly.html | 8 + .../bannerWithBreakpointImageContentOnly.html | 10 + .../bannerWithDefaultHtmlContentOnly.html | 7 + ...ltImageContentOnly.withOptionalValues.html | 11 ++ ...mageContentOnly.withoutOptionalValues.html | 9 + .../random/bannerWithMultipleContents.html | 22 +++ .../renderer/random/bannerWithoutContent.html | 1 + .../renderer/random/data-provider.php | 136 +++++++++++++ tests/resources/renderer/random/noBanner.html | 1 + .../bannerWithBreakpointHtmlContentOnly.html | 8 + .../bannerWithBreakpointImageContentOnly.html | 10 + .../bannerWithDefaultHtmlContentOnly.html | 7 + ...ltImageContentOnly.withOptionalValues.html | 11 ++ ...mageContentOnly.withoutOptionalValues.html | 9 + .../single/bannerWithMultipleContents.html | 22 +++ .../renderer/single/bannerWithoutContent.html | 1 + .../renderer/single/data-provider.php | 136 +++++++++++++ tests/resources/renderer/single/noBanner.html | 1 + 52 files changed, 2024 insertions(+), 1 deletion(-) create mode 100644 src/Exception/RendererException.php create mode 100644 src/Renderer/BannersResolver.php create mode 100644 src/Renderer/BannersResolverInterface.php create mode 100644 src/Renderer/BreakpointStyle/BreakpointStyle.php create mode 100644 src/Renderer/BreakpointStyle/Media.php create mode 100644 src/Renderer/BreakpointStyle/Property.php create mode 100644 src/Renderer/BreakpointStyle/Selector.php create mode 100644 src/Renderer/Fingerprint.php create mode 100644 src/Renderer/OutputBuffer.php create mode 100644 src/Renderer/Phtml/Helpers.php create mode 100644 src/Renderer/Phtml/PhtmlRendererBridge.php create mode 100644 src/Renderer/Phtml/Templates/contents.fragment.phtml create mode 100644 src/Renderer/Phtml/Templates/multiple.phtml create mode 100644 src/Renderer/Phtml/Templates/not-found.phtml create mode 100644 src/Renderer/Phtml/Templates/random.phtml create mode 100644 src/Renderer/Phtml/Templates/single.phtml create mode 100644 src/Renderer/Renderer.php create mode 100644 src/Renderer/RendererBridgeInterface.php create mode 100644 src/Renderer/RendererInterface.php create mode 100644 tests/Renderer/AssertHtml.php create mode 100644 tests/Renderer/BannersResolverTest.php create mode 100644 tests/Renderer/BreakpointStyle/BreakpointStyleTest.php create mode 100644 tests/Renderer/OutputBufferTest.php create mode 100644 tests/Renderer/Phtml/PhtmlRendererBridgeTest.php create mode 100644 tests/Renderer/RendererTest.php create mode 100644 tests/resources/renderer/multiple/data-provider.php create mode 100644 tests/resources/renderer/multiple/multipleBannersFullFeatured.html create mode 100644 tests/resources/renderer/multiple/multipleBannersWithoutContents.html create mode 100644 tests/resources/renderer/multiple/noBanner.html create mode 100644 tests/resources/renderer/multiple/singleBannerWithoutContents.html create mode 100644 tests/resources/renderer/not-found/data-provider.php create mode 100644 tests/resources/renderer/not-found/notFound.html create mode 100644 tests/resources/renderer/random/bannerWithBreakpointHtmlContentOnly.html create mode 100644 tests/resources/renderer/random/bannerWithBreakpointImageContentOnly.html create mode 100644 tests/resources/renderer/random/bannerWithDefaultHtmlContentOnly.html create mode 100644 tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withOptionalValues.html create mode 100644 tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withoutOptionalValues.html create mode 100644 tests/resources/renderer/random/bannerWithMultipleContents.html create mode 100644 tests/resources/renderer/random/bannerWithoutContent.html create mode 100644 tests/resources/renderer/random/data-provider.php create mode 100644 tests/resources/renderer/random/noBanner.html create mode 100644 tests/resources/renderer/single/bannerWithBreakpointHtmlContentOnly.html create mode 100644 tests/resources/renderer/single/bannerWithBreakpointImageContentOnly.html create mode 100644 tests/resources/renderer/single/bannerWithDefaultHtmlContentOnly.html create mode 100644 tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withOptionalValues.html create mode 100644 tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withoutOptionalValues.html create mode 100644 tests/resources/renderer/single/bannerWithMultipleContents.html create mode 100644 tests/resources/renderer/single/bannerWithoutContent.html create mode 100644 tests/resources/renderer/single/data-provider.php create mode 100644 tests/resources/renderer/single/noBanner.html diff --git a/composer.json b/composer.json index 98e3251..75899a7 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,11 @@ "guzzlehttp/guzzle": "^7.7" }, "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", "friendsofphp/php-cs-fixer": "^3.17", "kubawerlos/php-cs-fixer-custom-fixers": "^3.14", + "latte/latte": "^2.11", "mockery/mockery": "^1.6", "nette/bootstrap": "^3.1", "nette/caching": "^3.1", @@ -27,7 +30,8 @@ "psr/log": "^1.1", "roave/security-advisories": "dev-latest", "slope-it/clock-mock": "^0.4.0", - "symplify/phpstan-rules": "12.0.2.72" + "symplify/phpstan-rules": "12.0.2.72", + "wa72/html-pretty-min": "^0.2.0" }, "config": { "sort-packages": true diff --git a/phpstan.neon b/phpstan.neon index ef7e8ff..b3a6af5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -12,3 +12,8 @@ parameters: level: 8 paths: - src + ignoreErrors: + # ignore "unused" closure parameters for capturing the output buffer + - + message: '#Anonymous function has an unused use \$.+#' + path: src/Renderer/Phtml/PhtmlRendererBridge.php diff --git a/src/Exception/RendererException.php b/src/Exception/RendererException.php new file mode 100644 index 0000000..def79b8 --- /dev/null +++ b/src/Exception/RendererException.php @@ -0,0 +1,53 @@ +getMessage() : '', + ), + 0, + $previous, + ); + } + + /** + * @param class-string $rendererBridgeClassname + */ + public static function rendererBridgeThrownError(string $rendererBridgeClassname, string $positionCode, Throwable $previous): self + { + return new self( + sprintf( + 'Renderer bridge of type %s thrown an exception while rendering a position %s: %s', + $rendererBridgeClassname, + $positionCode, + $previous->getMessage(), + ), + 0, + $previous, + ); + } + + public static function templateFileNotFound(string $filename): self + { + return new self( + sprintf( + 'Template file "%s" not found.', + $filename, + ), + ); + } +} diff --git a/src/Renderer/BannersResolver.php b/src/Renderer/BannersResolver.php new file mode 100644 index 0000000..ea6037f --- /dev/null +++ b/src/Renderer/BannersResolver.php @@ -0,0 +1,83 @@ +getBanners(); + + if (0 >= count($banners)) { + return null; + } + + $scores = array_map( + static fn (Banner $banner) => $banner->getScore(), + $banners, + ); + $firstHighestScoreKey = array_search(max($scores), $scores, true); + + return $banners[$firstHighestScoreKey] ?? null; + } + + public function resolveRandom(Position $position): ?Banner + { + $banners = $position->getBanners(); + + if (0 >= count($banners)) { + return null; + } + + $distributions = []; + $weightTotal = 0; + + foreach ($banners as $banner) { + $weightTotal += $banner->getScore(); + } + + foreach ($banners as $index => $banner) { + $distributions[$index] = $banner->getScore() / $weightTotal; + } + + $key = 0; + $selector = mt_rand() / mt_getrandmax(); + + while (0 < $selector) { + $selector -= $distributions[$key]; + $key++; + } + + $key--; + + return $banners[$key] ?? $banners[0]; + } + + public function resolveMultiple(Position $position): array + { + $banners = $position->getBanners(); + + if (0 >= count($banners)) { + return []; + } + + usort( + $banners, + static fn (Banner $left, Banner $right): int => $right->getScore() <=> $left->getScore(), + ); + + return $banners; + } +} diff --git a/src/Renderer/BannersResolverInterface.php b/src/Renderer/BannersResolverInterface.php new file mode 100644 index 0000000..35e7071 --- /dev/null +++ b/src/Renderer/BannersResolverInterface.php @@ -0,0 +1,20 @@ + + */ + public function resolveMultiple(Position $position): array; +} diff --git a/src/Renderer/BreakpointStyle/BreakpointStyle.php b/src/Renderer/BreakpointStyle/BreakpointStyle.php new file mode 100644 index 0000000..d1bd0b1 --- /dev/null +++ b/src/Renderer/BreakpointStyle/BreakpointStyle.php @@ -0,0 +1,136 @@ + */ + private array $selectors = []; + + /** @var array */ + private array $media = []; + + public function __construct(Position $position, Banner $banner) + { + $selectorMask = sprintf( + '[data-amp-banner="%s"] [data-amp-content-breakpoint="%s"]', + Helpers::escapeHtmlAttr($position->getCode()), + '%s', + ); + + $defaultContent = null; + $alternativeContents = []; + + foreach ($banner->getContents() as $content) { + if (null === $content->getBreakpoint()) { + $defaultContent = $content; + } else { + $alternativeContents[$content->getBreakpoint()] = $content; + } + } + + if (Position::BreakpointTypeMax === $position->getBreakpointType()) { + $mediaRuleMask = 'max-width: %dpx'; + krsort($alternativeContents); + } else { + $mediaRuleMask = 'min-width: %dpx'; + ksort($alternativeContents); + } + + foreach ($alternativeContents as $alternativeContent) { + $this->selectors[] = $selector = new Selector(sprintf( + $selectorMask, + $alternativeContent->getBreakpoint(), + )); + + $selector->properties[] = new Property('display', 'none'); + + $this->media[] = $media = new Media(sprintf( + $mediaRuleMask, + $alternativeContent->getBreakpoint(), + )); + + if (null !== $defaultContent) { + $media->selectors[] = $selectorInMedia = new Selector(sprintf( + $selectorMask, + 'default', + )); + + $selectorInMedia->properties[] = new Property('display', 'none'); + } + + foreach ($alternativeContents as $alternativeContentInner) { + $media->selectors[] = $selectorInMedia = new Selector(sprintf( + $selectorMask, + $alternativeContentInner->getBreakpoint(), + )); + + $selectorInMedia->properties[] = new Property('display', $alternativeContentInner === $alternativeContent ? 'block' : 'none'); + } + } + } + + public function __toString(): string + { + return $this->getCss(); + } + + public function getCss(): string + { + if (0 >= count($this->selectors) && 0 >= count($this->media)) { + return ''; + } + + $styles = []; + + foreach ($this->selectors as $selector) { + $styles[] = $this->stringifySelector($selector); + } + + foreach ($this->media as $media) { + $styles[] = $this->stringifyMedia($media); + } + + return ''; + } + + private function stringifySelector(Selector $selector): string + { + $properties = array_map( + static fn (Property $property): string => $property->name . ':' . $property->value, + $selector->properties, + ); + + return sprintf( + '%s{%s}', + $selector->selector, + implode(';', $properties), + ); + } + + private function stringifyMedia(Media $media): string + { + $selectors = array_map( + fn (Selector $selector): string => $this->stringifySelector($selector), + $media->selectors, + ); + + return sprintf( + '@media(%s){%s}', + $media->rule, + implode('', $selectors), + ); + } +} diff --git a/src/Renderer/BreakpointStyle/Media.php b/src/Renderer/BreakpointStyle/Media.php new file mode 100644 index 0000000..dc2b346 --- /dev/null +++ b/src/Renderer/BreakpointStyle/Media.php @@ -0,0 +1,18 @@ + */ + public array $selectors = []; + + public function __construct(string $rule) + { + $this->rule = $rule; + } +} diff --git a/src/Renderer/BreakpointStyle/Property.php b/src/Renderer/BreakpointStyle/Property.php new file mode 100644 index 0000000..3c1f282 --- /dev/null +++ b/src/Renderer/BreakpointStyle/Property.php @@ -0,0 +1,18 @@ +name = $name; + $this->value = $value; + } +} diff --git a/src/Renderer/BreakpointStyle/Selector.php b/src/Renderer/BreakpointStyle/Selector.php new file mode 100644 index 0000000..e19312f --- /dev/null +++ b/src/Renderer/BreakpointStyle/Selector.php @@ -0,0 +1,18 @@ + */ + public array $properties = []; + + public function __construct(string $selector) + { + $this->selector = $selector; + } +} diff --git a/src/Renderer/Fingerprint.php b/src/Renderer/Fingerprint.php new file mode 100644 index 0000000..7359cf0 --- /dev/null +++ b/src/Renderer/Fingerprint.php @@ -0,0 +1,66 @@ +value = $value; + } + + /** + * @throws RendererException + */ + public static function create(Position $position, Banner $banner): self + { + $components = [ + 'bannerId' => $banner->getId(), + 'bannerName' => $banner->getName(), + 'positionId' => $position->getId(), + 'positionCode' => $position->getCode(), + 'positionName' => $position->getName(), + 'campaignId' => $banner->getCampaignId(), + 'campaignCode' => $banner->getCampaignCode(), + 'campaignName' => $banner->getCampaignName(), + ]; + + try { + $json = json_encode($components, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw RendererException::unableToCreateFingerprint($position->getCode(), $banner->getId(), $e); + } + + return new self( + rtrim( + base64_encode( + rawurlencode($json), + ), + '=', + ), + ); + } + + public function getValue(): string + { + return $this->value; + } + + public function __toString(): string + { + return $this->getValue(); + } +} diff --git a/src/Renderer/OutputBuffer.php b/src/Renderer/OutputBuffer.php new file mode 100644 index 0000000..3ca37f9 --- /dev/null +++ b/src/Renderer/OutputBuffer.php @@ -0,0 +1,29 @@ +"\'')) { + $string .= ' '; + } + + $string = self::escapeHtml($string, $double); + + return str_replace('{', '{', $string); + } +} diff --git a/src/Renderer/Phtml/PhtmlRendererBridge.php b/src/Renderer/Phtml/PhtmlRendererBridge.php new file mode 100644 index 0000000..7ee0b0b --- /dev/null +++ b/src/Renderer/Phtml/PhtmlRendererBridge.php @@ -0,0 +1,115 @@ +templates = array_merge( + [ + self::TemplateSingle => __DIR__ . '/Templates/single.phtml', + self::TemplateRandom => __DIR__ . '/Templates/random.phtml', + self::TemplateMultiple => __DIR__ . '/Templates/multiple.phtml', + self::TemplateNotFound => __DIR__ . '/Templates/not-found.phtml', + ], + $templatesOverrides, + ); + } + + /** + * @throws Throwable + */ + public function renderNotFound(Position $position): string + { + $filename = $this->getTemplateFilename(self::TemplateNotFound); + + return OutputBuffer::capture(function () use ($filename, $position) { + require $filename; + }); + } + + /** + * @throws Throwable + */ + public function renderSingle(Position $position, ?Banner $banner): string + { + $filename = $this->getTemplateFilename(self::TemplateSingle); + + return OutputBuffer::capture(function () use ($filename, $position, $banner) { + require $filename; + }); + } + + /** + * @throws Throwable + */ + public function renderRandom(Position $position, ?Banner $banner): string + { + $filename = $this->getTemplateFilename(self::TemplateRandom); + + return OutputBuffer::capture(function () use ($filename, $position, $banner) { + require $filename; + }); + } + + /** + * @throws Throwable + */ + public function renderMultiple(Position $position, array $banners): string + { + $filename = $this->getTemplateFilename(self::TemplateMultiple); + + return OutputBuffer::capture(function () use ($filename, $position, $banners) { + require $filename; + }); + } + + /** + * @throws RendererException + */ + private function getTemplateFilename(string $type): string + { + $filename = $this->templates[$type] ?? ''; + + if (!file_exists($filename)) { + throw RendererException::templateFileNotFound($filename); + } + + return $filename; + } +} diff --git a/src/Renderer/Phtml/Templates/contents.fragment.phtml b/src/Renderer/Phtml/Templates/contents.fragment.phtml new file mode 100644 index 0000000..58ce465 --- /dev/null +++ b/src/Renderer/Phtml/Templates/contents.fragment.phtml @@ -0,0 +1,42 @@ + +getContents() as $content): ?> + + getTarget()) : ?>target="getTarget()) ?>"> + + getSources() as $source): ?> + + + <?= Helpers::escapeHtmlAttr($content->getAlt()) ?>getTitle()) : ?>title="getTitle()) ?>"> + + + +
+ getHtml() ?> +
+ + + diff --git a/src/Renderer/Phtml/Templates/multiple.phtml b/src/Renderer/Phtml/Templates/multiple.phtml new file mode 100644 index 0000000..76c3601 --- /dev/null +++ b/src/Renderer/Phtml/Templates/multiple.phtml @@ -0,0 +1,28 @@ + $banners */ +?> +
+ 0 < count($banner->getContents())) ?> + +
+
+ +
+ +
+ +
+
+ +
diff --git a/src/Renderer/Phtml/Templates/not-found.phtml b/src/Renderer/Phtml/Templates/not-found.phtml new file mode 100644 index 0000000..0807e3a --- /dev/null +++ b/src/Renderer/Phtml/Templates/not-found.phtml @@ -0,0 +1,11 @@ + +
diff --git a/src/Renderer/Phtml/Templates/random.phtml b/src/Renderer/Phtml/Templates/random.phtml new file mode 100644 index 0000000..21c6143 --- /dev/null +++ b/src/Renderer/Phtml/Templates/random.phtml @@ -0,0 +1,21 @@ + +
+ getContents())): ?> +
+ +
+ +
diff --git a/src/Renderer/Phtml/Templates/single.phtml b/src/Renderer/Phtml/Templates/single.phtml new file mode 100644 index 0000000..544f3b4 --- /dev/null +++ b/src/Renderer/Phtml/Templates/single.phtml @@ -0,0 +1,21 @@ + +
+ getContents())): ?> +
+ +
+ +
diff --git a/src/Renderer/Renderer.php b/src/Renderer/Renderer.php new file mode 100644 index 0000000..ca19137 --- /dev/null +++ b/src/Renderer/Renderer.php @@ -0,0 +1,70 @@ +bannersResolver = $bannersResolver; + $this->rendererBridge = $rendererBridge; + } + + public static function create(?RendererBridgeInterface $rendererBridge = null): self + { + return new self( + new BannersResolver(), + $rendererBridge ?? new PhtmlRendererBridge(), + ); + } + + public function render(Position $position): string + { + try { + switch ($position->getDisplayType()) { + case null: + return $this->rendererBridge->renderNotFound($position); + case Position::DisplayTypeMultiple: + return $this->rendererBridge->renderMultiple( + $position, + $this->bannersResolver->resolveMultiple($position), + ); + case Position::DisplayTypeRandom: + return $this->rendererBridge->renderRandom( + $position, + $this->bannersResolver->resolveRandom($position), + ); + case Position::DisplayTypeSingle: + default: + return $this->rendererBridge->renderSingle( + $position, + $this->bannersResolver->resolveSingle($position), + ); + } + } catch (Throwable $e) { + if ($e instanceof RendererException) { + throw $e; + } + + throw RendererException::rendererBridgeThrownError( + get_class($this->rendererBridge), + $position->getCode(), + $e, + ); + } + } +} diff --git a/src/Renderer/RendererBridgeInterface.php b/src/Renderer/RendererBridgeInterface.php new file mode 100644 index 0000000..d9446b3 --- /dev/null +++ b/src/Renderer/RendererBridgeInterface.php @@ -0,0 +1,22 @@ + $banners + */ + public function renderMultiple(Position $position, array $banners): string; +} diff --git a/src/Renderer/RendererInterface.php b/src/Renderer/RendererInterface.php new file mode 100644 index 0000000..507b27e --- /dev/null +++ b/src/Renderer/RendererInterface.php @@ -0,0 +1,16 @@ +loadHtml(file_get_contents($expectationFile), LIBXML_NOERROR); + + $expectedHtml = (new PrettyMin(['minify_js' => false, 'minify_css' => false, 'remove_comments' => false])) + ->load($expectedDom) + ->minify() + ->saveHtml(); + + $actualDom = new DomDocument(); + $actualDom->loadHtml($actualHtml, LIBXML_NOERROR); + + $actualHtml = (new PrettyMin(['minify_js' => false, 'minify_css' => false, 'remove_comments' => false])) + ->load($actualDom) + ->minify() + ->saveHtml(); + + Assert::same($expectedHtml, $actualHtml); + } +} diff --git a/tests/Renderer/BannersResolverTest.php b/tests/Renderer/BannersResolverTest.php new file mode 100644 index 0000000..f38882c --- /dev/null +++ b/tests/Renderer/BannersResolverTest.php @@ -0,0 +1,83 @@ +resolveSingle($position)); + } + + public function testFirstBannerWithHighestScoreShouldBeReturnedWhenResolvingSinglePosition(): void + { + $banner1 = new Banner('1', '1', 0, null, null, null, []); + $banner2 = new Banner('2', '2', 2, null, null, null, []); + $banner3 = new Banner('3', '3', 1, null, null, null, []); + $banner4 = new Banner('4', '4', 2, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeSingle, Position::BreakpointTypeMin, [$banner1, $banner2, $banner3, $banner4]); + $resolver = new BannersResolver(); + + Assert::same($banner2, $resolver->resolveSingle($position)); + } + + public function testEmptyArrayShouldBeReturnedWhenResolvingMultiplePositionWithoutBanners(): void + { + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeMultiple, Position::BreakpointTypeMin, []); + $resolver = new BannersResolver(); + + Assert::same([], $resolver->resolveMultiple($position)); + } + + public function testSortedBannersShouldBeReturnedWhenResolvingMultiplePosition(): void + { + $banner1 = new Banner('1', '1', 0, null, null, null, []); + $banner2 = new Banner('2', '2', 2, null, null, null, []); + $banner3 = new Banner('3', '3', 1, null, null, null, []); + $banner4 = new Banner('4', '4', 2, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeMultiple, Position::BreakpointTypeMin, [$banner1, $banner2, $banner3, $banner4]); + $resolver = new BannersResolver(); + + Assert::same([ + $banner2, + $banner4, + $banner3, + $banner1, + ], $resolver->resolveMultiple($position)); + } + + public function testNullShouldBeReturnedWhenResolvingRandomPositionWithoutBanners(): void + { + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeRandom, Position::BreakpointTypeMin, []); + $resolver = new BannersResolver(); + + Assert::null($resolver->resolveRandom($position)); + } + + public function testRandomBannerShouldBeReturnedWhenResolvingRandomPosition(): void + { + $banner1 = new Banner('1', '1', 1, null, null, null, []); + $banner2 = new Banner('2', '2', 3, null, null, null, []); + $banner3 = new Banner('3', '3', 2, null, null, null, []); + $banner4 = new Banner('4', '4', 3, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeRandom, Position::BreakpointTypeMin, [$banner1, $banner2, $banner3, $banner4]); + $resolver = new BannersResolver(); + + Assert::type(Banner::class, $resolver->resolveRandom($position)); # @todo: Mock mt_rand() ? + } +} + +(new BannersResolverTest())->run(); diff --git a/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php b/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php new file mode 100644 index 0000000..65d6126 --- /dev/null +++ b/tests/Renderer/BreakpointStyle/BreakpointStyleTest.php @@ -0,0 +1,140 @@ +createBreakpointStyle(Position::BreakpointTypeMin, []); + + Assert::same('', $style->getCss()); + Assert::same('', (string) $style); + } + + public function testStylesShouldBeEmptyInMaxModeWhenNoContentsDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMax, []); + + Assert::same('', $style->getCss()); + Assert::same('', (string) $style); + } + + public function testStylesShouldBeEmptyInMinModeWhenOnlyDefaultContentDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMin, [ + new HtmlContent(null, ''), + ]); + + Assert::same('', $style->getCss()); + Assert::same('', (string) $style); + } + + public function testStylesShouldBeEmptyInMaxModeWhenOnlyDefaultContentDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMax, [ + new HtmlContent(null, ''), + ]); + + Assert::same('', $style->getCss()); + Assert::same('', (string) $style); + } + + public function testStylesOutputInMinModeWhenOnlyContentWithNumericBreakpointDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMin, [ + new HtmlContent(500, ''), + ]); + + $expected = <<[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}@media(min-width: 500px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:block}} +HTML; + + Assert::same($expected, $style->getCss()); + Assert::same($expected, (string) $style); + } + + public function testStylesOutputInMaxModeWhenOnlyContentWithNumericBreakpointDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMax, [ + new HtmlContent(500, ''), + ]); + + $expected = <<[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}@media(max-width: 500px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:block}} +HTML; + + Assert::same($expected, $style->getCss()); + Assert::same($expected, (string) $style); + } + + public function testStylesOutputInMinModeWhenMultipleContentsDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMin, [ + new HtmlContent(null, ''), + new HtmlContent(500, ''), + new HtmlContent(900, ''), + ]); + + $expected = <<[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:none}@media(min-width: 500px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="default"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:block}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:none}}@media(min-width: 900px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="default"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:block}} +HTML; + + Assert::same($expected, $style->getCss()); + Assert::same($expected, (string) $style); + } + + public function testStylesOutputInMaxModeWhenMultipleContentsDefined(): void + { + $style = $this->createBreakpointStyle(Position::BreakpointTypeMax, [ + new HtmlContent(null, ''), + new HtmlContent(500, ''), + new HtmlContent(900, ''), + ]); + + $expected = <<[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}@media(max-width: 900px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="default"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:block}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:none}}@media(max-width: 500px){[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="default"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="900"]{display:none}[data-amp-banner="homepage.top"] [data-amp-content-breakpoint="500"]{display:block}} +HTML; + + Assert::same($expected, $style->getCss()); + Assert::same($expected, (string) $style); + } + + private function createBreakpointStyle(string $breakpointType, array $contents): BreakpointStyle + { + $banner = new Banner( + '12344', + 'Main', + 2, + null, + null, + null, + $contents, + ); + + $position = new Position( + '1234', + 'homepage.top', + 'Homepage top', + 5, + Position::DisplayTypeSingle, + $breakpointType, + [$banner], + ); + + return new BreakpointStyle($position, $banner); + } +} + +(new BreakpointStyleTest())->run(); diff --git a/tests/Renderer/OutputBufferTest.php b/tests/Renderer/OutputBufferTest.php new file mode 100644 index 0000000..3fad025 --- /dev/null +++ b/tests/Renderer/OutputBufferTest.php @@ -0,0 +1,49 @@ +run(); diff --git a/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php b/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php new file mode 100644 index 0000000..b7cc385 --- /dev/null +++ b/tests/Renderer/Phtml/PhtmlRendererBridgeTest.php @@ -0,0 +1,157 @@ + '/path/to/template.phtml', + ]); + + $templates = call_user_func(Closure::bind(static fn () => $renderer->templates, null, PhtmlRendererBridge::class)); + + Assert::same('/path/to/template.phtml', $templates[PhtmlRendererBridge::TemplateSingle]); + } + + public function testRendererExceptionShouldBeThrownWhenTemplateFileNotExists(): void + { + $renderer = new PhtmlRendererBridge([ + PhtmlRendererBridge::TemplateSingle => 'non-existent.single.phtml', + PhtmlRendererBridge::TemplateMultiple => 'non-existent.multiple.phtml', + PhtmlRendererBridge::TemplateRandom => 'non-existent.random.phtml', + PhtmlRendererBridge::TemplateNotFound => 'non-existent.not-found.phtml', + ]); + + Assert::exception( + static function () use ($renderer): void { + $banner = new Banner('1234', 'Main', 0, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeSingle, Position::BreakpointTypeMin, [$banner]); + + $renderer->renderSingle($position, $banner); + }, + RendererException::class, + 'Template file "non-existent.single.phtml" not found.', + ); + + Assert::exception( + static function () use ($renderer): void { + $banner = new Banner('1234', 'Main', 0, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeMultiple, Position::BreakpointTypeMin, [$banner]); + + $renderer->renderMultiple($position, [$banner]); + }, + RendererException::class, + 'Template file "non-existent.multiple.phtml" not found.', + ); + + Assert::exception( + static function () use ($renderer): void { + $banner = new Banner('1234', 'Main', 0, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeRandom, Position::BreakpointTypeMin, [$banner]); + + $renderer->renderRandom($position, $banner); + }, + RendererException::class, + 'Template file "non-existent.random.phtml" not found.', + ); + + Assert::exception( + static function () use ($renderer): void { + $position = new Position(null, 'homepage.top', null, 0, null, Position::BreakpointTypeMin, []); + + $renderer->renderNotFound($position); + }, + RendererException::class, + 'Template file "non-existent.not-found.phtml" not found.', + ); + } + + /** + * @dataProvider notFoundTemplateDataProvider + */ + public function testNotFoundTemplateRendering( + Position $position, + string $expectationFile + ): void { + $renderer = new PhtmlRendererBridge(); + + AssertHtml::assert($expectationFile, $renderer->renderNotFound($position)); + } + + /** + * @dataProvider singleTemplateDataProvider + */ + public function testSingleTemplateRendering( + Position $position, + ?Banner $banner, + string $expectationFile + ): void { + $renderer = new PhtmlRendererBridge(); + + AssertHtml::assert($expectationFile, $renderer->renderSingle($position, $banner)); + } + + /** + * @dataProvider randomTemplateDataProvider + */ + public function testRandomTemplateRendering( + Position $position, + ?Banner $banner, + string $expectationFile + ): void { + $renderer = new PhtmlRendererBridge(); + + AssertHtml::assert($expectationFile, $renderer->renderRandom($position, $banner)); + } + + /** + * @dataProvider multipleTemplateDataProvider + */ + public function testMultipleTemplateRendering( + Position $position, + array $banners, + string $expectationFile + ): void { + $renderer = new PhtmlRendererBridge(); + + AssertHtml::assert($expectationFile, $renderer->renderMultiple($position, $banners)); + } + + public function notFoundTemplateDataProvider(): array + { + return require __DIR__ . '/../../resources/renderer/not-found/data-provider.php'; + } + + public function singleTemplateDataProvider(): array + { + return require __DIR__ . '/../../resources/renderer/single/data-provider.php'; + } + + public function randomTemplateDataProvider(): array + { + return require __DIR__ . '/../../resources/renderer/random/data-provider.php'; + } + + public function multipleTemplateDataProvider(): array + { + return require __DIR__ . '/../../resources/renderer/multiple/data-provider.php'; + } +} + +(new PhtmlRendererBridgeTest())->run(); diff --git a/tests/Renderer/RendererTest.php b/tests/Renderer/RendererTest.php new file mode 100644 index 0000000..dc7c2f8 --- /dev/null +++ b/tests/Renderer/RendererTest.php @@ -0,0 +1,178 @@ +bannersResolver, + $renderer->rendererBridge, + ]; + }, null, Renderer::class)); + + Assert::equal(new BannersResolver(), $bannersResolver); + Assert::equal(new PhtmlRendererBridge(), $rendererBridge); + } + + public function testNotFoundTemplateShouldBeRendered(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $position = new Position(null, 'homepage.top', null, 0, null, Position::BreakpointTypeMin, []); + + $rendererBridge + ->shouldReceive('renderNotFound') + ->once() + ->andReturn('not found'); + + Assert::same('not found', $renderer->render($position)); + } + + public function testSingleTemplateShouldBeRendered(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $banner = new Banner('1234', 'Main', 0, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeSingle, Position::BreakpointTypeMin, [$banner]); + + $bannersResolver + ->shouldReceive('resolveSingle') + ->once() + ->with($position) + ->andReturn($banner); + + $rendererBridge + ->shouldReceive('renderSingle') + ->once() + ->with($position, $banner) + ->andReturn('single'); + + Assert::same('single', $renderer->render($position)); + } + + public function testRandomTemplateShouldBeRendered(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $banner = new Banner('1234', 'Main', 0, null, null, null, []); + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeRandom, Position::BreakpointTypeMin, [$banner]); + + $bannersResolver + ->shouldReceive('resolveRandom') + ->once() + ->with($position) + ->andReturn($banner); + + $rendererBridge + ->shouldReceive('renderRandom') + ->once() + ->with($position, $banner) + ->andReturn('random'); + + Assert::same('random', $renderer->render($position)); + } + + public function testMultipleTemplateShouldBeRendered(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $banners = [ + new Banner('1234', 'Main', 0, null, null, null, []), + new Banner('1235', 'Secondary', 0, null, null, null, []), + ]; + $position = new Position('1234', 'homepage.top', 'Homepage top', 0, Position::DisplayTypeMultiple, Position::BreakpointTypeMin, $banners); + + $bannersResolver + ->shouldReceive('resolveMultiple') + ->once() + ->with($position) + ->andReturn($banners); + + $rendererBridge + ->shouldReceive('renderMultiple') + ->once() + ->with($position, $banners) + ->andReturn('multiple'); + + Assert::same('multiple', $renderer->render($position)); + } + + public function testRendererExceptionShouldBeThrownWhenBridgeThrowsTheException(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $position = new Position(null, 'homepage.top', null, 0, null, Position::BreakpointTypeMin, []); + + $rendererBridge + ->shouldReceive('renderNotFound') + ->once() + ->andThrow(new RendererException('Test exception')); + + Assert::exception( + static fn () => $renderer->render($position), + RendererException::class, + 'Test exception', + ); + } + + public function testRendererExceptionShouldBeThrownWhenBridgeThrowsAnyException(): void + { + $bannersResolver = Mockery::mock(BannersResolverInterface::class); + $rendererBridge = Mockery::mock(RendererBridgeInterface::class); + $renderer = new Renderer($bannersResolver, $rendererBridge); + + $position = new Position(null, 'homepage.top', null, 0, null, Position::BreakpointTypeMin, []); + + $rendererBridge + ->shouldReceive('renderNotFound') + ->once() + ->andThrow(new Exception('Test exception')); + + Assert::exception( + static fn () => $renderer->render($position), + RendererException::class, + 'Renderer bridge of type %A% thrown an exception while rendering a position homepage.top: Test exception', + ); + } + + protected function tearDown(): void + { + Mockery::close(); + } +} + +(new RendererTest())->run(); diff --git a/tests/resources/renderer/multiple/data-provider.php b/tests/resources/renderer/multiple/data-provider.php new file mode 100644 index 0000000..7aecba7 --- /dev/null +++ b/tests/resources/renderer/multiple/data-provider.php @@ -0,0 +1,88 @@ + [ + $position, + [], + __DIR__ . '/noBanner.html', + ], + 'Single banner without contents' => [ + $position, + [ + new Banner('1234', 'Main', 0, null, null, null, []), + ], + __DIR__ . '/singleBannerWithoutContents.html', + ], + 'Multiple banners without contents' => [ + $position, + [ + new Banner('1234', 'Main 1', 0, null, null, null, []), + new Banner('1235', 'Main 2', 0, null, null, null, []), + ], + __DIR__ . '/multipleBannersWithoutContents.html', + ], + 'Multiple banners - full featured' => [ + $position, + [ + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + null, + '

Small content

', + ), + new ImageContent( + 600, + 'https://www.example.com/main1', + '_blank', + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/800/main1.png 800w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) 1000px, 100vw', + [ + new Source('image/avif', 'https://img.example.com/800/main1.avif 800w, https://img.example.com/1000/main1.avif 1000w'), + new Source('image/webp', 'https://img.example.com/800/main1.webp 800w, https://img.example.com/1000/main1.webp 1000w'), + ], + ), + new ImageContent( + 400, + 'https://www.example.com/main2', + '_blank', + 'Main 2', + 'Main 2', + 'https://img.example.com/600/main2.png', + 'https://img.example.com/600/main2.png 600w', + '100vw', + [ + new Source('image/avif', 'https://img.example.com/600/main2.avif 600w'), + new Source('image/webp', 'https://img.example.com/600/main2.webp 600w'), + ], + ), + ]), + new Banner('1235', 'Secondary', 0, null, null, null, [ + new ImageContent( + null, + 'https://www.example.com/secondary1', + null, + 'Secondary 1', + 'Secondary 1', + 'https://img.example.com/1000/secondary1.png', + 'https://img.example.com/800/secondary1.png 800w, https://img.example.com/1000/secondary1.png 1000w', + '(min-width: 1000px) 1000px, 100vw', + [], + ), + ]), + new Banner('1236', 'No contents', 0, null, null, null, []), + ], + __DIR__ . '/multipleBannersFullFeatured.html', + ], +]; diff --git a/tests/resources/renderer/multiple/multipleBannersFullFeatured.html b/tests/resources/renderer/multiple/multipleBannersFullFeatured.html new file mode 100644 index 0000000..3a99d00 --- /dev/null +++ b/tests/resources/renderer/multiple/multipleBannersFullFeatured.html @@ -0,0 +1,33 @@ +
+
+
+
+
+

Small content

+
+ + + + + Main 1 + + + + + + + Main 2 + + + +
+ +
+
+
diff --git a/tests/resources/renderer/multiple/multipleBannersWithoutContents.html b/tests/resources/renderer/multiple/multipleBannersWithoutContents.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/multiple/multipleBannersWithoutContents.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/multiple/noBanner.html b/tests/resources/renderer/multiple/noBanner.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/multiple/noBanner.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/multiple/singleBannerWithoutContents.html b/tests/resources/renderer/multiple/singleBannerWithoutContents.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/multiple/singleBannerWithoutContents.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/not-found/data-provider.php b/tests/resources/renderer/not-found/data-provider.php new file mode 100644 index 0000000..49b8438 --- /dev/null +++ b/tests/resources/renderer/not-found/data-provider.php @@ -0,0 +1,12 @@ + [ + new Position(null, 'homepage.top', null, 0, null, Position::BreakpointTypeMin, []), + __DIR__ . '/notFound.html', + ], +]; diff --git a/tests/resources/renderer/not-found/notFound.html b/tests/resources/renderer/not-found/notFound.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/not-found/notFound.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/random/bannerWithBreakpointHtmlContentOnly.html b/tests/resources/renderer/random/bannerWithBreakpointHtmlContentOnly.html new file mode 100644 index 0000000..dce30e1 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithBreakpointHtmlContentOnly.html @@ -0,0 +1,8 @@ +
+
+
+

My Awesome content!

+
+ +
+
diff --git a/tests/resources/renderer/random/bannerWithBreakpointImageContentOnly.html b/tests/resources/renderer/random/bannerWithBreakpointImageContentOnly.html new file mode 100644 index 0000000..a997274 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithBreakpointImageContentOnly.html @@ -0,0 +1,10 @@ +
+
+ + + Main 1 + + + +
+
diff --git a/tests/resources/renderer/random/bannerWithDefaultHtmlContentOnly.html b/tests/resources/renderer/random/bannerWithDefaultHtmlContentOnly.html new file mode 100644 index 0000000..f0f5183 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithDefaultHtmlContentOnly.html @@ -0,0 +1,7 @@ +
+
+
+

My Awesome content!

+
+
+
diff --git a/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withOptionalValues.html b/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withOptionalValues.html new file mode 100644 index 0000000..20d17cf --- /dev/null +++ b/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withOptionalValues.html @@ -0,0 +1,11 @@ +
+ +
diff --git a/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withoutOptionalValues.html b/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withoutOptionalValues.html new file mode 100644 index 0000000..1dafb25 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithDefaultImageContentOnly.withoutOptionalValues.html @@ -0,0 +1,9 @@ +
+ +
diff --git a/tests/resources/renderer/random/bannerWithMultipleContents.html b/tests/resources/renderer/random/bannerWithMultipleContents.html new file mode 100644 index 0000000..58f2c84 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithMultipleContents.html @@ -0,0 +1,22 @@ +
+
+
+

Small content

+
+ + + + + Main 1 + + + + + + + Main 2 + + + +
+
diff --git a/tests/resources/renderer/random/bannerWithoutContent.html b/tests/resources/renderer/random/bannerWithoutContent.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/random/bannerWithoutContent.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/random/data-provider.php b/tests/resources/renderer/random/data-provider.php new file mode 100644 index 0000000..d590abe --- /dev/null +++ b/tests/resources/renderer/random/data-provider.php @@ -0,0 +1,136 @@ + [ + $position, + null, + __DIR__ . '/noBanner.html', + ], + 'Banner without contents' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, []), + __DIR__ . '/bannerWithoutContent.html', + ], + 'Banner with default content only: image without optional values' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + null, + 'https://www.example.com/main1', + null, + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [], + ), + ]), + __DIR__ . '/bannerWithDefaultImageContentOnly.withoutOptionalValues.html', + ], + 'Banner with default content only: image with optional value' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + null, + 'https://www.example.com/main1', + '_blank', + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [ + new Source('image/avif', 'https://img.example.com/500/main1.avif 500w, https://img.example.com/1000/main1.avif 1000w'), + new Source('image/webp', 'https://img.example.com/500/main1.webp 500w, https://img.example.com/1000/main1.webp 1000w'), + ], + ), + ]), + __DIR__ . '/bannerWithDefaultImageContentOnly.withOptionalValues.html', + ], + 'Banner with breakpoint content only: image' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + 500, + 'https://www.example.com/main1', + null, + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [], + ), + ]), + __DIR__ . '/bannerWithBreakpointImageContentOnly.html', + ], + 'Banner with default content only: html' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + null, + '

My Awesome content!

', + ), + ]), + __DIR__ . '/bannerWithDefaultHtmlContentOnly.html', + ], + 'Banner with breakpoint content only: html' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + 500, + '

My Awesome content!

', + ), + ]), + __DIR__ . '/bannerWithBreakpointHtmlContentOnly.html', + ], + 'Banner with multiple contents' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + null, + '

Small content

', + ), + new ImageContent( + 600, + 'https://www.example.com/main1', + '_blank', + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/800/main1.png 800w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) 1000px, 100vw', + [ + new Source('image/avif', 'https://img.example.com/800/main1.avif 800w, https://img.example.com/1000/main1.avif 1000w'), + new Source('image/webp', 'https://img.example.com/800/main1.webp 800w, https://img.example.com/1000/main1.webp 1000w'), + ], + ), + new ImageContent( + 400, + 'https://www.example.com/main2', + '_blank', + 'Main 2', + 'Main 2', + 'https://img.example.com/600/main2.png', + 'https://img.example.com/600/main2.png 600w', + '100vw', + [ + new Source('image/avif', 'https://img.example.com/600/main2.avif 600w'), + new Source('image/webp', 'https://img.example.com/600/main2.webp 600w'), + ], + ), + ]), + __DIR__ . '/bannerWithMultipleContents.html', + ], +]; diff --git a/tests/resources/renderer/random/noBanner.html b/tests/resources/renderer/random/noBanner.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/random/noBanner.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/single/bannerWithBreakpointHtmlContentOnly.html b/tests/resources/renderer/single/bannerWithBreakpointHtmlContentOnly.html new file mode 100644 index 0000000..3fcab10 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithBreakpointHtmlContentOnly.html @@ -0,0 +1,8 @@ +
+
+
+

My Awesome content!

+
+ +
+
diff --git a/tests/resources/renderer/single/bannerWithBreakpointImageContentOnly.html b/tests/resources/renderer/single/bannerWithBreakpointImageContentOnly.html new file mode 100644 index 0000000..d79ce76 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithBreakpointImageContentOnly.html @@ -0,0 +1,10 @@ +
+
+ + + Main 1 + + + +
+
diff --git a/tests/resources/renderer/single/bannerWithDefaultHtmlContentOnly.html b/tests/resources/renderer/single/bannerWithDefaultHtmlContentOnly.html new file mode 100644 index 0000000..5b0f7a8 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithDefaultHtmlContentOnly.html @@ -0,0 +1,7 @@ +
+
+
+

My Awesome content!

+
+
+
diff --git a/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withOptionalValues.html b/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withOptionalValues.html new file mode 100644 index 0000000..c213650 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withOptionalValues.html @@ -0,0 +1,11 @@ +
+ +
diff --git a/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withoutOptionalValues.html b/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withoutOptionalValues.html new file mode 100644 index 0000000..fc6ae64 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithDefaultImageContentOnly.withoutOptionalValues.html @@ -0,0 +1,9 @@ +
+ +
diff --git a/tests/resources/renderer/single/bannerWithMultipleContents.html b/tests/resources/renderer/single/bannerWithMultipleContents.html new file mode 100644 index 0000000..d63f743 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithMultipleContents.html @@ -0,0 +1,22 @@ +
+
+
+

Small content

+
+ + + + + Main 1 + + + + + + + Main 2 + + + +
+
diff --git a/tests/resources/renderer/single/bannerWithoutContent.html b/tests/resources/renderer/single/bannerWithoutContent.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/single/bannerWithoutContent.html @@ -0,0 +1 @@ +
diff --git a/tests/resources/renderer/single/data-provider.php b/tests/resources/renderer/single/data-provider.php new file mode 100644 index 0000000..832ffe2 --- /dev/null +++ b/tests/resources/renderer/single/data-provider.php @@ -0,0 +1,136 @@ + [ + $position, + null, + __DIR__ . '/noBanner.html', + ], + 'Banner without contents' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, []), + __DIR__ . '/bannerWithoutContent.html', + ], + 'Banner with default content only: image without optional values' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + null, + 'https://www.example.com/main1', + null, + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [], + ), + ]), + __DIR__ . '/bannerWithDefaultImageContentOnly.withoutOptionalValues.html', + ], + 'Banner with default content only: image with optional value' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + null, + 'https://www.example.com/main1', + '_blank', + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [ + new Source('image/avif', 'https://img.example.com/500/main1.avif 500w, https://img.example.com/1000/main1.avif 1000w'), + new Source('image/webp', 'https://img.example.com/500/main1.webp 500w, https://img.example.com/1000/main1.webp 1000w'), + ], + ), + ]), + __DIR__ . '/bannerWithDefaultImageContentOnly.withOptionalValues.html', + ], + 'Banner with breakpoint content only: image' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new ImageContent( + 500, + 'https://www.example.com/main1', + null, + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/500/main1.png 500w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) calc(1000px - 2 * 16px), (min-width: 600px) calc(100vw - 2 * 16px), 100vw', + [], + ), + ]), + __DIR__ . '/bannerWithBreakpointImageContentOnly.html', + ], + 'Banner with default content only: html' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + null, + '

My Awesome content!

', + ), + ]), + __DIR__ . '/bannerWithDefaultHtmlContentOnly.html', + ], + 'Banner with breakpoint content only: html' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + 500, + '

My Awesome content!

', + ), + ]), + __DIR__ . '/bannerWithBreakpointHtmlContentOnly.html', + ], + 'Banner with multiple contents' => [ + $position, + new Banner('1234', 'Main', 0, null, null, null, [ + new HtmlContent( + null, + '

Small content

', + ), + new ImageContent( + 600, + 'https://www.example.com/main1', + '_blank', + 'Main 1', + 'Main 1', + 'https://img.example.com/1000/main1.png', + 'https://img.example.com/800/main1.png 800w, https://img.example.com/1000/main1.png 1000w', + '(min-width: 1000px) 1000px, 100vw', + [ + new Source('image/avif', 'https://img.example.com/800/main1.avif 800w, https://img.example.com/1000/main1.avif 1000w'), + new Source('image/webp', 'https://img.example.com/800/main1.webp 800w, https://img.example.com/1000/main1.webp 1000w'), + ], + ), + new ImageContent( + 400, + 'https://www.example.com/main2', + '_blank', + 'Main 2', + 'Main 2', + 'https://img.example.com/600/main2.png', + 'https://img.example.com/600/main2.png 600w', + '100vw', + [ + new Source('image/avif', 'https://img.example.com/600/main2.avif 600w'), + new Source('image/webp', 'https://img.example.com/600/main2.webp 600w'), + ], + ), + ]), + __DIR__ . '/bannerWithMultipleContents.html', + ], +]; diff --git a/tests/resources/renderer/single/noBanner.html b/tests/resources/renderer/single/noBanner.html new file mode 100644 index 0000000..4420688 --- /dev/null +++ b/tests/resources/renderer/single/noBanner.html @@ -0,0 +1 @@ +